@joshuafolkken/kit 1.1178.0 → 1.1240.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 (442) 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 +7 -3
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
  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 +10 -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 +2 -1
  20. package/.claude/skills/workflow-commands/latest-gate.md +1 -1
  21. package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
  22. package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
  23. package/.claude/skills/workflow-commands/prerequisite.md +2 -2
  24. package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
  25. package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
  26. package/.codex/hooks.json +2 -2
  27. package/.cursorrules +6 -41
  28. package/.github/actions/setup-node/action.yml +1 -1
  29. package/.github/workflows/auto-tag.yml +3 -4
  30. package/.github/workflows/ci.yml +32 -31
  31. package/.github/workflows/dependabot-auto-merge.yml +13 -6
  32. package/.github/workflows/publish.yml +50 -4
  33. package/.github/workflows/sonar-qube.yml +3 -0
  34. package/.pnpmfile.mjs +4 -3
  35. package/.prettierignore +8 -2
  36. package/CLAUDE.md +18 -20
  37. package/README.md +44 -38
  38. package/SECURITY.md +15 -1
  39. package/dist/CLAUDE.md +18 -20
  40. package/dist/commands/document-section-cli.js +734 -0
  41. package/dist/hooks/audit-provision.js +346 -0
  42. package/dist/hooks/batch-guard.js +5 -4
  43. package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
  44. package/dist/hooks/chunk-5VNDSNG3.js +12 -0
  45. package/dist/hooks/chunk-CDY4NFQG.js +15 -0
  46. package/dist/hooks/chunk-ELCN5LCH.js +135 -0
  47. package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
  48. package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
  49. package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
  50. package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
  51. package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
  52. package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
  53. package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
  54. package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
  55. package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
  56. package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
  57. package/dist/hooks/codex-hook-adapter.js +14 -12
  58. package/dist/hooks/duplicate-read-guard.js +6 -5
  59. package/dist/hooks/format-edited.js +4 -3
  60. package/dist/hooks/inputs.json +203 -179
  61. package/dist/hooks/investigation-guard.js +6 -5
  62. package/dist/hooks/pretool-guard.js +12 -10
  63. package/dist/hooks/session-lang.js +2 -4
  64. package/dist/hooks/stop-guard.js +66 -72
  65. package/dist/josh.js +1088 -78
  66. package/dist/version/index.d.ts +7 -2
  67. package/dist/version/index.js +152 -75
  68. package/eslint/base.js +23 -1
  69. package/eslint/rules/code-quality.js +3 -0
  70. package/eslint/rules/early-return-one-liner.js +98 -0
  71. package/eslint/rules/unicorn.js +16 -1
  72. package/package.json +7 -6
  73. package/pnpm-workspace.yaml +9 -4
  74. package/prettier/basic.d.ts +3 -1
  75. package/prettier/index.d.ts +3 -1
  76. package/prettier/static.d.ts +3 -1
  77. package/prompts/coding-standards.md +1 -1
  78. package/prompts/collaboration-workflow/file-edits.md +4 -4
  79. package/prompts/collaboration-workflow/glossary.md +1 -1
  80. package/prompts/collaboration-workflow/issue-template.md +1 -0
  81. package/prompts/collaboration-workflow/operating-rules.md +17 -9
  82. package/prompts/collaboration-workflow/overview.md +2 -2
  83. package/prompts/collaboration-workflow/principles.md +14 -12
  84. package/prompts/collaboration-workflow/report-format.md +1 -1
  85. package/prompts/collaboration-workflow/residency.md +1 -1
  86. package/prompts/collaboration-workflow/rule-delivery.md +6 -6
  87. package/prompts/collaboration-workflow/shell-body.md +6 -6
  88. package/prompts/collaboration-workflow/turn-batching.md +1 -1
  89. package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
  90. package/prompts/collaboration-workflow/wip-cap.md +10 -10
  91. package/prompts/review-rubric.md +1 -2
  92. package/prompts/review.md +1 -1
  93. package/scripts/agent/agent-headless.ts +29 -0
  94. package/scripts/agent/agent-launch-environment.ts +6 -2
  95. package/scripts/agent/codex-agent-argv.ts +2 -1
  96. package/scripts/auto-ok/auto-ok-cli.ts +1 -1
  97. package/scripts/backlog/backlog-arrival.ts +3 -3
  98. package/scripts/backlog/backlog-budget.ts +1 -1
  99. package/scripts/backlog/backlog-drive-args.ts +135 -0
  100. package/scripts/backlog/backlog-drive-cli.ts +11 -137
  101. package/scripts/backlog/backlog-drive-launch.ts +39 -0
  102. package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
  103. package/scripts/backlog/backlog-drive-named.ts +3 -3
  104. package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
  105. package/scripts/backlog/backlog-drive-owner.ts +1 -1
  106. package/scripts/backlog/backlog-drive-restore.ts +1 -1
  107. package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
  108. package/scripts/backlog/backlog-drive.ts +1 -1
  109. package/scripts/backlog/backlog-next.ts +16 -5
  110. package/scripts/backlog/backlog-offer-cli.ts +6 -6
  111. package/scripts/backlog/backlog-offer.ts +1 -1
  112. package/scripts/backlog/backlog-plan-cli.ts +1 -1
  113. package/scripts/backlog/backlog-pool.ts +57 -11
  114. package/scripts/backlog/backlog-ready.ts +1 -1
  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/build/build-codex-hooks.ts +3 -1
  119. package/scripts/build/build-commands.ts +66 -0
  120. package/scripts/build/build-hooks.ts +2 -1
  121. package/scripts/build/build-library.ts +12 -3
  122. package/scripts/build/build.ts +70 -0
  123. package/scripts/build/local-bin.ts +3 -13
  124. package/scripts/delegation/investigation-reads.ts +3 -1
  125. package/scripts/discovery/repo-origin.ts +5 -5
  126. package/scripts/doctor/doctor-consumer.ts +2 -2
  127. package/scripts/doctor/doctor-io.ts +2 -3
  128. package/scripts/document/capped-print-part.ts +30 -0
  129. package/scripts/document/capped-print.ts +3 -4
  130. package/scripts/document/document-byte-budget.ts +11 -2
  131. package/scripts/document/document-scan.ts +117 -0
  132. package/scripts/document/document-section.ts +143 -16
  133. package/scripts/document/entry-read-budget.ts +5 -3
  134. package/scripts/document/entry-read-set.ts +10 -89
  135. package/scripts/document/read-set-cost.ts +98 -0
  136. package/scripts/epic/epic-add-body.ts +1 -0
  137. package/scripts/epic/epic-audit-checks.ts +1 -1
  138. package/scripts/epic/epic-audit-rationale.ts +4 -3
  139. package/scripts/epic/epic-bundle-cli.ts +14 -3
  140. package/scripts/epic/epic-bundle-poll.ts +41 -0
  141. package/scripts/epic/epic-busy.ts +1 -1
  142. package/scripts/epic/epic-candidate-confirm.ts +3 -2
  143. package/scripts/epic/epic-classify.ts +1 -1
  144. package/scripts/epic/epic-cli-argv.ts +78 -46
  145. package/scripts/epic/epic-cli-remove.ts +11 -19
  146. package/scripts/epic/epic-cli.ts +74 -73
  147. package/scripts/epic/epic-close-comment.ts +1 -1
  148. package/scripts/epic/epic-close.ts +1 -1
  149. package/scripts/epic/epic-cross-repo.ts +5 -5
  150. package/scripts/epic/epic-graph.ts +1 -1
  151. package/scripts/epic/epic-index.ts +1 -1
  152. package/scripts/epic/epic-issue.ts +2 -6
  153. package/scripts/epic/epic-lane-offer.ts +2 -2
  154. package/scripts/epic/epic-next.ts +29 -42
  155. package/scripts/epic/epic-solo-stale.ts +1 -1
  156. package/scripts/epic/epic-triage.ts +8 -4
  157. package/scripts/epic/epic-validate.ts +1 -1
  158. package/scripts/followup/git-followup-finish.ts +2 -2
  159. package/scripts/followup/git-followup-label.ts +2 -2
  160. package/scripts/followup/git-followup-merged.ts +2 -2
  161. package/scripts/followup/git-pr-followup-checks.ts +142 -0
  162. package/scripts/followup/git-pr-followup.ts +4 -129
  163. package/scripts/gate/core-admission.ts +162 -0
  164. package/scripts/gate/core-budget.ts +190 -68
  165. package/scripts/gate/gate-plan.ts +80 -7
  166. package/scripts/gate/gate-report.ts +28 -9
  167. package/scripts/gate/gate-skip.ts +38 -7
  168. package/scripts/gate/hook-gate-reuse.ts +12 -10
  169. package/scripts/gate/machine-capacity.ts +175 -0
  170. package/scripts/gate/pre-commit-type-check.ts +4 -1
  171. package/scripts/gate/project-checks.ts +6 -9
  172. package/scripts/gate/type-check-step.ts +2 -3
  173. package/scripts/gate/verification-gate.ts +34 -10
  174. package/scripts/gh/git-gh-api-path.ts +2 -2
  175. package/scripts/gh/git-gh-issue-rest.ts +3 -3
  176. package/scripts/gh/git-gh-issue-write.ts +53 -7
  177. package/scripts/gh/git-gh-pr-read.ts +64 -19
  178. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  179. package/scripts/gh/git-gh-pr.ts +5 -1
  180. package/scripts/gh/git-pr-ai-review.ts +1 -1
  181. package/scripts/gh/git-pr-checks-eval.ts +1 -3
  182. package/scripts/gh/git-pr-checks-parse.ts +1 -1
  183. package/scripts/gh/git-pr-checks-watch.ts +6 -5
  184. package/scripts/gh/git-pr-checks.ts +7 -5
  185. package/scripts/gh/git-pr-coderabbit.ts +1 -1
  186. package/scripts/gh/git-pr-error.ts +1 -3
  187. package/scripts/gh/git-pr.ts +9 -13
  188. package/scripts/gh/github-issue-url.ts +2 -2
  189. package/scripts/git/constants.ts +2 -6
  190. package/scripts/git/git-command.ts +20 -218
  191. package/scripts/git/git-diff-reads.ts +225 -0
  192. package/scripts/git/git-error.ts +4 -12
  193. package/scripts/git/git-pre-push-hook.ts +139 -0
  194. package/scripts/git/git-prompt.ts +2 -6
  195. package/scripts/git/git-spawn-sync.ts +3 -1
  196. package/scripts/git/git-spawn.ts +1 -1
  197. package/scripts/git/git-staging.ts +4 -6
  198. package/scripts/git/git-status.ts +3 -9
  199. package/scripts/hooks/codex-hook-adapter.ts +1 -1
  200. package/scripts/hooks/edited-cspell.ts +2 -4
  201. package/scripts/hooks/format-edited-file.ts +3 -12
  202. package/scripts/hooks/pre-push-unit.ts +31 -9
  203. package/scripts/hooks/reserved-run.ts +2 -2
  204. package/scripts/hooks/run-hook.sh +5 -4
  205. package/scripts/hooks/stop-guard.ts +5 -3
  206. package/scripts/init/init-actions.ts +0 -5
  207. package/scripts/init/init-ai-copy.ts +13 -2
  208. package/scripts/init/init-logic-json-merge.ts +19 -0
  209. package/scripts/init/init-logic-secretlint.ts +7 -7
  210. package/scripts/init/init-logic-workspace.ts +41 -2
  211. package/scripts/init/init-logic.ts +16 -26
  212. package/scripts/init/init.ts +10 -16
  213. package/scripts/init/initial-commit.ts +4 -4
  214. package/scripts/init/kit-development-versions.ts +40 -0
  215. package/scripts/init/kit-setup-state.ts +2 -2
  216. package/scripts/init/package-manager-pin.ts +35 -0
  217. package/scripts/init/start-exec.ts +68 -19
  218. package/scripts/init/start-setup-pr.ts +9 -9
  219. package/scripts/init/start-steps.ts +6 -10
  220. package/scripts/issue/defect-rate-cli.ts +1 -1
  221. package/scripts/issue/git-next-issues.ts +1 -1
  222. package/scripts/issue/issue-auto-ok.ts +32 -7
  223. package/scripts/issue/issue-backlinks-cli.ts +2 -2
  224. package/scripts/issue/issue-backlinks.ts +9 -9
  225. package/scripts/issue/issue-comment-cli.ts +19 -13
  226. package/scripts/issue/issue-file-cli.ts +72 -22
  227. package/scripts/issue/issue-file.ts +29 -1
  228. package/scripts/issue/issue-labels.ts +13 -0
  229. package/scripts/issue/issue-release-cli.ts +155 -0
  230. package/scripts/issue/issue-release.ts +58 -0
  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 +14 -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 +18 -0
  238. package/scripts/josh/josh-commands-document.ts +1 -0
  239. package/scripts/josh/josh-commands-issue.ts +8 -1
  240. package/scripts/josh/josh-commands-lane.ts +24 -1
  241. package/scripts/josh/josh-commands-maintenance.ts +6 -6
  242. package/scripts/josh/josh-commands-run.ts +12 -7
  243. package/scripts/josh/josh-in-process.ts +28 -9
  244. package/scripts/josh/josh-logic.ts +34 -15
  245. package/scripts/josh/process-identity.ts +1 -3
  246. package/scripts/josh/session-language-cli.ts +3 -4
  247. package/scripts/lane/lane-await.ts +5 -1
  248. package/scripts/lane/lane-child-invocation.ts +2 -2
  249. package/scripts/lane/lane-cli.ts +12 -4
  250. package/scripts/lane/lane-dispatch.ts +14 -10
  251. package/scripts/lane/lane-environment.ts +5 -1
  252. package/scripts/lane/lane-handoff.ts +1 -1
  253. package/scripts/lane/lane-install.ts +2 -13
  254. package/scripts/lane/lane-launch-cli.ts +15 -1
  255. package/scripts/lane/lane-ledger.ts +145 -0
  256. package/scripts/lane/lane-leftover.ts +44 -30
  257. package/scripts/lane/lane-load.ts +108 -0
  258. package/scripts/lane/lane-measure-cli.ts +149 -0
  259. package/scripts/lane/lane-occupancy.ts +3 -0
  260. package/scripts/lane/lane-open.ts +5 -5
  261. package/scripts/lane/lane-paths.ts +13 -0
  262. package/scripts/lane/lane-reap.ts +1 -3
  263. package/scripts/lane/lane-registry.ts +11 -0
  264. package/scripts/lane/lane-relaunch.ts +1 -1
  265. package/scripts/lane/lane-stats.ts +224 -0
  266. package/scripts/lane/lane-stray.ts +140 -0
  267. package/scripts/lane/lane-vacant.ts +36 -0
  268. package/scripts/lane/openai-lane-ship.ts +2 -2
  269. package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
  270. package/scripts/lane/openai-lane-supervisor.ts +7 -165
  271. package/scripts/lane/openai-review-broker.ts +2 -4
  272. package/scripts/lib/ancestor-directories.ts +35 -0
  273. package/scripts/lib/animation-helpers.ts +2 -2
  274. package/scripts/lib/buffered-process.ts +3 -7
  275. package/scripts/lib/cli-flags.ts +71 -1
  276. package/scripts/lib/spawn-exit.ts +2 -2
  277. package/scripts/lib/timeouts.ts +35 -2
  278. package/scripts/lines/line-budget.ts +4 -6
  279. package/scripts/notify/telegram-test.ts +6 -0
  280. package/scripts/observations/observation-ledger-line.ts +6 -6
  281. package/scripts/observations/observation-ledger-migrate.ts +55 -4
  282. package/scripts/observations/observation-ledger.ts +26 -7
  283. package/scripts/overrides/lockfile-overrides.ts +1 -1
  284. package/scripts/overrides/overrides-logic.ts +1 -1
  285. package/scripts/propagate/propagate-git.ts +1 -1
  286. package/scripts/propagate/propagate-publish.ts +2 -2
  287. package/scripts/propagate/propagate-steps.ts +20 -15
  288. package/scripts/propagate/propagate.ts +36 -27
  289. package/scripts/refactor/import-graph.ts +18 -1
  290. package/scripts/refactor/refactor-lint.ts +4 -4
  291. package/scripts/release/github-release-environment.ts +1 -0
  292. package/scripts/release/github-release-runs.ts +115 -0
  293. package/scripts/release/github-release.ts +13 -67
  294. package/scripts/repo/repository-labels.ts +2 -2
  295. package/scripts/repo/required-checks-logic.ts +1 -3
  296. package/scripts/review/live-evidence.ts +2 -4
  297. package/scripts/review/review-brief-cli.ts +1 -1
  298. package/scripts/review/review-finding-ledger.ts +1 -1
  299. package/scripts/review/review-findings-cli.ts +1 -3
  300. package/scripts/rules/decision-oracle-batch.ts +128 -0
  301. package/scripts/rules/decision-oracle-tokens.ts +21 -0
  302. package/scripts/rules/decision-oracle.ts +8 -120
  303. package/scripts/rules/delivered-rules.ts +26 -182
  304. package/scripts/rules/direct-filing.ts +1 -1
  305. package/scripts/rules/early-heartbeat.ts +3 -3
  306. package/scripts/rules/git-argv.ts +1 -1
  307. package/scripts/rules/git-force.ts +1 -1
  308. package/scripts/rules/implementation-cut.ts +2 -2
  309. package/scripts/rules/index-guard.ts +1 -1
  310. package/scripts/rules/issue-comments.ts +118 -0
  311. package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
  312. package/scripts/rules/lane-background.ts +2 -2
  313. package/scripts/rules/lane-park.ts +18 -0
  314. package/scripts/rules/lane-split-park.ts +128 -0
  315. package/scripts/rules/oracle-firing.ts +1 -1
  316. package/scripts/rules/poll-loop.ts +1 -1
  317. package/scripts/rules/pre-gate-cut.ts +1 -1
  318. package/scripts/rules/protected-files.ts +59 -4
  319. package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
  320. package/scripts/rules/shell-body-trigger.ts +1 -1
  321. package/scripts/rules/stop-rules.ts +18 -7
  322. package/scripts/rules/test-declared-commit.ts +4 -4
  323. package/scripts/rules/wip-cap.ts +63 -0
  324. package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
  325. package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
  326. package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
  327. package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
  328. package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
  329. package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
  330. package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
  331. package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
  332. package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
  333. package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
  334. package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
  335. package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
  336. package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
  337. package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
  338. package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
  339. package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
  340. package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
  341. package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
  342. package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
  343. package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
  344. package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
  345. package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
  346. package/scripts/run/run-ending.ts +2 -2
  347. package/scripts/run/run-halfrun-resume.ts +6 -1
  348. package/scripts/run/run-headless.ts +1 -1
  349. package/scripts/run/run-invocation.ts +85 -84
  350. package/scripts/run/run-liveness.ts +1 -1
  351. package/scripts/run/run-parent-cut-hook.ts +1 -1
  352. package/scripts/run/run-preflight.ts +6 -6
  353. package/scripts/run/run-prep-cli.ts +1 -1
  354. package/scripts/run/run-prrun-resume.ts +1 -1
  355. package/scripts/run/run-report-cli.ts +4 -4
  356. package/scripts/run/run-report.ts +2 -2
  357. package/scripts/run/run-status-cli.ts +1 -1
  358. package/scripts/run/run-step-cli.ts +3 -3
  359. package/scripts/run/run-step.ts +7 -4
  360. package/scripts/run/run-stop-notify.ts +1 -1
  361. package/scripts/run/run-stranded-detect.ts +4 -4
  362. package/scripts/run/run-stranded.ts +2 -2
  363. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  364. package/scripts/run/run-watcher-guard.ts +5 -3
  365. package/scripts/run/run-watcher-hook.ts +1 -1
  366. package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
  367. package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
  368. package/scripts/run/ship/run-ship-lock.ts +50 -0
  369. package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
  370. package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
  371. package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
  372. package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
  373. package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
  374. package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
  375. package/scripts/run/ship/run-ship-stop-text.ts +13 -0
  376. package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
  377. package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
  378. package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
  379. package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
  380. package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
  381. package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
  382. package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
  383. package/scripts/run/wake/run-wake-restart.ts +58 -0
  384. package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
  385. package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
  386. package/scripts/safe-chain/preinstall-command.ts +20 -21
  387. package/scripts/safe-chain/project-config.ts +19 -15
  388. package/scripts/sync/sync-ai-files.ts +1 -1
  389. package/scripts/sync/sync-configs.ts +2 -1
  390. package/scripts/sync/sync.ts +15 -2
  391. package/scripts/test/unit-worker-share.ts +1 -0
  392. package/scripts/time-runtime/time-background.ts +5 -1
  393. package/scripts/time-runtime/time-instant.ts +3 -1
  394. package/scripts/time-runtime/time-spans.ts +6 -139
  395. package/scripts/time-runtime/time-tool-call.ts +160 -0
  396. package/scripts/version/effective-upstream.ts +2 -20
  397. package/scripts/version/latest-corepack.ts +124 -93
  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 +6 -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/git/{schemas.ts → git-schemas.ts} +0 -0
  419. /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
  420. /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
  421. /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
  422. /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
  423. /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
  424. /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
  425. /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
  426. /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
  427. /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
  428. /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
  429. /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
  430. /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
  431. /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
  432. /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
  433. /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
  434. /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
  435. /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
  436. /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
  437. /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
  438. /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
  439. /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
  440. /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
  441. /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
  442. /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { backlog_budget_cli } from '#scripts/backlog/backlog-budget-cli'
2
+ import { cli_flags } from '#scripts/lib/cli-flags'
2
3
  import { run_issue_number } from './run-issue-number'
3
4
 
4
5
  // The grammar of an invocation that can be carried across a session cut, and the only place it is
@@ -39,17 +40,34 @@ const KNOWN_FLAGS: ReadonlyArray<string> = ['--max', '--idle']
39
40
  // pool" (joshuafolkken/kit#1984). It is carried across a cut like any other part of the invocation, so
40
41
  // a resumed session that reads it back does not silently start draining the backlog the person excluded.
41
42
  const ONLY_FLAG = '--only'
43
+ // The read itself is `cli_flags`'s (joshuafolkken/kit#3261): strict, so an unknown flag, a flag with
44
+ // no value, `--only=<x>` and a value that is itself a flag are all refused before this file looks.
45
+ const OPTIONS = {
46
+ max: { type: 'string' },
47
+ idle: { type: 'string' },
48
+ only: { type: 'boolean' },
49
+ } as const
42
50
  const ISSUE_PREFIX = '#'
51
+ const FLAG_PREFIX = '-'
43
52
  const TOKEN_SEPARATOR = ' '
44
53
  const FIRST_TOKEN = 0
45
54
  const FIRST_ARGUMENT_INDEX = 1
46
- const VALUE_OFFSET = 1
47
- const FLAG_PAIR_LENGTH = 2
48
- // `--only` advances the scan by one token, having no value where a `--max` / `--idle` pair advances two.
49
- const ONLY_STEP = 1
50
55
  const EMPTY_LENGTH = 0
51
56
  const NOT_FOUND = -1
52
57
 
58
+ // One token of `parseArgs`'s `tokens` answer, as far as this file reads it: a positional's text, or an
59
+ // option's name and the value it was given (`--max 5` and `--max=5` alike).
60
+ interface ArgumentToken {
61
+ kind: string
62
+ name?: string
63
+ value?: string | undefined
64
+ }
65
+
66
+ interface Invocation {
67
+ issues: ReadonlyArray<number>
68
+ flags: ReadonlyArray<string>
69
+ }
70
+
53
71
  // A space is the only separator that can survive `detached-launch.ts`'s `is_safe_value`, which has
54
72
  // already refused every control character — so the split needs no pattern, and this file is left with
55
73
  // no regular expression of its own for a backtracking analysis to find. The one pattern it does use
@@ -60,57 +78,33 @@ function tokens_of(invocation: string): ReadonlyArray<string> {
60
78
 
61
79
  // **The integer check is `backlog:budget`'s own, imported rather than restated.** It is the command
62
80
  // this invocation is going to be handed to, so a second copy of the rule here would be free to drift
63
- // from the one that actually reads the number.
64
- function flag_token(token: string, raw: string | undefined): string | undefined {
65
- const flag = KNOWN_FLAGS.find((known) => known === token)
66
- const value = backlog_budget_cli.to_count(raw)
81
+ // from the one that actually reads the number. `--only` carries no value and renders to its own
82
+ // constant for the same taint reason every other token does — the constant severs the record's string
83
+ // from the rebuilt one.
84
+ function render_option(token: ArgumentToken): string | undefined {
85
+ if (token.name === cli_flags.option_name(ONLY_FLAG)) return ONLY_FLAG
86
+ const flag = KNOWN_FLAGS.find((known) => cli_flags.option_name(known) === token.name)
87
+ const value = backlog_budget_cli.to_count(token.value)
67
88
 
68
89
  if (flag === undefined || value === undefined) return undefined
69
90
 
70
91
  return `${flag} ${String(value)}`
71
92
  }
72
93
 
73
- // One rendered flag and how far it advanced the scan: two tokens for a valued flag, one for `--only`.
74
- // The advance is returned rather than assumed, so `flag_tokens` handles a valueless flag mixed in with
75
- // the pairs without knowing which kind it read.
76
- interface FlagRead {
77
- part: string
78
- advance: number
79
- }
80
-
81
- // **`--only` is read before the valued flags**, because it is the one token that carries no value: read
82
- // as a valued flag it would swallow whatever follows it as its argument. It renders to its own constant
83
- // for the same taint reason every other token does — the constant severs the record's string from the
84
- // rebuilt one.
85
- function read_flag(tokens: ReadonlyArray<string>, index: number): FlagRead | undefined {
86
- const token = tokens[index] ?? ''
87
-
88
- if (token === ONLY_FLAG) return { part: ONLY_FLAG, advance: ONLY_STEP }
89
-
90
- const part = flag_token(token, tokens[index + VALUE_OFFSET])
91
-
92
- return part === undefined ? undefined : { part, advance: FLAG_PAIR_LENGTH }
93
- }
94
-
95
94
  // **An unknown flag is refused, never dropped.** Dropping one would wake a session running to a budget
96
95
  // the person did not declare the first time `backlogrun` grows a flag this list has not caught up with
97
96
  // — which is the failure that is hardest to notice, because the session runs and looks fine. A stray
98
- // `#N` after the flags lands here too, as a token that matches no known flag, and is refused: the
99
- // named list is the invocation's leading block, never interleaved with the budget.
100
- function flag_tokens(
101
- tokens: ReadonlyArray<string>,
102
- start: number,
103
- ): ReadonlyArray<string> | undefined {
97
+ // `#N` after the flags lands here too, as a token that is no option, and is refused: the named list is
98
+ // the invocation's leading block, never interleaved with the budget. So is a `--` terminator.
99
+ function rendered_options(tokens: ReadonlyArray<ArgumentToken>): ReadonlyArray<string> | undefined {
104
100
  const rendered: Array<string> = []
105
- let index = start
106
101
 
107
- while (index < tokens.length) {
108
- const read = read_flag(tokens, index)
102
+ for (const token of tokens) {
103
+ const part = render_option(token)
109
104
 
110
- if (read === undefined) return undefined
105
+ if (part === undefined) return undefined
111
106
 
112
- rendered.push(read.part)
113
- index += read.advance
107
+ rendered.push(part)
114
108
  }
115
109
 
116
110
  return rendered
@@ -135,25 +129,15 @@ function issue_of(token: string): number | undefined {
135
129
  return Number.isSafeInteger(issue) ? issue : undefined
136
130
  }
137
131
 
138
- // The named issues are the leading block of `#N` tokens, in the order they were typed. The scan stops
139
- // at the first token that is not an issue reference — that is where the budget flags begin — and a
140
- // `#`-prefixed token that does not validate refuses the whole invocation rather than being read as the
141
- // start of the flags. An empty block is a valid answer here (a bare `backlogrun` or a budget-only one);
142
- // `issue_numbers` is where "no named issues" becomes `undefined`.
143
- // The contiguous leading block of `#`-prefixed tokens — everything up to the first token that is not
144
- // an issue reference, which is where the budget flags begin.
145
- function take_leading_prefixed(args: ReadonlyArray<string>): ReadonlyArray<string> {
146
- const stop = args.findIndex((token) => !token.startsWith(ISSUE_PREFIX))
147
-
148
- return args.slice(FIRST_TOKEN, stop === NOT_FOUND ? args.length : stop)
149
- }
150
-
151
- function leading_issues(tokens: ReadonlyArray<string>): ReadonlyArray<number> | undefined {
132
+ // The named issues are the leading block of positionals, in the order they were typed, and each must be
133
+ // a `#N` that validates — anything else refuses the whole invocation rather than being dropped. An
134
+ // empty block is a valid answer here (a bare `backlogrun` or a budget-only one); `issue_numbers` is
135
+ // where "no named issues" becomes `undefined`.
136
+ function issues_of(tokens: ReadonlyArray<ArgumentToken>): ReadonlyArray<number> | undefined {
152
137
  const issues: Array<number> = []
153
- const prefixed = take_leading_prefixed(tokens.slice(FIRST_ARGUMENT_INDEX))
154
138
 
155
- for (const token of prefixed) {
156
- const issue = issue_of(token)
139
+ for (const token of tokens) {
140
+ const issue = issue_of(token.value ?? '')
157
141
 
158
142
  if (issue === undefined) return undefined
159
143
 
@@ -163,6 +147,35 @@ function leading_issues(tokens: ReadonlyArray<string>): ReadonlyArray<number> |
163
147
  return issues
164
148
  }
165
149
 
150
+ // The arguments after the command word, as `parseArgs` tokens, or `undefined` for another command or
151
+ // an argument list the strict read refuses.
152
+ function argument_tokens(invocation: string): ReadonlyArray<ArgumentToken> | undefined {
153
+ const tokens = tokens_of(invocation)
154
+
155
+ if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return undefined
156
+
157
+ return cli_flags.parse_or_undefined({
158
+ args: tokens.slice(FIRST_ARGUMENT_INDEX),
159
+ options: OPTIONS,
160
+ allowPositionals: true,
161
+ tokens: true,
162
+ })?.tokens
163
+ }
164
+
165
+ // The named list is everything before the first option, and the budget flags everything from it on.
166
+ function parse(invocation: string): Invocation | undefined {
167
+ const tokens = argument_tokens(invocation)
168
+ if (tokens === undefined) return undefined
169
+ const stop = tokens.findIndex((token) => token.kind !== 'positional')
170
+ const split_at = stop === NOT_FOUND ? tokens.length : stop
171
+ const issues = issues_of(tokens.slice(FIRST_TOKEN, split_at))
172
+ const flags = rendered_options(tokens.slice(split_at))
173
+
174
+ if (issues === undefined || flags === undefined) return undefined
175
+
176
+ return { issues, flags }
177
+ }
178
+
166
179
  function issue_token(issue: number): string {
167
180
  return `${ISSUE_PREFIX}${String(issue)}`
168
181
  }
@@ -174,26 +187,17 @@ function joined(command: string, rendered: ReadonlyArray<string>): string {
174
187
  // The named list rebuilds first and the budget flags after it, so the canonical text is
175
188
  // `backlogrun #1 #2 --max 5` whatever spacing the record held. Both halves are composed from this
176
189
  // file's own constants and validated integers, so no token of the record survives into the result.
177
- function rebuilt_backlog(tokens: ReadonlyArray<string>): string | undefined {
178
- const issues = leading_issues(tokens)
179
-
180
- if (issues === undefined) return undefined
181
-
182
- const flags = flag_tokens(tokens, FIRST_ARGUMENT_INDEX + issues.length)
183
-
184
- if (flags === undefined) return undefined
185
-
186
- return joined(BACKLOG_COMMAND, [...issues.map((issue) => issue_token(issue)), ...flags])
187
- }
188
-
189
190
  // The command word is compared against this file's own constant and the **constant** is what the
190
191
  // rebuilt text is composed from, so no token of the record survives into the result.
191
192
  function rebuild(invocation: string): string | undefined {
192
- const tokens = tokens_of(invocation)
193
+ const parsed = parse(invocation)
193
194
 
194
- if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return undefined
195
+ if (parsed === undefined) return undefined
195
196
 
196
- return rebuilt_backlog(tokens)
197
+ return joined(BACKLOG_COMMAND, [
198
+ ...parsed.issues.map((issue) => issue_token(issue)),
199
+ ...parsed.flags,
200
+ ])
197
201
  }
198
202
 
199
203
  // The issues a `backlogrun` invocation named, in the order it named them. An invocation with no named
@@ -201,12 +205,13 @@ function rebuild(invocation: string): string | undefined {
201
205
  // "this invocation named no issues" and "this run has no issues left" are different facts, and a
202
206
  // caller that could not tell them apart would report a finished sequential run as one that never had a
203
207
  // list. A malformed leading `#N` answers `undefined` too, because there is then no list to trust.
208
+ // Only the leading block is read, so a flag this grammar does not carry — `backlog:plan`'s `--exclude`
209
+ // — after it does not hide the named issues.
204
210
  function issue_numbers(invocation: string): ReadonlyArray<number> | undefined {
205
211
  const tokens = tokens_of(invocation)
206
-
207
- if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return undefined
208
-
209
- const issues = leading_issues(tokens)
212
+ const stop = tokens.findIndex((token) => token.startsWith(FLAG_PREFIX))
213
+ const leading = stop === NOT_FOUND ? tokens : tokens.slice(FIRST_TOKEN, stop)
214
+ const issues = parse(leading.join(TOKEN_SEPARATOR))?.issues
210
215
 
211
216
  if (issues === undefined || issues.length === EMPTY_LENGTH) return undefined
212
217
 
@@ -214,15 +219,11 @@ function issue_numbers(invocation: string): ReadonlyArray<number> | undefined {
214
219
  }
215
220
 
216
221
  // Whether the invocation carries `--only`, so a resumed session runs the named list and stops rather
217
- // than draining the pool. It reads the token from the same grammar `rebuild` composes from, so the two
222
+ // than draining the pool. It reads the flag from the same parse `rebuild` composes from, so the two
218
223
  // cannot disagree on what `--only` looks like. A non-`backlogrun` invocation carries none, which is
219
224
  // `false`.
220
225
  function has_only(invocation: string): boolean {
221
- const tokens = tokens_of(invocation)
222
-
223
- if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return false
224
-
225
- return tokens.slice(FIRST_ARGUMENT_INDEX).includes(ONLY_FLAG)
226
+ return parse(invocation)?.flags.includes(ONLY_FLAG) === true
226
227
  }
227
228
 
228
229
  // **Three entries, because three things call in.** The command word, the flag list and the token
@@ -7,7 +7,7 @@ import { git_gh_issue_read } from '#scripts/gh/git-gh-issue-read'
7
7
  import { ALREADY_DONE_LABEL, NEEDS_DECISION_LABEL } from '#scripts/issue/issue-labels'
8
8
  import { issue_state } from '#scripts/issue/issue-state'
9
9
  import { PLATFORM_TEMP_ROOT } from '#scripts/josh/platform-temporary'
10
- import { run_hold } from './run-hold'
10
+ import { run_hold } from '#scripts/run/hold/run-hold'
11
11
  import { run_issue_number } from './run-issue-number'
12
12
 
13
13
  // `josh run:liveness <N>` — whether the delegated unit running child `<N>` is still working, or
@@ -5,7 +5,7 @@ import { cost_verdict } from '#scripts/cost-runtime/cost-verdict'
5
5
  import { hook_decision } from '#scripts/josh/hook-decision'
6
6
  import { error_text } from '#scripts/lib/error-message'
7
7
  import { implementation_cut_verdict } from '#scripts/rules/implementation-cut-verdict'
8
- import { run_carry, type RunCarry } from './run-carry'
8
+ import { run_carry, type RunCarry } from '#scripts/run/carry/run-carry'
9
9
  import { run_headless } from './run-headless'
10
10
  import { run_watcher_hook } from './run-watcher-hook'
11
11
 
@@ -1,8 +1,8 @@
1
1
  import { git_gh_pr_read } from '#scripts/gh/git-gh-pr-read'
2
2
  import { git_command } from '#scripts/git/git-command'
3
3
  import { lane_registry, type LaneInfo } from '#scripts/lane/lane-registry'
4
+ import { run_hold } from '#scripts/run/hold/run-hold'
4
5
  import { z } from 'zod'
5
- import { run_hold } from './run-hold'
6
6
  import { run_issue_number } from './run-issue-number'
7
7
 
8
8
  // The preflight check — what an interrupted run left in this working tree, and what the rule says to
@@ -230,11 +230,11 @@ async function read_tree_state(): Promise<TreeState> {
230
230
  // could not complete (joshuafolkken/kit#1048). That throw reaches the CLI as `unknown`; without it a
231
231
  // rate-limited or logged-out `gh` turns a **merged** pull request into `resume`, and the run commits
232
232
  // on top of work somebody already landed — the exact hazard `park` exists for.
233
- // **The second half of the same guard.** `pr_exists` refuses to read an unreadable lookup as an
234
- // absence, but `pr_view` still folds any failure of its own into `''` — and the two are separate
235
- // round trips, so a rate limit or a 5xx arriving between them would put a **merged** pull request
236
- // back through `NO_PR` and out as `resume`. An empty answer for a branch `pr_exists` has just
237
- // confirmed is therefore a failed read by construction, and it throws rather than answering.
233
+ // **The second half of the same guard.** `pr_view` throws on a read it could not complete
234
+ // (joshuafolkken/kit#3263), but the two are separate round trips, so a lookup that answers empty
235
+ // between them would still put a **merged** pull request back through `NO_PR` and out as `resume`.
236
+ // An empty answer for a branch `pr_exists` has just confirmed is therefore a failed read by
237
+ // construction, and it throws rather than answering.
238
238
  async function read_pr_state(branch_name: string): Promise<PrState> {
239
239
  if (!(await git_gh_pr_read.pr_exists(branch_name))) return NO_PR
240
240
 
@@ -5,10 +5,10 @@ import { issue_read_cli, type BlockRead } from '#scripts/issue/issue-read-cli'
5
5
  import { issue_state_cli, type StateRead } from '#scripts/issue/issue-state-cli'
6
6
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
7
7
  import { error_text } from '#scripts/lib/error-message'
8
+ import { run_ship_preflight } from '#scripts/run/ship/run-ship-preflight'
8
9
  import { latest_scope_cli } from '#scripts/version/latest-scope-cli'
9
10
  import { run_prep, type PrepParts } from './run-prep'
10
11
  import { run_prep_locate } from './run-prep-locate'
11
- import { run_ship_preflight } from './run-ship-preflight'
12
12
 
13
13
  // `josh run:prep <N>` — one call for the reads a `fullrun` makes before its first edit
14
14
  // (joshuafolkken/kit#1978): the issue body and comments (`issue:read`), the state, labels and
@@ -2,8 +2,8 @@ import { git_gh_command } from '#scripts/gh/git-gh-command'
2
2
  import type { PullMergeState } from '#scripts/gh/git-gh-pr-read'
3
3
  import { git_branch } from '#scripts/git/git-branch'
4
4
  import { git_command } from '#scripts/git/git-command'
5
+ import { run_hold, type HoldRead } from '#scripts/run/hold/run-hold'
5
6
  import { run_halfrun_resume, type StopMark } from './run-halfrun-resume'
6
- import { run_hold, type HoldRead } from './run-hold'
7
7
 
8
8
  // joshuafolkken/kit#3023: a `prrun` stops at a green, mergeable pull request and **keeps** its hold, so
9
9
  // a person can look before anything merges. What comes next is one of three, and `run:entry` answers
@@ -2,10 +2,10 @@
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { git_followup_pending } from '#scripts/followup/git-followup-pending'
4
4
  import { release_scope_cli } from '#scripts/release/release-scope-cli'
5
- import { run_carry } from './run-carry'
6
- import { run_event_scope, type EventScope } from './run-event-scope'
7
- import { run_event_stream, type RunEvent } from './run-event-stream'
8
- import { run_event_stream_emit } from './run-event-stream-emit'
5
+ import { run_carry } from '#scripts/run/carry/run-carry'
6
+ import { run_event_scope, type EventScope } from '#scripts/run/event/run-event-scope'
7
+ import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
8
+ import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
9
9
  import { run_report } from './run-report'
10
10
 
11
11
  // `josh run:report` — the session-facing report, generated from the run's event stream rather than
@@ -1,6 +1,6 @@
1
1
  import { release_scope_cli, type Decision } from '#scripts/release/release-scope-cli'
2
- import { run_event_scope, type EventScope } from './run-event-scope'
3
- import { run_event_stream, type RunEvent } from './run-event-stream'
2
+ import { run_event_scope, type EventScope } from '#scripts/run/event/run-event-scope'
3
+ import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
4
4
 
5
5
  // The session-facing report generated *from* the run's event stream, rather than composed by hand each
6
6
  // time (joshuafolkken/kit#2249). #2205 gave the run a typed, ordered stream of what happened — a plan, a
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url'
3
3
  import { cost_cli, type CostVerdict } from '#scripts/cost-runtime/cost-cli'
4
4
  import { issue_state_cli, type StateRead } from '#scripts/issue/issue-state-cli'
5
5
  import { cli_flags } from '#scripts/lib/cli-flags'
6
- import { run_carry, type CarryRead } from './run-carry'
6
+ import { run_carry, type CarryRead } from '#scripts/run/carry/run-carry'
7
7
  import { run_status, type StatusParts } from './run-status'
8
8
 
9
9
  // `josh run:status <N>` — one call for the read-only status a run glances at (joshuafolkken/kit#2165):
@@ -6,9 +6,9 @@ import { hook_decision } from '#scripts/josh/hook-decision'
6
6
  import { find_package_directory } from '#scripts/josh/josh-logic'
7
7
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
8
8
  import { lane_reap } from '#scripts/lane/lane-reap'
9
- import { run_carry, type CarryRead } from './run-carry'
10
- import { run_event_scope } from './run-event-scope'
11
- import { run_event_stream } from './run-event-stream'
9
+ import { run_carry, type CarryRead } from '#scripts/run/carry/run-carry'
10
+ import { run_event_scope } from '#scripts/run/event/run-event-scope'
11
+ import { run_event_stream } from '#scripts/run/event/run-event-stream'
12
12
  import { run_headless } from './run-headless'
13
13
  import { run_prep, type PrepParts } from './run-prep'
14
14
  import { run_prep_cli } from './run-prep-cli'
@@ -1,7 +1,7 @@
1
1
  import { git_stash } from '#scripts/git/stash/git-stash'
2
+ import type { CarryRead } from '#scripts/run/carry/run-carry'
3
+ import { run_event_stream } from '#scripts/run/event/run-event-stream'
2
4
  import { latest_scope_cli } from '#scripts/version/latest-scope-cli'
3
- import type { CarryRead } from './run-carry'
4
- import { run_event_stream } from './run-event-stream'
5
5
  import { run_retrospective } from './run-retrospective'
6
6
 
7
7
  // `josh run:step <N>` — the run's next single action, computed from three mechanical inputs and nothing
@@ -236,9 +236,12 @@ function event_action(event: string, issue_number: string, is_lane_child: boolea
236
236
  }
237
237
 
238
238
  // A run that has planned but emitted nothing else is still at its pre-implementation position, so a
239
- // `plan` reads the same as an empty stream.
239
+ // `plan` reads the same as an empty stream. A resume from an implementation cut is back at the same
240
+ // position (joshuafolkken/kit#3375): read as the `cut` it follows, it pointed at the spent resume again.
241
+ const IMPLEMENTATION_POSITIONS: ReadonlySet<string> = new Set([KIND.PLAN, KIND.RESUME])
242
+
240
243
  function is_pre_implementation(last_event: string | undefined): boolean {
241
- return last_event === undefined || last_event === run_event_stream.EVENT_KIND.PLAN
244
+ return last_event === undefined || IMPLEMENTATION_POSITIONS.has(last_event)
242
245
  }
243
246
 
244
247
  // **No cut at the setup→implementation boundary** (joshuafolkken/kit#2489). A lane child's context is
@@ -1,5 +1,5 @@
1
1
  import { telegram_notify } from '#scripts/notify/telegram-notify'
2
- import type { CarryRead } from './run-carry'
2
+ import type { CarryRead } from '#scripts/run/carry/run-carry'
3
3
 
4
4
  // **A `backlogrun` that halts because a person is needed used to reach nobody once the session was
5
5
  // cut** (joshuafolkken/kit#2136). Every remaining child blocked behind a parked one, the backlog
@@ -1,14 +1,14 @@
1
1
  import { telegram_notify } from '#scripts/notify/telegram-notify'
2
- import { run_carry, type CarryRead, type RunCarry } from './run-carry'
3
- import { run_event_stream } from './run-event-stream'
4
- import { run_event_stream_emit } from './run-event-stream-emit'
2
+ import { run_carry, type CarryRead, type RunCarry } from '#scripts/run/carry/run-carry'
3
+ import { run_event_stream } from '#scripts/run/event/run-event-stream'
4
+ import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
5
+ import { run_wake, type RunWake } from '#scripts/run/wake/run-wake'
5
6
  import {
6
7
  run_stranded,
7
8
  type StrandedInput,
8
9
  type StrandedVerdict,
9
10
  type SupervisorLiveness,
10
11
  } from './run-stranded'
11
- import { run_wake, type RunWake } from './run-wake'
12
12
 
13
13
  // The I/O half of the strand detector (joshuafolkken/kit#2375): it gathers the three facts the pure
14
14
  // judge in `run-stranded.ts` weighs, then — on a strand — leaves the marker and sends the one
@@ -1,6 +1,6 @@
1
1
  import { process_identity } from '#scripts/josh/process-identity'
2
- import { run_carry, type CarryRead, type RunCarry } from './run-carry'
3
- import type { RunWake } from './run-wake'
2
+ import { run_carry, type CarryRead, type RunCarry } from '#scripts/run/carry/run-carry'
3
+ import type { RunWake } from '#scripts/run/wake/run-wake'
4
4
 
5
5
  // The mechanical judge of a *stranded* run (joshuafolkken/kit#2375). On 2026-09-22 a `backlogrun`'s
6
6
  // cost hand-off left the record carried and handed off, the session that cut it died, no successor ever
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import { run_progress_read } from './run-progress-read'
3
+ import { run_progress_read } from '#scripts/run/progress/run-progress-read'
4
4
  import { run_watcher_guard } from './run-watcher-guard'
5
5
 
6
6
  // `josh run:watcher:guard` — refuses when lane children are in-flight but the progress watcher has not
@@ -1,6 +1,6 @@
1
1
  import { lane_registry } from '#scripts/lane/lane-registry'
2
+ import { run_progress_clock } from '#scripts/run/progress/run-progress-clock'
2
3
  import { run_headless } from './run-headless'
3
- import { run_progress_clock } from './run-progress-clock'
4
4
 
5
5
  // A watcher that stopped while children are still in-flight leaves the run's event stream without a
6
6
  // heartbeat and the parent without the wake an arrival would deliver. This guard detects that state so
@@ -37,9 +37,11 @@ const OK_RESULT: GuardResult = { kind: 'ok' }
37
37
  // **Only the run that opened the lanes owes them a watcher** (joshuafolkken/kit#2965). The lane listing
38
38
  // is machine-wide, so a session with no run of its own — an investigation, a `fullrun` beside a batch —
39
39
  // used to be refused for another run's lanes. The session that drives the live `backlogrun` carry
40
- // record is the one that dispatched them; every other session passes.
40
+ // record is the one that dispatched them; every other session passes. A session `run:wake` woke owes
41
+ // none either (joshuafolkken/kit#3245): the supervisor's driver watches the lanes, and the woken session
42
+ // only acts on the branch it was handed and cuts.
41
43
  async function owes_watcher(): Promise<boolean> {
42
- if (!(await run_headless.is_backlog_parent())) return false
44
+ if (run_headless.is_headless() || !(await run_headless.is_backlog_parent())) return false
43
45
 
44
46
  return await lane_registry.has_lanes_in_flight()
45
47
  }
@@ -1,7 +1,7 @@
1
1
  import { hook_decision } from '#scripts/josh/hook-decision'
2
2
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
3
+ import { run_progress_read } from '#scripts/run/progress/run-progress-read'
3
4
  import { z } from 'zod'
4
- import { run_progress_read } from './run-progress-read'
5
5
  import { run_watcher_guard } from './run-watcher-guard'
6
6
 
7
7
  // The `PreToolUse` adapter that turns `run_watcher_guard`'s verdict into a deny reason, so the guard
@@ -2,8 +2,8 @@
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
4
4
  import { cli_flags } from '#scripts/lib/cli-flags'
5
- import { run_event_stream } from './run-event-stream'
6
- import { run_event_stream_emit } from './run-event-stream-emit'
5
+ import { run_event_stream } from '#scripts/run/event/run-event-stream'
6
+ import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
7
7
  import { run_ship, type ShipSection } from './run-ship'
8
8
  import { run_ship_detach } from './run-ship-detach'
9
9
  import { run_ship_next } from './run-ship-next'
@@ -3,10 +3,10 @@ import { setTimeout as sleep } from 'node:timers/promises'
3
3
  import { agent_role_profile } from '#scripts/agent/agent-role-profile'
4
4
  import { process_identity } from '#scripts/josh/process-identity'
5
5
  import { stamp_file } from '#scripts/josh/stamp-file'
6
+ import { detached_launch, type LaunchArgv } from '#scripts/run/detached-launch'
7
+ import { run_event_stream } from '#scripts/run/event/run-event-stream'
8
+ import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
6
9
  import { z } from 'zod'
7
- import { detached_launch, type LaunchArgv } from './detached-launch'
8
- import { run_event_stream } from './run-event-stream'
9
- import { run_event_stream_emit } from './run-event-stream-emit'
10
10
 
11
11
  // `josh ship --detach` — hand the post-implementation region to a supervisor that outlives the agent
12
12
  // (joshuafolkken/kit#2428). A lane child used to take the pre-gate cut and relaunch a fresh agent session
@@ -0,0 +1,50 @@
1
+ import { existsSync } from 'node:fs'
2
+ import path from 'node:path'
3
+ import { stripVTControlCharacters } from 'node:util'
4
+ import { buffered_process } from '#scripts/lib/buffered-process'
5
+ import { INSTALL_TIMEOUT_MS } from '#scripts/lib/timeouts'
6
+
7
+ // A lock file that no longer matches its inputs, asked before the gate (joshuafolkken/kit#3307).
8
+ //
9
+ // The pre-push hook's setup runs `pnpm install`, which rewrites a lock that has drifted — a
10
+ // `.pnpmfile.mjs` edit moves `pnpmfileChecksum` — and so leaves the working tree dirty under the push.
11
+ // The hook then refuses the gate's record and re-runs the whole unit suite, long enough under a crowded
12
+ // machine to time the push out. The frozen, lock-only install answers the same question without
13
+ // writing anything or touching `node_modules`, in about a third of a second.
14
+ //
15
+ // A project with no lock file has nothing to drift, so nothing is asked there.
16
+ //
17
+ // **pnpm's own error line rides along**: the install refuses for reasons other than drift too — an
18
+ // engine mismatch, a timeout on a crowded machine — and there "run `pnpm install`" fixes nothing, so
19
+ // the reader needs pnpm's error code to tell the two apart. The line naming an `ERR_PNPM_` code wins
20
+ // over the first line, which in a workspace is pnpm's "Scope: all N workspace projects" banner.
21
+
22
+ const LOCK_FILE = 'pnpm-lock.yaml'
23
+ const PNPM_ERROR_CODE = 'ERR_PNPM_'
24
+ const LOCK_CHECK_ARGUMENTS = ['install', '--frozen-lockfile', '--lockfile-only', '--ignore-scripts']
25
+ const LOCK_DRIFT = `${LOCK_FILE} does not match its inputs (\`pnpm install --frozen-lockfile\` refuses it), so the pre-push hook's install would rewrite it under the push: run \`pnpm install\` and commit ${LOCK_FILE}.`
26
+
27
+ function drift_problem(output: string): string {
28
+ const lines = stripVTControlCharacters(output)
29
+ .split('\n')
30
+ .map((line) => line.trim())
31
+ .filter((line) => line !== '')
32
+ const reason = lines.find((line) => line.includes(PNPM_ERROR_CODE)) ?? lines[0]
33
+
34
+ return reason === undefined ? LOCK_DRIFT : `${LOCK_DRIFT} pnpm said: ${reason}`
35
+ }
36
+
37
+ async function lock_problems(directory: string = process.cwd()): Promise<Array<string>> {
38
+ if (!existsSync(path.join(directory, LOCK_FILE))) return []
39
+
40
+ const result = await buffered_process.run_buffered_process(LOCK_CHECK_ARGUMENTS, {
41
+ cwd: directory,
42
+ timeout_ms: INSTALL_TIMEOUT_MS,
43
+ })
44
+
45
+ return buffered_process.is_process_failed(result) ? [drift_problem(result.output)] : []
46
+ }
47
+
48
+ const run_ship_lock = { LOCK_CHECK_ARGUMENTS, LOCK_DRIFT, lock_problems }
49
+
50
+ export { run_ship_lock }
@@ -7,6 +7,7 @@ import { git_preflight } from '#scripts/git/git-preflight'
7
7
  import type { JoshResult } from '#scripts/josh/josh-run'
8
8
  import { error_text } from '#scripts/lib/error-message'
9
9
  import { live_evidence } from '#scripts/review/live-evidence'
10
+ import { run_ship_lock } from './run-ship-lock'
10
11
  import { run_ship_scoped } from './run-ship-scoped'
11
12
 
12
13
  // `josh ship`'s first stage (joshuafolkken/kit#2946). A detached ship used to meet the pull request's
@@ -71,13 +72,16 @@ async function caught(problems: Promise<Array<string>>): Promise<Array<string>>
71
72
  }
72
73
  }
73
74
 
75
+ // The lock file is asked here too (joshuafolkken/kit#3307), so a drift stops the ship before the gate
76
+ // rather than being rewritten by the pre-push hook's install under the push.
74
77
  async function asked_problems(request: PreflightRequest): Promise<Array<string>> {
75
- const [preflight, evidence] = await Promise.all([
78
+ const [preflight, evidence, lock] = await Promise.all([
76
79
  caught(git_preflight.problems_of({ cli_input: request.title, will_open_pr: true })),
77
80
  caught(evidence_problems(request.body_path)),
81
+ caught(run_ship_lock.lock_problems()),
78
82
  ])
79
83
 
80
- return [...preflight, ...evidence]
84
+ return [...preflight, ...evidence, ...lock]
81
85
  }
82
86
 
83
87
  async function stage(request: PreflightRequest): Promise<JoshResult> {
@@ -1,8 +1,8 @@
1
1
  import { git_command } from '#scripts/git/git-command'
2
2
  import { git_worktree } from '#scripts/git/git-worktree'
3
3
  import { error_text } from '#scripts/lib/error-message'
4
- import { run_hold } from './run-hold'
5
- import { run_preflight } from './run-preflight'
4
+ import { run_hold } from '#scripts/run/hold/run-hold'
5
+ import { run_preflight } from '#scripts/run/run-preflight'
6
6
  import { run_ship_stage, type ShipState } from './run-ship-stage'
7
7
 
8
8
  // The read side of a resumed `josh ship` (joshuafolkken/kit#2426): the repository's actual state, asked
@@ -3,10 +3,11 @@ import { lane_child_invocation } from '#scripts/lane/lane-child-invocation'
3
3
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
4
4
  import { lane_registry, type LaneInfo } from '#scripts/lane/lane-registry'
5
5
  import { lane_relaunch } from '#scripts/lane/lane-relaunch'
6
- import { run_event_stream } from './run-event-stream'
7
- import { run_event_stream_emit } from './run-event-stream-emit'
6
+ import { run_event_stream } from '#scripts/run/event/run-event-stream'
7
+ import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
8
8
  import type { ShipResume } from './run-ship-next'
9
9
  import { run_ship_stage, type Stage } from './run-ship-stage'
10
+ import { run_ship_stop_text } from './run-ship-stop-text'
10
11
 
11
12
  // How a detached `josh ship` supervisor hands a stopped stage back (joshuafolkken/kit#2428). The agent
12
13
  // ended when it handed the region over, so a red gate, a High/Medium review, a failed push or red CI has
@@ -29,7 +30,7 @@ const OPENAI_PROVIDER = 'openai'
29
30
  type ReturnOutcome = 'relaunched' | 'recorded'
30
31
 
31
32
  function stop_text(issue: string, stage: Stage, resume: ShipResume): string {
32
- return `#${issue} ${run_ship_stage.stop_reason(stage, resume.conflicts)} failed — pnpm josh ship --log ${issue}`
33
+ return run_ship_stop_text.format(issue, run_ship_stage.stop_reason(stage, resume.conflicts))
33
34
  }
34
35
 
35
36
  function can_relaunch(lane: LaneInfo | undefined): lane is LaneInfo {