@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
@@ -0,0 +1,90 @@
1
+ import type { LaunchOutcome } from '#scripts/lane/lane-launch-cli'
2
+ import type { DrivePorts, DriveState, PassResult } from './backlog-drive'
3
+ import { backlog_drive_seats } from './backlog-drive-seats'
4
+
5
+ // The launches of one offered pass, out of `backlog-drive.ts` so the loop stays within its size.
6
+
7
+ const NO_FREE_LANES = 0
8
+ const LAUNCHED = 'launched'
9
+ const UNOPENED = 'unopened'
10
+ // The launch port's answer for an issue its measurement declaration closed (`backlog-measure-close.ts`).
11
+ const MEASURED_LAUNCH = 'measured' as const
12
+
13
+ type DriveLaunch = LaunchOutcome['kind'] | typeof MEASURED_LAUNCH
14
+
15
+ interface Settled {
16
+ in_flight: Array<string>
17
+ measured: Array<string>
18
+ }
19
+
20
+ function handed_back(issue: string, state: DriveState): PassResult {
21
+ return { kind: 'end', end: { reason: 'launch', token: undefined, issue }, state }
22
+ }
23
+
24
+ // A `lane:open` refused with every lane taken is a wait, not a judgement: the
25
+ // next offer after a lane frees retries it. A step failing after the lane opened is always handed back —
26
+ // that lane holds the last seat itself, so the free count would read the failure as a full pool.
27
+ async function refused(
28
+ issue: string,
29
+ outcome: DriveLaunch,
30
+ state: DriveState,
31
+ ports: DrivePorts,
32
+ ): Promise<PassResult> {
33
+ if (outcome !== UNOPENED) return handed_back(issue, state)
34
+ if ((await ports.free_lanes()) <= NO_FREE_LANES) return { kind: 'continue', state }
35
+
36
+ return handed_back(issue, state)
37
+ }
38
+
39
+ // **Only a launch or a measured close is activity**: a pass that started nothing — every lane taken
40
+ // — keeps `active` where it was, or the once-a-minute offer would restart the idle budget for good. A
41
+ // measured issue is closed but may still read OPEN to `backlog:next`, so it is excluded as a merge is.
42
+ function launched(state: DriveState, settled: Settled, ports: DrivePorts): DriveState {
43
+ const { in_flight, measured } = settled
44
+
45
+ if (in_flight.length === state.in_flight.length && measured.length === 0) {
46
+ return { ...state, in_flight }
47
+ }
48
+
49
+ const exclude = [...state.exclude, ...measured]
50
+
51
+ return { ...state, in_flight, exclude, active: ports.now().toISOString() }
52
+ }
53
+
54
+ // Whether the launch settled the issue — into flight, or closed by its measurement.
55
+ function is_settled(settled: Settled, issue: string, outcome: DriveLaunch): boolean {
56
+ if (outcome === MEASURED_LAUNCH) settled.measured.push(issue)
57
+ if (outcome === LAUNCHED) settled.in_flight.push(issue)
58
+
59
+ return outcome === MEASURED_LAUNCH || outcome === LAUNCHED
60
+ }
61
+
62
+ // **No more new lanes than free lanes**. `backlog:offer` lists every ready issue without reading the
63
+ // lanes, so launching all of them overran `JOSH_LANE_LIMIT` until `lane:open` ran out of seats. An issue
64
+ // that already holds a kept lane reuses its port seat but takes a place under the limit, ahead of a
65
+ // fresh launch; the rest launch up to the free count (`backlog-drive-seats.ts`), read before the first.
66
+ async function launch_all(
67
+ issues: ReadonlyArray<string>,
68
+ state: DriveState,
69
+ ports: DrivePorts,
70
+ ): Promise<PassResult> {
71
+ const settled: Settled = { in_flight: [...state.in_flight], measured: [] }
72
+ const launching = await backlog_drive_seats.seated(issues, ports)
73
+
74
+ for (const issue of launching) {
75
+ // eslint-disable-next-line no-await-in-loop -- lanes launch in order and the first refusal ends the pass
76
+ const outcome = await ports.launch(issue)
77
+
78
+ if (!is_settled(settled, issue, outcome)) {
79
+ // eslint-disable-next-line no-await-in-loop -- lanes launch in order and the first refusal ends the pass
80
+ return await refused(issue, outcome, launched(state, settled, ports), ports)
81
+ }
82
+ }
83
+
84
+ return { kind: 'continue', state: launched(state, settled, ports) }
85
+ }
86
+
87
+ const backlog_drive_launch_pass = { MEASURED_LAUNCH, launch_all }
88
+
89
+ export type { DriveLaunch }
90
+ export { backlog_drive_launch_pass }
@@ -1,7 +1,11 @@
1
1
  import { git_stash } from '#scripts/git/stash/git-stash'
2
- import { lane_launch_cli, type LaunchOutcome } from '#scripts/lane/lane-launch-cli'
2
+ import { lane_launch_cli } from '#scripts/lane/lane-launch-cli'
3
3
  import { run_event_setup } from '#scripts/run/event/run-event-setup'
4
+ import { backlog_drive_launch_pass, type DriveLaunch } from './backlog-drive-launch-pass'
4
5
  import { backlog_drive_owner } from './backlog-drive-owner'
6
+ import { backlog_measure_close } from './backlog-measure-close'
7
+
8
+ const { MEASURED_LAUNCH } = backlog_drive_launch_pass
5
9
 
6
10
  // The driver's lane launch. The parent that ran `josh latest` in the primary
7
11
  // checkout leaves its rewritten lock file in a stash under this message — `backlogrun-lanes.md` →
@@ -27,8 +31,10 @@ async function latest_stash(): Promise<string | undefined> {
27
31
  return selection.kind === 'match' ? LATEST_STASH_MESSAGE : undefined
28
32
  }
29
33
 
30
- async function launch(issue: string, owner: string): Promise<LaunchOutcome['kind']> {
34
+ // An issue its measurement declaration closed takes no lane (`backlog-measure-close.ts`).
35
+ async function launch(issue: string, owner: string): Promise<DriveLaunch> {
31
36
  await backlog_drive_owner.assert_current(owner)
37
+ if (await backlog_measure_close.closed(issue)) return MEASURED_LAUNCH
32
38
  await run_event_setup.emit(run_event_setup.opening_lane(issue))
33
39
 
34
40
  const outcome = await lane_launch_cli.launch_lane({ issue, stash: await latest_stash() })
@@ -1,14 +1,22 @@
1
+ import { auto_ok_cli } from '#scripts/auto-ok/auto-ok-cli'
1
2
  import { issue_cite } from '#scripts/issue/issue-cite'
2
- import { EPIC_LABEL, has_label_name, has_parked_label } from '#scripts/issue/issue-labels'
3
+ import {
4
+ EPIC_LABEL,
5
+ has_label_name,
6
+ has_parked_label,
7
+ parked_label_of,
8
+ } from '#scripts/issue/issue-labels'
3
9
  import type { IssueState } from '#scripts/issue/issue-state'
4
10
  import { issue_state_cli } from '#scripts/issue/issue-state-cli'
11
+ import { lane_plan } from '#scripts/lane/lane-plan'
5
12
  import type { RunCarry } from '#scripts/run/carry/run-carry'
6
- import type { MergeResult } from '#scripts/run/merge/run-merge-cli'
7
13
  import { backlog_budget } from './backlog-budget'
8
14
  import type { DriveState, OfferRead } from './backlog-drive'
9
15
  import { backlog_drive_epic } from './backlog-drive-epic'
10
16
  import { backlog_drive_named } from './backlog-drive-named'
11
17
  import { backlog_drive_unread } from './backlog-drive-unread'
18
+ import type { Declared } from './backlog-overlap'
19
+ import { backlog_plan_comment } from './backlog-plan-comment'
12
20
 
13
21
  interface NamedContext {
14
22
  is_only: boolean
@@ -49,13 +57,11 @@ function budget_offer(
49
57
  }
50
58
  }
51
59
 
52
- // The outcome a named issue has already reached without this run dispatching it: closed is merged, and
53
- // a park (`PARKED_LABELS`) waits on a person, so re-dispatching it only repeats the stop it parked on.
54
- // Either is booked done rather than offered — the same reading `run:merge` gives a parked child.
55
- function settled_outcome(state: IssueState): MergeResult['outcome'] | undefined {
56
- if (state.state.toUpperCase() === 'CLOSED') return 'merged'
57
-
58
- return has_parked_label(state.labels) ? 'parked' : undefined
60
+ // Whether a named issue has already settled without this run dispatching it: closed is merged, and a
61
+ // park (`PARKED_LABELS`) waits on a person, so re-dispatching it only repeats the stop it parked on.
62
+ // Either is booked done rather than offered.
63
+ function is_settled(state: IssueState): boolean {
64
+ return state.state.toUpperCase() === 'CLOSED' || has_parked_label(state.labels)
59
65
  }
60
66
 
61
67
  async function classify(
@@ -68,14 +74,8 @@ async function classify(
68
74
 
69
75
  if (result.kind !== 'state') return backlog_drive_unread.offer(issue, result, state.retries)
70
76
 
71
- const outcome = settled_outcome(result.state)
72
-
73
- if (outcome !== undefined) {
74
- await backlog_drive_named.mark_done(
75
- issue,
76
- { outcome, code: 0, token: 'none', is_offer: true },
77
- owner,
78
- )
77
+ if (is_settled(result.state)) {
78
+ await backlog_drive_named.mark_settled(issue, owner)
79
79
 
80
80
  return { verdict: 'wait', issues: [], retries: NO_RETRIES }
81
81
  }
@@ -88,23 +88,37 @@ async function classify(
88
88
  // **A named issue booked done is not thereby merged**: a park and a failure are
89
89
  // booked done too, so the run moves past them. One the run's own merges did not record and GitHub does
90
90
  // not read as closed ended without a merge — and an unreadable state counts as that, because a false
91
- // stop reaches a person while a false finish ends the run where nobody sees it.
92
- async function is_unmerged(issue: number, merged: ReadonlyArray<number>): Promise<boolean> {
93
- if (merged.includes(issue)) return false
91
+ // stop reaches a person while a false finish ends the run where nobody sees it. The answer is why it
92
+ // did not merge (joshuafolkken/kit#4746): the park it sits on, or that it is open without one.
93
+ async function unmerged_reason(
94
+ issue: number,
95
+ merged: ReadonlyArray<number>,
96
+ ): Promise<string | undefined> {
97
+ if (merged.includes(issue)) return undefined
94
98
 
95
99
  const result = await issue_state_cli.read_issue(String(issue))
96
100
 
97
- return result.kind !== 'state' || result.state.state.toUpperCase() !== 'CLOSED'
101
+ if (result.kind !== 'state') return 'state unreadable'
102
+ if (result.state.state.toUpperCase() === 'CLOSED') return undefined
103
+
104
+ return parked_label_of(result.state.labels) ?? 'open'
98
105
  }
99
106
 
100
- // The issues `run:add` put in count too: one that parked is no more a merge
101
- // than a declared one.
102
- async function unmerged_of(carry: RunCarry): Promise<Array<number>> {
107
+ function cite(issue: number, reason: string | undefined): string | undefined {
108
+ return reason === undefined ? undefined : `${issue_cite.plain(issue)} (${reason})`
109
+ }
110
+
111
+ // Each named issue that did not merge, with its reason. The issues `run:add` put in count too: one that
112
+ // parked is no more a merge than a declared one.
113
+ async function unmerged_of(carry: RunCarry): Promise<Array<string>> {
103
114
  const declared = backlog_drive_named.named_issues(carry)
104
115
  const merged = carry.merged_issues ?? []
105
- const flags = await Promise.all(declared.map(async (issue) => await is_unmerged(issue, merged)))
116
+ const reasons = await Promise.all(
117
+ declared.map(async (issue) => await unmerged_reason(issue, merged)),
118
+ )
119
+ const cited = declared.map((issue, index) => cite(issue, reasons[index]))
106
120
 
107
- return declared.filter((_, index) => flags[index] === true)
121
+ return cited.filter((line) => line !== undefined)
108
122
  }
109
123
 
110
124
  // The `--only` run's end: a finish when every named issue merged, otherwise a stop naming the ones that
@@ -114,9 +128,11 @@ async function only_end(carry: RunCarry, named: OfferRead): Promise<OfferRead> {
114
128
 
115
129
  if (unmerged.length === 0) return named
116
130
 
117
- const listed = unmerged.map((issue) => issue_cite.plain(issue)).join(', ')
118
-
119
- return { ...named, reason: `only: ${listed} ended without a merge`, is_finish: false }
131
+ return {
132
+ ...named,
133
+ reason: `only: ${unmerged.join(', ')} ended without a merge`,
134
+ is_finish: false,
135
+ }
120
136
  }
121
137
 
122
138
  async function not_run(
@@ -126,12 +142,39 @@ async function not_run(
126
142
  return named?.is_finish === true ? await only_end(carry, named) : named
127
143
  }
128
144
 
145
+ // What the lanes in flight declare — recorded plan, else plan comment, else body, the scope `lane:plan` and
146
+ // `backlog:next` read. A lane the listing does not carry, or an unread listing, declares its plan alone.
147
+ async function in_flight_declared(in_flight: ReadonlyArray<string>): Promise<Declared> {
148
+ const listing = await auto_ok_cli.fetch_opted_in()
149
+ const listed = listing.kind === 'read' ? listing.issues : []
150
+ const pool = in_flight
151
+ .map(Number)
152
+ .map((number) => listed.find((issue) => issue.number === number) ?? { number })
153
+
154
+ return await backlog_plan_comment.declared_of(pool, process.cwd())
155
+ }
156
+
157
+ // The issues in flight, with each named one whose yielded plan a running lane still holds: the offer
158
+ // waits on it as on a lane, so the run does not relaunch it into the same yield (joshuafolkken/kit#4926).
159
+ // The listing is read only when a named issue yielded and a lane runs.
160
+ async function holding(carry: RunCarry, in_flight: ReadonlyArray<string>): Promise<Array<string>> {
161
+ const yielded = backlog_drive_named
162
+ .named_issues(carry)
163
+ .filter((issue) => lane_plan.is_yielded(issue, process.cwd()))
164
+ if (yielded.length === 0 || in_flight.length === 0) return [...in_flight]
165
+ const declared = await in_flight_declared(in_flight)
166
+ const held = yielded.filter((issue) => lane_plan.is_yield_held(issue, declared, process.cwd()))
167
+
168
+ return [...new Set([...in_flight, ...held.map(String)])]
169
+ }
170
+
129
171
  async function read(
130
172
  carry: RunCarry,
131
173
  state: DriveState,
132
174
  context: NamedContext,
133
175
  ): Promise<OfferRead | undefined> {
134
- const named = backlog_drive_named.offer(carry, state, context.is_only)
176
+ const waiting = { ...state, in_flight: await holding(carry, state.in_flight) }
177
+ const named = backlog_drive_named.offer(carry, waiting, context.is_only)
135
178
  if (named?.verdict !== 'run') return await not_run(carry, named)
136
179
  const budget = budget_offer(carry, state, context.forwarded)
137
180
  if (budget.verdict !== 'run') return budget
@@ -53,6 +53,15 @@ function held_after(
53
53
  return index > declared.indexOf(next) ? declared[index - 1] : undefined
54
54
  }
55
55
 
56
+ // **An `--only` run ends once nothing it named is left and nothing is in flight**
57
+ // (joshuafolkken/kit#4746): the stop's reason is read when it is offered, so one offered while a lane
58
+ // still ran would name that lane's issue unmerged even after it merged.
59
+ function only_end(state: DriveState): OfferRead {
60
+ return state.in_flight.length > 0
61
+ ? { verdict: 'wait', issues: [], retries: NO_RETRIES }
62
+ : { verdict: 'stop', issues: [], retries: NO_RETRIES, reason: 'only', is_finish: true }
63
+ }
64
+
56
65
  function offer(carry: RunCarry, state: DriveState, is_only: boolean): OfferRead | undefined {
57
66
  const next = next_of(carry, state.in_flight)
58
67
 
@@ -64,9 +73,7 @@ function offer(carry: RunCarry, state: DriveState, is_only: boolean): OfferRead
64
73
  : { verdict: 'run', issues: [issue], retries: NO_RETRIES }
65
74
  }
66
75
 
67
- return is_only
68
- ? { verdict: 'stop', issues: [], retries: NO_RETRIES, reason: 'only', is_finish: true }
69
- : undefined
76
+ return is_only ? only_end(state) : undefined
70
77
  }
71
78
 
72
79
  function is_done(result: MergeResult): boolean {
@@ -121,15 +128,28 @@ async function should_skip(issue: string, outcome: MergeResult['outcome']): Prom
121
128
  return await lane_parked.is_parked(issue)
122
129
  }
123
130
 
131
+ async function carry_target(): Promise<string | undefined> {
132
+ const directory = await run_carry.repository_directory()
133
+
134
+ return directory === undefined ? undefined : run_carry.carry_path(directory)
135
+ }
136
+
124
137
  async function mark_done(issue: string, result: MergeResult, owner: string): Promise<void> {
125
138
  if (!is_done(result)) return
126
- const directory = await run_carry.repository_directory()
127
- if (directory === undefined) return
128
- const target = run_carry.carry_path(directory)
129
- if (!mark_one(target, issue, owner)) return
139
+ const target = await carry_target()
140
+ if (target === undefined || !mark_one(target, issue, owner)) return
130
141
  if (await should_skip(issue, result.outcome)) skip_after_failure(target, issue, owner)
131
142
  }
132
143
 
144
+ // **A named issue settled before this run dispatched it skips nothing** (joshuafolkken/kit#4746): it
145
+ // did not fail inside the run, so booking the ones declared after it done would end an `--only` run
146
+ // over issues no lane ever started.
147
+ async function mark_settled(issue: string, owner: string): Promise<void> {
148
+ const target = await carry_target()
149
+
150
+ if (target !== undefined) mark_one(target, issue, owner)
151
+ }
152
+
133
153
  async function reconcile(
134
154
  carry: RunCarry,
135
155
  events: ReadonlyArray<RunEvent>,
@@ -173,6 +193,7 @@ export const backlog_drive_named = {
173
193
  offer,
174
194
  held_after,
175
195
  mark_done,
196
+ mark_settled,
176
197
  named_issues,
177
198
  own_carried,
178
199
  reconcile,
@@ -11,6 +11,7 @@ import { DEFAULT_SILENT_MINUTES } from '#scripts/run/run-liveness'
11
11
  import type { DrivePorts, OfferRead, PassResult } from './backlog-drive'
12
12
  import { backlog_drive_idle } from './backlog-drive-idle'
13
13
  import { backlog_overlap } from './backlog-overlap'
14
+ import { backlog_plan_comment } from './backlog-plan-comment'
14
15
 
15
16
  // A run held only by paused lanes (joshuafolkken/kit#4270): nothing of this run in flight, nothing
16
17
  // dispatched, and a lane a person paused past the silent window `run:liveness` waits out declares a
@@ -85,12 +86,12 @@ function waiting_of(
85
86
  }
86
87
 
87
88
  /** The `paused` records whose declared paths a waiting candidate overlaps. */
88
- function holding(
89
+ async function holding(
89
90
  paused: ReadonlyArray<LaneStateRecord>,
90
91
  issues: ReadonlyArray<OpenIssueData>,
91
92
  records: ReadonlyArray<LaneStateRecord>,
92
- ): Array<LaneStateRecord> {
93
- const declared = backlog_overlap.declared_of(issues)
93
+ ): Promise<Array<LaneStateRecord>> {
94
+ const declared = await backlog_plan_comment.declared_of(issues, process.cwd())
94
95
  const waiting = waiting_of(issues, records)
95
96
 
96
97
  return paused.filter((record) =>
@@ -106,7 +107,7 @@ async function held_by(ports: PausedPorts): Promise<Array<LaneStateRecord>> {
106
107
 
107
108
  const issues = await ports.read_issues()
108
109
 
109
- return issues === undefined ? [] : holding(paused, issues, records)
110
+ return issues === undefined ? [] : await holding(paused, issues, records)
110
111
  }
111
112
 
112
113
  function describe(records: ReadonlyArray<LaneStateRecord>): string {
@@ -1,3 +1,4 @@
1
+ import { lane_yield } from '#scripts/lane/lane-yield'
1
2
  import { run_event_scope } from '#scripts/run/event/run-event-scope'
2
3
  import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
3
4
  import { backlog_offer_exclude } from './backlog-offer-exclude'
@@ -9,6 +10,7 @@ const SETTLED_KINDS: ReadonlySet<string> = new Set([
9
10
  run_event_stream.EVENT_KIND.SPLIT,
10
11
  run_event_stream.EVENT_KIND.WITHDRAW,
11
12
  run_event_stream.EVENT_KIND.OUTAGE,
13
+ run_event_stream.EVENT_KIND.USAGE_LIMIT,
12
14
  ])
13
15
  const DONE_KINDS: ReadonlySet<string> = new Set([
14
16
  run_event_stream.EVENT_KIND.MERGE,
@@ -37,8 +39,11 @@ function active_issues(events: ReadonlyArray<RunEvent>): ReadonlySet<string> {
37
39
  return active
38
40
  }
39
41
 
42
+ // A yield is not among them: it gave its lane back without settling the issue (`lane-yield.ts`).
40
43
  function settled_issues(events: ReadonlyArray<RunEvent>): ReadonlySet<number> {
41
- const settled = events.filter((event) => DONE_KINDS.has(event.kind))
44
+ const settled = events.filter(
45
+ (event) => DONE_KINDS.has(event.kind) && !lane_yield.is_yield(event),
46
+ )
42
47
 
43
48
  return new Set(
44
49
  settled
@@ -56,11 +61,14 @@ function issues_of_kind(events: ReadonlyArray<RunEvent>, kind: string): Readonly
56
61
  }
57
62
 
58
63
  // The issues a person withdrew from this run and nobody launched again: the withdrawal leaves the issue
59
- // open for the next run, so this one keeps it out of the offer (joshuafolkken/kit#4365).
64
+ // open for the next run, so this one keeps it out of the offer (joshuafolkken/kit#4365). A lane that
65
+ // yielded stays in the run: its retained plan holds it back until the holder ends (`lane-yield.ts`).
60
66
  function withdrawn_issues(events: ReadonlyArray<RunEvent>): ReadonlyArray<string> {
61
67
  const named = issues_of_kind(events, run_event_stream.EVENT_KIND.WITHDRAW)
62
68
 
63
- return named.filter((issue) => run_event_scope.is_withdrawn(events, issue))
69
+ return named.filter(
70
+ (issue) => run_event_scope.is_withdrawn(events, issue) && !lane_yield.is_yielded(events, issue),
71
+ )
64
72
  }
65
73
 
66
74
  // The offer's exclusion as the stream records it: the merged and withdrawn children and the ones still
@@ -17,16 +17,15 @@
17
17
  // without a process, a network or a clock.
18
18
 
19
19
  import { issue_cite } from '#scripts/issue/issue-cite'
20
- import type { LaunchOutcome } from '#scripts/lane/lane-launch-cli'
21
20
  import type { MergeResult } from '#scripts/run/merge/run-merge-cli'
22
21
  import { run_merge_token } from '#scripts/run/merge/run-merge-token'
23
22
  import { backlog_drive_adopt } from './backlog-drive-adopt'
24
23
  import { backlog_drive_hold, type HeldWaiting } from './backlog-drive-hold'
25
24
  import { backlog_drive_idle } from './backlog-drive-idle'
26
25
  import { backlog_drive_judge } from './backlog-drive-judge'
26
+ import { backlog_drive_launch_pass, type DriveLaunch } from './backlog-drive-launch-pass'
27
27
  import { backlog_drive_memory } from './backlog-drive-memory'
28
28
  import { backlog_drive_paused } from './backlog-drive-paused'
29
- import { backlog_drive_seats } from './backlog-drive-seats'
30
29
  import { backlog_drive_token_handling } from './backlog-drive-token-handling'
31
30
 
32
31
  const { MERGE_TOKEN } = run_merge_token
@@ -44,11 +43,11 @@ const KNOWN_VERDICTS: ReadonlySet<string> = new Set([
44
43
  'wait',
45
44
  ])
46
45
  const NO_RETRIES = 0
47
- const NO_FREE_LANES = 0
48
46
  const EXHAUSTED_ANSWER = 'exhausted'
49
47
  const JUDGE_REASON = 'judge'
50
- const LAUNCHED = 'launched'
51
- const UNOPENED = 'unopened'
48
+ // The `hold` reason a pass held by a Claude usage limit records.
49
+ const USAGE_LIMIT_REASON = 'usage-limit'
50
+ const { launch_all } = backlog_drive_launch_pass
52
51
 
53
52
  interface OfferRead extends HeldWaiting {
54
53
  verdict: string
@@ -106,8 +105,9 @@ interface DrivePorts {
106
105
  adoptable: (in_flight: ReadonlyArray<string>) => Promise<ReadonlyArray<string>>
107
106
  // `backlog:offer`'s answer for the state, or `undefined` when it exited non-zero.
108
107
  offer: (state: DriveState) => Promise<OfferRead | undefined>
109
- // Where `lane:launch`'s chain ended — `launched`, `unopened` (no lane taken) or `failed` (one taken).
110
- launch: (issue: string) => Promise<LaunchOutcome['kind']>
108
+ // Where `lane:launch`'s chain ended — `launched`, `unopened` (no lane taken) or `failed` (one taken) —
109
+ // or `measured`, the issue closed by its declaration with no lane taken.
110
+ launch: (issue: string) => Promise<DriveLaunch>
111
111
  // The lanes free right now — `backlog_ready.drive_free_lane_count` in production, the limit less the
112
112
  // open lanes.
113
113
  free_lanes: () => Promise<number>
@@ -122,6 +122,9 @@ interface DrivePorts {
122
122
  // Whether the machine has no memory for one more lane — `backlog_drive_memory.gauge()` in production,
123
123
  // which carries its hysteresis between calls.
124
124
  memory_held: () => Promise<boolean>
125
+ // Whether a Claude usage limit that stopped a child has yet to reset — `run_merge_usage_limit.is_held`
126
+ // in production (joshuafolkken/kit#4745).
127
+ usage_limited: () => Promise<boolean>
125
128
  // Why an offered pass started nothing (`backlog-drive-hold.ts`). Production records it in the lane
126
129
  // ledger only while a lane is free. `undefined` is the run's stop, which owes no lane a child.
127
130
  hold: (reason: string | undefined, waiting?: ReadonlyArray<number>) => Promise<void>
@@ -250,58 +253,6 @@ async function collect_finished(state: DriveState, ports: DrivePorts): Promise<P
250
253
  return { kind: 'continue', state: current }
251
254
  }
252
255
 
253
- // A `lane:open` refused with every lane taken is a wait, not a judgement: the
254
- // next offer after a lane frees retries it. A step failing after the lane opened is always handed back —
255
- // that lane holds the last seat itself, so the free count would read the failure as a full pool.
256
- async function refused(
257
- issue: string,
258
- outcome: LaunchOutcome['kind'],
259
- state: DriveState,
260
- ports: DrivePorts,
261
- ): Promise<PassResult> {
262
- if (outcome !== UNOPENED) return ended('launch', state, undefined, issue)
263
- if ((await ports.free_lanes()) <= NO_FREE_LANES) return { kind: 'continue', state }
264
-
265
- return ended('launch', state, undefined, issue)
266
- }
267
-
268
- // **No more new lanes than free lanes**. `backlog:offer` lists every ready issue without reading the
269
- // lanes, so launching all of them overran `JOSH_LANE_LIMIT` until `lane:open` ran out of seats. An issue
270
- // that already holds a kept lane reuses its port seat but takes a place under the limit, ahead of a
271
- // fresh launch; the rest launch up to the free count (`backlog-drive-seats.ts`), read before the first.
272
- //
273
- // **Only a launch is activity**: a pass that started nothing — every lane taken
274
- // — keeps `active` where it was, or the once-a-minute offer would restart the idle budget for good.
275
- function launched(state: DriveState, in_flight: Array<string>, ports: DrivePorts): DriveState {
276
- if (in_flight.length === state.in_flight.length) return { ...state, in_flight }
277
-
278
- return { ...state, in_flight, active: ports.now().toISOString() }
279
- }
280
-
281
- async function launch_all(
282
- issues: ReadonlyArray<string>,
283
- state: DriveState,
284
- ports: DrivePorts,
285
- ): Promise<PassResult> {
286
- const in_flight = [...state.in_flight]
287
-
288
- const launching = await backlog_drive_seats.seated(issues, ports)
289
-
290
- for (const issue of launching) {
291
- // eslint-disable-next-line no-await-in-loop -- lanes launch in order and the first refusal ends the pass
292
- const outcome = await ports.launch(issue)
293
-
294
- if (outcome !== LAUNCHED) {
295
- // eslint-disable-next-line no-await-in-loop -- lanes launch in order and the first refusal ends the pass
296
- return await refused(issue, outcome, launched(state, in_flight, ports), ports)
297
- }
298
-
299
- in_flight.push(issue)
300
- }
301
-
302
- return { kind: 'continue', state: launched(state, in_flight, ports) }
303
- }
304
-
305
256
  // A watch with nothing in flight is the drain: `run:step` owes the retrospective there, so it is the
306
257
  // parent's. With children still running the watch is only waiting on them.
307
258
  function on_verdict(read: OfferRead, state: DriveState): PassResult {
@@ -328,12 +279,21 @@ function on_verdict(read: OfferRead, state: DriveState): PassResult {
328
279
  return { kind: 'continue', state }
329
280
  }
330
281
 
331
- // An offer answered `run`: launched while the machine has memory to spare, held until it does otherwise.
282
+ async function usage_held(state: DriveState, ports: DrivePorts): Promise<PassResult> {
283
+ await ports.hold(USAGE_LIMIT_REASON)
284
+
285
+ return { kind: 'continue', state }
286
+ }
287
+
288
+ // An offer answered `run`: launched while the machine has memory to spare and no usage limit is in
289
+ // force, held until both hold otherwise. A usage-limited pass polls on as a held one does.
332
290
  async function run_offered(
333
291
  issues: ReadonlyArray<string>,
334
292
  state: DriveState,
335
293
  ports: DrivePorts,
336
294
  ): Promise<PassResult> {
295
+ if (await ports.usage_limited()) return await usage_held(state, ports)
296
+
337
297
  if (await ports.memory_held()) return await backlog_drive_memory.held(state, ports)
338
298
 
339
299
  return await launch_all(issues, state, ports)
@@ -464,6 +424,7 @@ async function run_loop(
464
424
  const backlog_drive = {
465
425
  HANDOFF_TOKENS,
466
426
  TOKEN_HANDLING,
427
+ USAGE_LIMIT_REASON,
467
428
  initial_state,
468
429
  merge_stopped,
469
430
  run_loop,
@@ -0,0 +1,61 @@
1
+ import type { OptedInRead, TrackingRead } from '#scripts/auto-ok/auto-ok-cli'
2
+ import { epic_graph } from '#scripts/epic/epic-graph'
3
+ import { epic_index } from '#scripts/epic/epic-index'
4
+ import { lane_plan } from '#scripts/lane/lane-plan'
5
+ import { lane_state } from '#scripts/lane/lane-state'
6
+ import { backlog_plan_comment } from './backlog-plan-comment'
7
+ import { backlog_pool } from './backlog-pool'
8
+ import type { GateScope } from './backlog-rank'
9
+
10
+ type OptedIn = Extract<OptedInRead, { kind: 'read' }>
11
+ type Tracking = Extract<TrackingRead, { kind: 'read' }>
12
+
13
+ // Everything the answer is assembled from, read once and passed down rather than re-fetched.
14
+ interface PoolContext {
15
+ opted_in: OptedIn
16
+ tracking: Tracking
17
+ repo: string
18
+ exclude: ReadonlyArray<number>
19
+ }
20
+
21
+ // The rows the offer's cap bounds — the standalone half.
22
+ function standalone_keys(context: PoolContext): ReadonlySet<string> {
23
+ return backlog_pool.standalone_keys(context.opted_in.issues, {
24
+ tracked: epic_index.withheld_children(context.tracking.index, context.opted_in.issues),
25
+ exclude: context.exclude,
26
+ repo: context.repo,
27
+ })
28
+ }
29
+
30
+ // The issues whose lane yielded on a plan it kept (`lane-yield.ts`) — offered first (`backlog-rank.ts`,
31
+ // joshuafolkken/kit#5107).
32
+ function yielded_keys(context: PoolContext, directory: string): ReadonlySet<string> {
33
+ const yielded = context.opted_in.issues.filter((issue) =>
34
+ lane_plan.is_yielded(issue.number, directory),
35
+ )
36
+
37
+ return new Set(yielded.map(({ number }) => epic_graph.key_of({ number, repo: context.repo })))
38
+ }
39
+
40
+ // The running lanes still carry the pickup label, so the opted-in listing holds their bodies too —
41
+ // which is what lets a candidate be held back from a path a running lane already declares.
42
+ // `backlog:plan --waves` reads the same scope, so its first wave is this offer. Their lane state
43
+ // records name a paused holder in the wait it causes (joshuafolkken/kit#4270); they are local files.
44
+ // A lane's recorded plan (joshuafolkken/kit#4864), else a plan comment (joshuafolkken/kit#4923), is
45
+ // read in place of the body (joshuafolkken/kit#4937).
46
+ async function gate_scope(context: PoolContext): Promise<GateScope> {
47
+ const { issues } = context.opted_in
48
+ const directory = process.cwd()
49
+
50
+ return {
51
+ standalone: standalone_keys(context),
52
+ declared: await backlog_plan_comment.declared_of(issues, directory),
53
+ lanes: await lane_state.read_states(issues.map((issue) => String(issue.number))),
54
+ yielded: yielded_keys(context, directory),
55
+ }
56
+ }
57
+
58
+ const backlog_gate_scope = { gate_scope }
59
+
60
+ export { backlog_gate_scope }
61
+ export type { OptedIn, PoolContext }