@joshuafolkken/kit 1.1784.0 → 1.2114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/.claude/agents/reviewer-verification.md +19 -0
  2. package/.claude/agents/reviewer.md +19 -0
  3. package/.claude/settings.json +5 -6
  4. package/.claude/skills/dependency-update/SKILL.md +9 -10
  5. package/.claude/skills/workflow-commands/SKILL.md +6 -6
  6. package/.claude/skills/workflow-commands/background-commands.md +8 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
  13. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  14. package/.claude/skills/workflow-commands/chain-rule.md +41 -32
  15. package/.claude/skills/workflow-commands/delegation.md +6 -6
  16. package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
  17. package/.claude/skills/workflow-commands/followup.md +7 -5
  18. package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
  19. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  20. package/.claude/skills/workflow-commands/issue-comments.md +9 -4
  21. package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +2 -2
  23. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  24. package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
  25. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
  26. package/.claude/skills/workflow-commands/prerequisite.md +3 -2
  27. package/.claude/skills/workflow-commands/retrospective.md +2 -2
  28. package/.claude/skills/workflow-commands/split-assessment.md +7 -5
  29. package/.codex/hooks.json +5 -5
  30. package/.github/actions/setup-pnpm/action.yml +9 -16
  31. package/.github/dependabot.yml +16 -0
  32. package/.github/workflows/auto-tag.yml +45 -0
  33. package/.github/workflows/ci.yml +155 -15
  34. package/.github/workflows/dependabot-auto-merge.yml +63 -4
  35. package/.github/workflows/production.yml +8 -0
  36. package/.github/workflows/publish.yml +59 -45
  37. package/.vscode/settings.json +19 -0
  38. package/CLAUDE.md +5 -12
  39. package/SECURITY.md +2 -2
  40. package/dist/CLAUDE.md +5 -12
  41. package/dist/commands/document-read-cli.js +2 -1
  42. package/dist/commands/document-section-main.js +2 -1
  43. package/dist/commands/read-set-cli.js +194 -134
  44. package/dist/commands/run-prep-main.js +9694 -10173
  45. package/dist/commands/run-step-cli.js +22138 -18285
  46. package/dist/hooks/audit-provision.js +23 -5
  47. package/dist/hooks/chunk-3GKE653K.js +6204 -0
  48. package/dist/hooks/chunk-45CJJOO7.js +1243 -0
  49. package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
  50. package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
  51. package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
  52. package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
  53. package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
  54. package/dist/hooks/chunk-VVNYJWEI.js +30 -0
  55. package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
  56. package/dist/hooks/codex-hook-adapter.js +8 -8
  57. package/dist/hooks/format-edited.js +4 -5
  58. package/dist/hooks/inputs.json +415 -389
  59. package/dist/hooks/pretool-guard.js +7 -6
  60. package/dist/hooks/session-lang.js +101 -258
  61. package/dist/hooks/stop-guard.js +1744 -2576
  62. package/dist/josh.js +7133 -6368
  63. package/dist/version/index.js +161 -1
  64. package/eslint/rules/comment-why-only.js +108 -0
  65. package/package.json +15 -8
  66. package/prompts/coding-standards.md +1 -1
  67. package/prompts/collaboration-workflow/file-edits.md +14 -11
  68. package/prompts/collaboration-workflow/issue-citation.md +0 -4
  69. package/prompts/collaboration-workflow/issue-template.md +40 -21
  70. package/prompts/collaboration-workflow/lane-instructions.md +3 -1
  71. package/prompts/collaboration-workflow/measurement.md +8 -1
  72. package/prompts/collaboration-workflow/operating-rules.md +11 -2
  73. package/prompts/collaboration-workflow/output-bounds.md +6 -2
  74. package/prompts/collaboration-workflow/plan-comment.md +1 -1
  75. package/prompts/collaboration-workflow/principles.md +9 -0
  76. package/prompts/collaboration-workflow/residency.md +3 -2
  77. package/prompts/collaboration-workflow/rule-delivery.md +13 -1
  78. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  79. package/prompts/collaboration-workflow/wip-cap.md +3 -1
  80. package/prompts/review-rubric.md +8 -0
  81. package/prompts/review.md +31 -22
  82. package/prompts/testing-guide.md +1 -0
  83. package/scripts/agent/agent-argv.ts +14 -16
  84. package/scripts/agent/agent-provider.ts +138 -0
  85. package/scripts/agent/agent-role-profile.ts +108 -156
  86. package/scripts/agent/agent-safe-text.ts +20 -5
  87. package/scripts/agent/api-usage-limit.ts +65 -0
  88. package/scripts/agent/claude-agent-argv.ts +19 -9
  89. package/scripts/agent/codex-agent-argv.ts +118 -22
  90. package/scripts/agent/usage-limit-reset.ts +179 -0
  91. package/scripts/auth/claude-token-output.ts +2 -2
  92. package/scripts/auto-ok/auto-ok-author.ts +53 -0
  93. package/scripts/auto-ok/auto-ok-cli.ts +20 -9
  94. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  95. package/scripts/backlog/backlog-drive-cli.ts +6 -1
  96. package/scripts/backlog/backlog-drive-hold.ts +3 -3
  97. package/scripts/backlog/backlog-drive-idle.ts +10 -0
  98. package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
  99. package/scripts/backlog/backlog-drive-launch.ts +8 -2
  100. package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
  101. package/scripts/backlog/backlog-drive-named.ts +28 -7
  102. package/scripts/backlog/backlog-drive-paused.ts +5 -4
  103. package/scripts/backlog/backlog-drive-restore.ts +11 -3
  104. package/scripts/backlog/backlog-drive.ts +21 -60
  105. package/scripts/backlog/backlog-gate-scope.ts +61 -0
  106. package/scripts/backlog/backlog-measure-close.ts +112 -0
  107. package/scripts/backlog/backlog-next.ts +39 -49
  108. package/scripts/backlog/backlog-occupied.ts +2 -2
  109. package/scripts/backlog/backlog-overlap.ts +42 -8
  110. package/scripts/backlog/backlog-plan-cli.ts +4 -1
  111. package/scripts/backlog/backlog-plan-comment.ts +81 -0
  112. package/scripts/backlog/backlog-plan-read.ts +19 -6
  113. package/scripts/backlog/backlog-pool.ts +50 -12
  114. package/scripts/backlog/backlog-rank.ts +60 -13
  115. package/scripts/backlog/backlog-stalled-detect.ts +2 -1
  116. package/scripts/backlog/backlog-waves.ts +17 -2
  117. package/scripts/backlog/backlogrun-launch.ts +7 -4
  118. package/scripts/cost-runtime/context-cut-payback.ts +27 -25
  119. package/scripts/cost-runtime/cost-cli.ts +37 -16
  120. package/scripts/cost-runtime/cost-pricing.ts +9 -0
  121. package/scripts/cost-runtime/cost-verdict.ts +4 -1
  122. package/scripts/delegation/delegation-policy.ts +2 -2
  123. package/scripts/delegation/duplicate-read-guard.ts +1 -0
  124. package/scripts/delegation/investigation-guard.ts +14 -2
  125. package/scripts/delegation/investigation-reads.ts +45 -23
  126. package/scripts/dependabot/dependabot-cooldown.ts +3 -2
  127. package/scripts/discovery/repo-party.ts +80 -20
  128. package/scripts/discovery/session-owner-pin.ts +51 -0
  129. package/scripts/document/capped-print.ts +1 -1
  130. package/scripts/document/command-reference-line-budget.ts +42 -0
  131. package/scripts/document/document-byte-budget.ts +17 -7
  132. package/scripts/document/document-checks.ts +20 -0
  133. package/scripts/document/document-ratchet-check.ts +101 -0
  134. package/scripts/document/document-section.ts +1 -0
  135. package/scripts/document/entry-read-budget.ts +7 -3
  136. package/scripts/document/issue-citation-budget.ts +94 -0
  137. package/scripts/epic/epic-cli-argv.ts +2 -2
  138. package/scripts/epic/epic-fetch.ts +4 -2
  139. package/scripts/epic/epic-graph.ts +7 -2
  140. package/scripts/epic/epic-issue.ts +3 -1
  141. package/scripts/epic/epic-rank.ts +5 -0
  142. package/scripts/exports/unused-members-check.ts +62 -0
  143. package/scripts/exports/unused-members-cli.ts +6 -51
  144. package/scripts/followup/followup-ci-path.ts +67 -0
  145. package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
  146. package/scripts/followup/followup-coderabbit-wait.ts +13 -10
  147. package/scripts/followup/followup-failed-tests.ts +160 -0
  148. package/scripts/followup/followup-job-needs.ts +100 -0
  149. package/scripts/followup/followup-job-rerun.ts +303 -0
  150. package/scripts/followup/followup-main-move.ts +113 -0
  151. package/scripts/followup/git-followup-finish.ts +43 -11
  152. package/scripts/followup/git-followup-pending.ts +3 -3
  153. package/scripts/followup/git-followup-stages.ts +41 -11
  154. package/scripts/followup/git-pr-followup-checks.ts +55 -16
  155. package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
  156. package/scripts/followup/git-pr-followup.ts +62 -27
  157. package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
  158. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
  159. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
  160. package/scripts/gate/busy-average.ts +34 -2
  161. package/scripts/gate/check-related-pinned.ts +43 -0
  162. package/scripts/gate/check-related-reuse.ts +64 -0
  163. package/scripts/gate/check-related-section.ts +57 -0
  164. package/scripts/gate/check-related-unplanned.ts +34 -0
  165. package/scripts/gate/check-related.ts +265 -0
  166. package/scripts/gate/command-memory.ts +1 -1
  167. package/scripts/gate/core-admission.ts +24 -6
  168. package/scripts/gate/core-budget-timing.ts +20 -6
  169. package/scripts/gate/core-budget.ts +40 -11
  170. package/scripts/gate/core-wait-record.ts +3 -2
  171. package/scripts/gate/core-wait.ts +25 -2
  172. package/scripts/gate/core-weights.ts +15 -7
  173. package/scripts/gate/gate-labels.ts +46 -0
  174. package/scripts/gate/gate-ledger.ts +14 -6
  175. package/scripts/gate/gate-plan.ts +103 -58
  176. package/scripts/gate/gate-report.ts +9 -7
  177. package/scripts/gate/gate-run-steps.ts +26 -3
  178. package/scripts/gate/gate-skip.ts +45 -6
  179. package/scripts/gate/gate-static-group.ts +60 -0
  180. package/scripts/gate/gate-steps.ts +2 -1
  181. package/scripts/gate/gate-tree.ts +4 -2
  182. package/scripts/gate/hook-gate-reuse.ts +19 -2
  183. package/scripts/gate/metrics-totals.ts +59 -0
  184. package/scripts/gate/scoped-green.ts +68 -15
  185. package/scripts/gate/since-green.ts +14 -6
  186. package/scripts/gate/structural-checks.ts +96 -0
  187. package/scripts/gate/verification-gate.ts +51 -16
  188. package/scripts/gate/verification-inputs.ts +21 -6
  189. package/scripts/gate/verification-tail.ts +52 -0
  190. package/scripts/gh/gh-author-association.ts +23 -2
  191. package/scripts/gh/git-ai-review-scan.ts +1 -1
  192. package/scripts/gh/git-gh-api-path.ts +12 -3
  193. package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
  194. package/scripts/gh/git-gh-exec.ts +32 -2
  195. package/scripts/gh/git-gh-issue-read.ts +4 -2
  196. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  197. package/scripts/gh/git-gh-issue-write.ts +8 -2
  198. package/scripts/gh/git-gh-issue.ts +12 -5
  199. package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
  200. package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
  201. package/scripts/gh/git-gh-pr-read.ts +74 -13
  202. package/scripts/gh/git-gh-pr-rest.ts +3 -1
  203. package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
  204. package/scripts/gh/git-gh-pr-too-large.ts +20 -0
  205. package/scripts/gh/git-gh-pr.ts +21 -7
  206. package/scripts/gh/git-gh-retry.ts +141 -0
  207. package/scripts/gh/git-pr-ai-review.ts +15 -6
  208. package/scripts/gh/git-pr-checks-eval.ts +3 -0
  209. package/scripts/gh/git-pr-checks.ts +5 -5
  210. package/scripts/git/base-tree.ts +30 -0
  211. package/scripts/git/change-base.ts +4 -10
  212. package/scripts/git/changed-paths-sync.ts +63 -0
  213. package/scripts/git/changed-paths.ts +16 -1
  214. package/scripts/git/git-common-directory.ts +11 -1
  215. package/scripts/git/git-diff-reads.ts +66 -20
  216. package/scripts/git/git-error.ts +9 -2
  217. package/scripts/git/git-schemas.ts +13 -0
  218. package/scripts/git/git-worktree.ts +13 -0
  219. package/scripts/git/gone-branch.ts +4 -1
  220. package/scripts/git/main-merge-budget-conflict.ts +89 -0
  221. package/scripts/git/main-merge-guard.ts +55 -18
  222. package/scripts/git/main-merge-regenerate.ts +59 -25
  223. package/scripts/git/main-merge.ts +53 -5
  224. package/scripts/git/no-base-checkout.ts +127 -0
  225. package/scripts/git/stash/git-stash.ts +11 -2
  226. package/scripts/git/stash/stash-push-cli.ts +10 -0
  227. package/scripts/hooks/batch-guard.ts +40 -21
  228. package/scripts/hooks/edited-cspell.ts +37 -11
  229. package/scripts/hooks/format-edited-file.ts +22 -2
  230. package/scripts/hooks/lane-plan-guard.ts +50 -0
  231. package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
  232. package/scripts/hooks/pre-commit-lint.ts +14 -1
  233. package/scripts/hooks/pre-push-related-switch.ts +10 -0
  234. package/scripts/hooks/pre-push-unit.ts +76 -10
  235. package/scripts/hooks/pretool-guard.ts +2 -0
  236. package/scripts/hooks/reserved-run.ts +14 -1
  237. package/scripts/hooks/run-hook.sh +23 -8
  238. package/scripts/hooks/step-zero-notice.ts +6 -1
  239. package/scripts/hooks/stop-actions.ts +3 -15
  240. package/scripts/hooks/stop-guard.ts +7 -6
  241. package/scripts/init/distributed-paths.ts +1 -0
  242. package/scripts/init/hook-command-rewrite.ts +5 -1
  243. package/scripts/init/hook-launch.ts +4 -2
  244. package/scripts/issue/aggregation-split.ts +52 -0
  245. package/scripts/issue/defect-origin-line.ts +19 -1
  246. package/scripts/issue/defect-rate-cli.ts +5 -102
  247. package/scripts/issue/defect-report.ts +4 -4
  248. package/scripts/issue/effect-audit-cli.ts +178 -0
  249. package/scripts/issue/effect-audit.ts +185 -0
  250. package/scripts/issue/expected-effect-lint.ts +64 -9
  251. package/scripts/issue/git-next-issues.ts +5 -0
  252. package/scripts/issue/issue-body-or-comment.ts +38 -4
  253. package/scripts/issue/issue-cite-cli.ts +1 -3
  254. package/scripts/issue/issue-classification.ts +1 -0
  255. package/scripts/issue/issue-closing-pr.ts +8 -1
  256. package/scripts/issue/issue-evidence-exit.ts +4 -2
  257. package/scripts/issue/issue-file-cap.ts +58 -0
  258. package/scripts/issue/issue-file-cli.ts +111 -38
  259. package/scripts/issue/issue-file-deferral.ts +52 -0
  260. package/scripts/issue/issue-file-fold.ts +29 -10
  261. package/scripts/issue/issue-file-interrupt.ts +85 -0
  262. package/scripts/issue/issue-file-lane-hint.ts +30 -0
  263. package/scripts/issue/issue-file.ts +43 -6
  264. package/scripts/issue/issue-labels.ts +19 -2
  265. package/scripts/issue/issue-lint-cli.ts +27 -2
  266. package/scripts/issue/issue-measured-cli.ts +13 -35
  267. package/scripts/issue/issue-measured-close.ts +58 -0
  268. package/scripts/issue/issue-not-before-label.ts +2 -2
  269. package/scripts/issue/issue-not-before.ts +4 -1
  270. package/scripts/issue/issue-priority-cli.ts +60 -0
  271. package/scripts/issue/issue-priority-raise.ts +55 -0
  272. package/scripts/issue/issue-priority.ts +174 -43
  273. package/scripts/issue/issue-rank.ts +21 -4
  274. package/scripts/issue/issue-read-cli.ts +32 -6
  275. package/scripts/issue/issue-read.ts +37 -12
  276. package/scripts/issue/issue-sample-gate.ts +95 -74
  277. package/scripts/issue/issue-sample-prime.ts +127 -0
  278. package/scripts/issue/issue-sample-required.ts +57 -0
  279. package/scripts/issue/issue-sample-source.ts +12 -11
  280. package/scripts/issue/issue-search.ts +124 -0
  281. package/scripts/issue/issue-state.ts +13 -1
  282. package/scripts/issue/issue-template-section.ts +43 -0
  283. package/scripts/issue/issue-watch-diff.ts +31 -1
  284. package/scripts/issue/issue-watch-hook.ts +11 -2
  285. package/scripts/issue/issue-watch.ts +55 -12
  286. package/scripts/issue/markdown-section.ts +1 -0
  287. package/scripts/issue/measure-declaration.ts +121 -0
  288. package/scripts/issue/measure-rerun-cli.ts +7 -70
  289. package/scripts/issue/plan-consumers-section.ts +22 -1
  290. package/scripts/issue/reproduction-measure.ts +3 -1
  291. package/scripts/issue/size-estimate.ts +90 -0
  292. package/scripts/issue/trusted-measure.ts +84 -0
  293. package/scripts/josh/command-usage.ts +3 -2
  294. package/scripts/josh/durable-state-migrate.ts +105 -0
  295. package/scripts/josh/durable-state.ts +112 -0
  296. package/scripts/josh/josh-aliases.ts +95 -0
  297. package/scripts/josh/josh-command-map.ts +1 -82
  298. package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
  299. package/scripts/josh/josh-commands-ai.ts +5 -5
  300. package/scripts/josh/josh-commands-development.ts +59 -58
  301. package/scripts/josh/josh-commands-issue-write.ts +23 -4
  302. package/scripts/josh/josh-commands-issue.ts +32 -0
  303. package/scripts/josh/josh-commands-lane.ts +87 -4
  304. package/scripts/josh/josh-commands-lint.ts +16 -3
  305. package/scripts/josh/josh-commands-maintenance.ts +15 -0
  306. package/scripts/josh/josh-commands-review.ts +41 -6
  307. package/scripts/josh/josh-commands-ship.ts +56 -8
  308. package/scripts/josh/josh-commands-test-check.ts +80 -0
  309. package/scripts/josh/josh-commands-workflow.ts +1 -1
  310. package/scripts/josh/josh-environment-file.ts +43 -3
  311. package/scripts/josh/josh-in-process.ts +2 -2
  312. package/scripts/josh/josh-logic.ts +5 -3
  313. package/scripts/josh/josh-run.ts +19 -1
  314. package/scripts/josh/run-record-path.ts +15 -0
  315. package/scripts/josh/stamp-file.ts +11 -2
  316. package/scripts/lane/lane-adopt-cli.ts +98 -0
  317. package/scripts/lane/lane-adopt.ts +208 -0
  318. package/scripts/lane/lane-await.ts +5 -8
  319. package/scripts/lane/lane-baseline.ts +123 -0
  320. package/scripts/lane/lane-breakdown-cli.ts +64 -0
  321. package/scripts/lane/lane-breakdown-implement.ts +170 -0
  322. package/scripts/lane/lane-breakdown-laps.ts +107 -0
  323. package/scripts/lane/lane-breakdown-pinned.ts +76 -0
  324. package/scripts/lane/lane-breakdown-text.ts +145 -0
  325. package/scripts/lane/lane-breakdown.ts +239 -0
  326. package/scripts/lane/lane-check-stats.ts +29 -8
  327. package/scripts/lane/lane-child-invocation.ts +9 -2
  328. package/scripts/lane/lane-child-mark-keys.ts +12 -0
  329. package/scripts/lane/lane-child-marker.ts +20 -1
  330. package/scripts/lane/lane-ci-path-stats.ts +73 -0
  331. package/scripts/lane/lane-claim-stats.ts +84 -0
  332. package/scripts/lane/lane-close.ts +8 -3
  333. package/scripts/lane/lane-dispatch-log.ts +3 -3
  334. package/scripts/lane/lane-dispatch.ts +2 -2
  335. package/scripts/lane/lane-gap-stats.ts +7 -6
  336. package/scripts/lane/lane-guard-policy.ts +7 -1
  337. package/scripts/lane/lane-implement-stats.ts +207 -0
  338. package/scripts/lane/lane-implement-subagents.ts +90 -0
  339. package/scripts/lane/lane-investigation-switch.ts +16 -0
  340. package/scripts/lane/lane-ledger-kind.ts +31 -0
  341. package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
  342. package/scripts/lane/lane-ledger-schema.ts +119 -36
  343. package/scripts/lane/lane-ledger-write.ts +257 -0
  344. package/scripts/lane/lane-ledger.ts +31 -177
  345. package/scripts/lane/lane-load.ts +1 -1
  346. package/scripts/lane/lane-measure-cli.ts +176 -59
  347. package/scripts/lane/lane-net-read.ts +44 -0
  348. package/scripts/lane/lane-net-time.ts +212 -0
  349. package/scripts/lane/lane-open.ts +16 -7
  350. package/scripts/lane/lane-output.ts +2 -4
  351. package/scripts/lane/lane-overrun-prompt.ts +147 -0
  352. package/scripts/lane/lane-overrun.ts +274 -0
  353. package/scripts/lane/lane-paths.ts +24 -1
  354. package/scripts/lane/lane-pause-cli.ts +4 -1
  355. package/scripts/lane/lane-paused-spans.ts +97 -0
  356. package/scripts/lane/lane-plan-check.ts +207 -0
  357. package/scripts/lane/lane-plan-cli.ts +41 -0
  358. package/scripts/lane/lane-plan.ts +144 -0
  359. package/scripts/lane/lane-registry.ts +14 -1
  360. package/scripts/lane/lane-relaunch.ts +1 -1
  361. package/scripts/lane/lane-round-one-stats.ts +51 -0
  362. package/scripts/lane/lane-sampler.ts +23 -4
  363. package/scripts/lane/lane-session-action.ts +93 -18
  364. package/scripts/lane/lane-session-broker.ts +49 -21
  365. package/scripts/lane/lane-session-reread-main.ts +14 -0
  366. package/scripts/lane/lane-session-reread.ts +51 -0
  367. package/scripts/lane/lane-ship-lap-stats.ts +74 -0
  368. package/scripts/lane/lane-sleep.ts +58 -0
  369. package/scripts/lane/lane-spent.ts +1 -1
  370. package/scripts/lane/lane-split-stats.ts +28 -0
  371. package/scripts/lane/lane-stage-stats.ts +13 -1
  372. package/scripts/lane/lane-state.ts +41 -9
  373. package/scripts/lane/lane-stats-tables.ts +58 -0
  374. package/scripts/lane/lane-stats.ts +21 -5
  375. package/scripts/lane/lane-stop-stats.ts +51 -19
  376. package/scripts/lane/lane-tail-stats.ts +32 -0
  377. package/scripts/lane/lane-transcript-calls.ts +67 -10
  378. package/scripts/lane/lane-transcript-stats.ts +44 -12
  379. package/scripts/lane/lane-value-cell.ts +72 -0
  380. package/scripts/lane/lane-value.ts +118 -0
  381. package/scripts/lane/lane-withdraw-closed.ts +124 -0
  382. package/scripts/lane/lane-withdraw.ts +108 -31
  383. package/scripts/lane/lane-yield.ts +85 -0
  384. package/scripts/lane/openai-lane-ship.ts +12 -11
  385. package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
  386. package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
  387. package/scripts/lane/openai-lane-supervisor.ts +5 -4
  388. package/scripts/lib/poll.ts +38 -9
  389. package/scripts/lib/settled-together.ts +10 -0
  390. package/scripts/lib/shell-quote.ts +21 -0
  391. package/scripts/lib/token-shape.ts +111 -0
  392. package/scripts/lint/eslint-daemon-sources.ts +79 -0
  393. package/scripts/lint/eslint-daemon.ts +45 -6
  394. package/scripts/lint/eslint-warm-cli.ts +7 -0
  395. package/scripts/lint/eslint-warm.ts +66 -0
  396. package/scripts/lint/import-graph.ts +141 -11
  397. package/scripts/lint/lint-gate-scope.ts +2 -2
  398. package/scripts/lint/lint-parallel.ts +54 -6
  399. package/scripts/lint/lint-related.ts +154 -33
  400. package/scripts/lint/rename-cli.ts +179 -0
  401. package/scripts/lint/rename-identifier.ts +192 -0
  402. package/scripts/lint/type-only-imports.ts +58 -0
  403. package/scripts/notify/telegram-notify.ts +12 -3
  404. package/scripts/observations/observation-record.ts +42 -16
  405. package/scripts/observations/observations-flush.ts +32 -9
  406. package/scripts/overrides/overrides-check.ts +9 -1
  407. package/scripts/release/release-approval-prune.ts +7 -1
  408. package/scripts/repo/required-checks-logic.ts +4 -1
  409. package/scripts/repo/ruleset-check.ts +4 -3
  410. package/scripts/report/markdown-shape.ts +28 -0
  411. package/scripts/report/report-format-reference.ts +15 -1
  412. package/scripts/report/report-labels.ts +12 -1
  413. package/scripts/report/report-lint-child-summary.ts +94 -0
  414. package/scripts/report/report-lint-cli.ts +26 -7
  415. package/scripts/report/report-lint.ts +2 -20
  416. package/scripts/review/prose-stale-cli.ts +88 -0
  417. package/scripts/review/prose-stale-scan.ts +152 -0
  418. package/scripts/review/review-attest.ts +73 -23
  419. package/scripts/review/review-brief-cli.ts +68 -19
  420. package/scripts/review/review-brief-paths.ts +22 -0
  421. package/scripts/review/review-brief.ts +14 -7
  422. package/scripts/review/review-comment-carry.ts +47 -0
  423. package/scripts/review/review-comment-round.ts +85 -22
  424. package/scripts/review/review-finding-ledger.ts +109 -7
  425. package/scripts/review/review-finding-text.ts +43 -0
  426. package/scripts/review/review-interaction.ts +11 -0
  427. package/scripts/review/review-lane-handover.ts +68 -0
  428. package/scripts/review/review-lint-cli.ts +57 -0
  429. package/scripts/review/review-lint-findings.ts +92 -0
  430. package/scripts/review/review-lint-sections.ts +80 -0
  431. package/scripts/review/review-lint.ts +221 -0
  432. package/scripts/review/review-prior-findings.ts +102 -0
  433. package/scripts/review/review-readers-scan.ts +4 -2
  434. package/scripts/review/review-record-cli.ts +177 -54
  435. package/scripts/review/review-record-code-round.ts +55 -0
  436. package/scripts/review/review-record-comment.ts +60 -0
  437. package/scripts/review/review-record-interaction.ts +35 -0
  438. package/scripts/review/review-record-pinned.ts +108 -0
  439. package/scripts/review/review-record.ts +25 -1
  440. package/scripts/review/review-reviewed-tree.ts +307 -0
  441. package/scripts/review/review-round-one-closure.ts +62 -0
  442. package/scripts/review/review-round-reuse.ts +106 -0
  443. package/scripts/review/review-round2-cli.ts +91 -29
  444. package/scripts/review/review-round2-skip-comment.ts +55 -0
  445. package/scripts/review/review-round2.ts +24 -8
  446. package/scripts/review/review-stamps.ts +7 -3
  447. package/scripts/review/review-stop-log.ts +20 -6
  448. package/scripts/review/review-stops-cli.ts +2 -1
  449. package/scripts/review/review-submit-cli.ts +101 -0
  450. package/scripts/rules/agent-model.ts +1 -1
  451. package/scripts/rules/bash-triggers.ts +3 -3
  452. package/scripts/rules/concise-reply.ts +88 -0
  453. package/scripts/rules/credential-body.ts +77 -0
  454. package/scripts/rules/decision-oracle.ts +1 -1
  455. package/scripts/rules/delivered-rules.ts +36 -17
  456. package/scripts/rules/destructive-command.ts +3 -11
  457. package/scripts/rules/direct-issue-comment.ts +34 -12
  458. package/scripts/rules/direct-issue-read.ts +191 -0
  459. package/scripts/rules/direct-pr-create.ts +4 -24
  460. package/scripts/rules/early-heartbeat.ts +2 -0
  461. package/scripts/rules/edit-tool.ts +27 -0
  462. package/scripts/rules/file-edit-rows.ts +17 -0
  463. package/scripts/rules/file-rewrite.ts +146 -0
  464. package/scripts/rules/firing-point.ts +3 -2
  465. package/scripts/rules/gh-api.ts +128 -7
  466. package/scripts/rules/gh-repo-selector.ts +84 -0
  467. package/scripts/rules/git-force.ts +2 -2
  468. package/scripts/rules/guard-remedy.ts +14 -5
  469. package/scripts/rules/implementation-cut.ts +61 -28
  470. package/scripts/rules/investigator-foreground.ts +231 -0
  471. package/scripts/rules/issue-citation.ts +15 -60
  472. package/scripts/rules/issue-comments.ts +9 -4
  473. package/scripts/rules/josh-git-bare.ts +1 -1
  474. package/scripts/rules/lane-background.ts +41 -13
  475. package/scripts/rules/lane-direct-tool.ts +107 -9
  476. package/scripts/rules/lane-interactive-ask.ts +4 -1
  477. package/scripts/rules/lane-park.ts +4 -2
  478. package/scripts/rules/lane-spilled-read.ts +90 -0
  479. package/scripts/rules/lane-split-park.ts +1 -1
  480. package/scripts/rules/lane-switch-main.ts +4 -4
  481. package/scripts/rules/last-prompt.ts +20 -47
  482. package/scripts/rules/oracle-consulted.ts +1 -1
  483. package/scripts/rules/permission-guards.ts +5 -1
  484. package/scripts/rules/piped-verification-shapes.ts +110 -0
  485. package/scripts/rules/piped-verification.ts +67 -17
  486. package/scripts/rules/poll-loop.ts +72 -9
  487. package/scripts/rules/pr-merge.ts +5 -5
  488. package/scripts/rules/pre-gate-cut.ts +3 -3
  489. package/scripts/rules/protected-files.ts +88 -24
  490. package/scripts/rules/reply-language.ts +3 -17
  491. package/scripts/rules/reply-prose.ts +34 -0
  492. package/scripts/rules/repo-admin.ts +132 -0
  493. package/scripts/rules/reviewer-foreground.ts +88 -0
  494. package/scripts/rules/round-one-check-related.ts +120 -0
  495. package/scripts/rules/rule-body-guard.ts +5 -15
  496. package/scripts/rules/rule-value-cache.ts +53 -5
  497. package/scripts/rules/rule-value-candidates.ts +84 -0
  498. package/scripts/rules/rule-value-cli.ts +59 -11
  499. package/scripts/rules/rule-value.ts +17 -4
  500. package/scripts/rules/run-tail-rule.ts +1 -1
  501. package/scripts/rules/sed-in-place.ts +196 -0
  502. package/scripts/rules/shell-segments.ts +10 -2
  503. package/scripts/rules/stop-rule-rows.ts +1 -13
  504. package/scripts/rules/stop-rules.ts +33 -58
  505. package/scripts/rules/test-declared-commit.ts +1 -1
  506. package/scripts/rules/third-party-write.ts +110 -23
  507. package/scripts/rules/timed-rerun.ts +1 -1
  508. package/scripts/rules/turn-filings.ts +90 -0
  509. package/scripts/rules/worktree-guard.ts +22 -7
  510. package/scripts/run/board/run-board-ending-reason.ts +6 -2
  511. package/scripts/run/board/run-board-fit.ts +24 -1
  512. package/scripts/run/board/run-board-found.ts +134 -0
  513. package/scripts/run/board/run-board-header.ts +8 -0
  514. package/scripts/run/board/run-board-labels.ts +11 -1
  515. package/scripts/run/board/run-board-lane-time.ts +1 -0
  516. package/scripts/run/board/run-board-link.ts +15 -2
  517. package/scripts/run/board/run-board-notes.ts +7 -1
  518. package/scripts/run/board/run-board-operation.ts +4 -2
  519. package/scripts/run/board/run-board-phase.ts +2 -5
  520. package/scripts/run/board/run-board-plan.ts +1 -1
  521. package/scripts/run/board/run-board-read.ts +46 -1
  522. package/scripts/run/board/run-board-reason.ts +20 -8
  523. package/scripts/run/board/run-board-render-checks.ts +3 -2
  524. package/scripts/run/board/run-board-render-drawn.ts +11 -0
  525. package/scripts/run/board/run-board-render-found.ts +157 -0
  526. package/scripts/run/board/run-board-render-lead.ts +9 -0
  527. package/scripts/run/board/run-board-render-legend.ts +19 -0
  528. package/scripts/run/board/run-board-render-tail.ts +1 -1
  529. package/scripts/run/board/run-board-render.ts +82 -18
  530. package/scripts/run/board/run-board-spin.ts +1 -3
  531. package/scripts/run/board/run-board-status.ts +2 -0
  532. package/scripts/run/board/run-board-tick.ts +39 -10
  533. package/scripts/run/carry/run-carry-ended.ts +3 -2
  534. package/scripts/run/carry/run-carry.ts +3 -2
  535. package/scripts/run/cut/run-cut-cli.ts +33 -133
  536. package/scripts/run/cut/run-cut-digest.ts +152 -0
  537. package/scripts/run/cut/run-cut-handoff.ts +52 -8
  538. package/scripts/run/cut/run-cut-report.ts +24 -3
  539. package/scripts/run/cut/run-cut-resume.ts +129 -0
  540. package/scripts/run/cut/run-cut-review.ts +105 -0
  541. package/scripts/run/cut/run-cut.ts +18 -7
  542. package/scripts/run/detached-launch.ts +1 -1
  543. package/scripts/run/entry/run-entry-cli.ts +88 -19
  544. package/scripts/run/entry/run-entry-spill.ts +83 -0
  545. package/scripts/run/entry/run-entry-stop.ts +5 -1
  546. package/scripts/run/entry/run-entry-trust.ts +91 -0
  547. package/scripts/run/event/run-event-baseline.ts +56 -0
  548. package/scripts/run/event/run-event-filed.ts +9 -3
  549. package/scripts/run/event/run-event-overrun.ts +197 -0
  550. package/scripts/run/event/run-event-render.ts +4 -0
  551. package/scripts/run/event/run-event-stream.ts +25 -3
  552. package/scripts/run/hold/run-stop-notify.ts +3 -8
  553. package/scripts/run/merge/run-merge-cap.ts +57 -0
  554. package/scripts/run/merge/run-merge-cli.ts +66 -23
  555. package/scripts/run/merge/run-merge-outcome.ts +2 -0
  556. package/scripts/run/merge/run-merge-ship.ts +13 -3
  557. package/scripts/run/merge/run-merge-signals.ts +40 -23
  558. package/scripts/run/merge/run-merge-steps.ts +56 -36
  559. package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
  560. package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
  561. package/scripts/run/merge/run-merge.ts +41 -14
  562. package/scripts/run/review/run-review-cli.ts +306 -20
  563. package/scripts/run/review/run-review-early-push.ts +121 -0
  564. package/scripts/run/review/run-review-join-read.ts +94 -0
  565. package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
  566. package/scripts/run/review/run-review-pinned-join.ts +216 -0
  567. package/scripts/run/review/run-review-pinned-steps.ts +383 -0
  568. package/scripts/run/review/run-review-pinned-tree.ts +37 -0
  569. package/scripts/run/review/run-review-pinned.ts +53 -0
  570. package/scripts/run/review/run-review-steps.ts +70 -34
  571. package/scripts/run/review/run-review.ts +26 -6
  572. package/scripts/run/run-parent-cut-hook.ts +3 -4
  573. package/scripts/run/run-prep.ts +1 -0
  574. package/scripts/run/run-stage-read.ts +2 -6
  575. package/scripts/run/run-step-cli.ts +17 -5
  576. package/scripts/run/run-step-lane-plan.ts +27 -0
  577. package/scripts/run/run-step-section.ts +26 -7
  578. package/scripts/run/run-step.ts +5 -2
  579. package/scripts/run/ship/run-ship-args.ts +19 -0
  580. package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
  581. package/scripts/run/ship/run-ship-cli.ts +131 -39
  582. package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
  583. package/scripts/run/ship/run-ship-detach.ts +10 -4
  584. package/scripts/run/ship/run-ship-handover.ts +106 -0
  585. package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
  586. package/scripts/run/ship/run-ship-lap.ts +63 -0
  587. package/scripts/run/ship/run-ship-next.ts +27 -26
  588. package/scripts/run/ship/run-ship-overlap.ts +8 -3
  589. package/scripts/run/ship/run-ship-place.ts +9 -2
  590. package/scripts/run/ship/run-ship-planned.ts +92 -0
  591. package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
  592. package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
  593. package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
  594. package/scripts/run/ship/run-ship-preflight.ts +16 -4
  595. package/scripts/run/ship/run-ship-probe.ts +23 -9
  596. package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
  597. package/scripts/run/ship/run-ship-relaunch.ts +51 -0
  598. package/scripts/run/ship/run-ship-return.ts +103 -29
  599. package/scripts/run/ship/run-ship-review-reason.ts +24 -0
  600. package/scripts/run/ship/run-ship-review-steps.ts +230 -59
  601. package/scripts/run/ship/run-ship-review.ts +171 -51
  602. package/scripts/run/ship/run-ship-reviewer.ts +138 -42
  603. package/scripts/run/ship/run-ship-scoped.ts +39 -10
  604. package/scripts/run/ship/run-ship-stage.ts +49 -13
  605. package/scripts/run/ship/run-ship-steps.ts +24 -4
  606. package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
  607. package/scripts/run/ship/run-ship-stop-context.ts +50 -13
  608. package/scripts/run/ship/run-ship-sync.ts +171 -30
  609. package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
  610. package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
  611. package/scripts/run/ship/run-ship-waiter.ts +114 -0
  612. package/scripts/run/ship/run-ship.ts +16 -2
  613. package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
  614. package/scripts/run/wake/run-wake-cli.ts +6 -113
  615. package/scripts/run/wake/run-wake-code.ts +53 -11
  616. package/scripts/run/wake/run-wake-driver.ts +5 -3
  617. package/scripts/run/wake/run-wake-finish.ts +137 -0
  618. package/scripts/run/wake/run-wake-loop.ts +5 -3
  619. package/scripts/run/wake/run-wake-ports.ts +11 -3
  620. package/scripts/run/wake/run-wake-session.ts +2 -0
  621. package/scripts/run/wake/run-wake.ts +3 -2
  622. package/scripts/safe-chain/ci-installer-pin.ts +49 -8
  623. package/scripts/safe-chain/project-config.ts +3 -2
  624. package/scripts/security/secretlint-scan.ts +42 -9
  625. package/scripts/security/security-audit-provision-main.ts +9 -1
  626. package/scripts/security/security-audit-provision.ts +1 -1
  627. package/scripts/security/security-audit.ts +21 -1
  628. package/scripts/security/sonar-file.ts +10 -5
  629. package/scripts/split/split-assess.ts +12 -3
  630. package/scripts/split/split-early-notice.ts +116 -0
  631. package/scripts/split/split-measure.ts +20 -2
  632. package/scripts/split/split-resolution.ts +107 -0
  633. package/scripts/test/direct-importers.ts +185 -0
  634. package/scripts/test/pure-files-entries.ts +83 -0
  635. package/scripts/test/related-heavy-tests.ts +81 -0
  636. package/scripts/test/test-no-base.ts +88 -0
  637. package/scripts/test/test-pinned-cache-logic.ts +363 -0
  638. package/scripts/test/test-pinned-cache.ts +337 -0
  639. package/scripts/test/test-pinned-emit.ts +49 -0
  640. package/scripts/test/test-pinned-functions.ts +116 -0
  641. package/scripts/test/test-pinned-ledger.ts +80 -0
  642. package/scripts/test/test-pinned-logic.ts +208 -20
  643. package/scripts/test/test-pinned-main.ts +84 -18
  644. package/scripts/test/test-pinned-pool.ts +73 -0
  645. package/scripts/test/test-pinned-probe.ts +254 -0
  646. package/scripts/test/test-pinned-prune-logic.ts +118 -0
  647. package/scripts/test/test-pinned-reach.ts +194 -0
  648. package/scripts/test/test-pinned-record.ts +196 -0
  649. package/scripts/test/test-pinned-rewire.ts +148 -0
  650. package/scripts/test/test-pinned-spawn.ts +81 -0
  651. package/scripts/test/test-pinned-stage.ts +70 -0
  652. package/scripts/test/test-pinned.ts +258 -47
  653. package/scripts/test/test-reach-cli.ts +61 -0
  654. package/scripts/test/test-read-map.ts +35 -11
  655. package/scripts/test/test-read-recording.ts +17 -5
  656. package/scripts/test/test-read-trace.ts +26 -8
  657. package/scripts/test/test-red-logic.ts +46 -7
  658. package/scripts/test/test-red.ts +34 -14
  659. package/scripts/test/test-related-claim.ts +37 -0
  660. package/scripts/test/test-related-main.ts +4 -1
  661. package/scripts/test/test-related-moved.ts +91 -0
  662. package/scripts/test/test-related-record.ts +43 -23
  663. package/scripts/test/test-related-scope.ts +99 -34
  664. package/scripts/test/test-related-synced.ts +135 -0
  665. package/scripts/test/test-related-touched.ts +84 -0
  666. package/scripts/test/test-related.ts +167 -83
  667. package/scripts/test/test-unit-guard.ts +28 -5
  668. package/scripts/test/unit-durations.ts +41 -0
  669. package/scripts/test/unit-worker-share.ts +5 -4
  670. package/scripts/test/vitest-cache-directory.ts +19 -0
  671. package/scripts/test/vitest-cli-excludes.ts +14 -0
  672. package/scripts/test/vitest-failure-summary.ts +111 -0
  673. package/scripts/test/whole-suite-files.ts +12 -1
  674. package/scripts/time-runtime/time-batch-guard.ts +42 -4
  675. package/scripts/time-runtime/time-bundle-call.ts +1 -1
  676. package/scripts/time-runtime/time-bundles.ts +36 -5
  677. package/scripts/time-runtime/time-markers.ts +2 -2
  678. package/scripts/time-runtime/time-search-call.ts +38 -0
  679. package/scripts/time-runtime/time-shell.ts +1 -1
  680. package/scripts/time-runtime/time-transcript-line.ts +17 -5
  681. package/scripts/version/development-engines-verdict.ts +124 -0
  682. package/scripts/version/latest-corepack.ts +2 -0
  683. package/templates/gitignore +2 -0
  684. package/templates/workflows/ci.yml +41 -0
  685. package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
  686. package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
  687. package/dist/hooks/chunk-TUIFS5E7.js +0 -72
  688. package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
  689. package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
  690. package/scripts/rules/filing-cap.ts +0 -139
@@ -10,7 +10,8 @@ import { backlog_overlap, type Declared } from './backlog-overlap'
10
10
  // What the backlog offers, in the order it is decided: rank, then the
11
11
  // `run:solo` gate, then the cap. `backlog:next` and `backlog:plan --waves` both call `select`, so the
12
12
  // plan's first wave is what the run starts into an idle repository rather than an order the run never
13
- // takes.
13
+ // takes. Only the cap's width differs: `backlog:next` prints `DISPLAY_LIMIT` rows, a wave holds the
14
+ // lane limit (`limit`).
14
15
  //
15
16
  // **The cap comes last.** Applied while the standalone half is classified, before the gate, it would
16
17
  // cut a `run:solo` issue ranked sixth or lower before the gate could see it — and offer the issues
@@ -29,37 +30,51 @@ function everything_in(result: EpicNextResult): ReadonlyArray<EpicChild> {
29
30
  // The ranking is `epic_rank.rank`, shared with a named epic's lanes.
30
31
  const { rank } = epic_rank
31
32
 
32
- // This repository's candidates re-ranked inside the result, so `backlog:plan` prints the order the
33
- // offer is decided in.
34
- function rank_result(result: EpicNextResult, repo: string): EpicNextResult {
33
+ // This repository's candidates put in `order` inside the result; another repository's are left as
34
+ // they came.
35
+ function reorder_repo(
36
+ result: EpicNextResult,
37
+ repo: string,
38
+ order: (children: ReadonlyArray<EpicChild>) => ReadonlyArray<EpicChild>,
39
+ ): EpicNextResult {
35
40
  if (result.verdict !== 'run') return result
36
41
 
37
- const pool = everything_in(result)
38
-
39
42
  return {
40
43
  ...result,
41
44
  candidates: result.candidates.map((bundle) =>
42
- bundle.repo === repo ? { ...bundle, children: rank(bundle.children, pool) } : bundle,
45
+ bundle.repo === repo ? { ...bundle, children: order(bundle.children) } : bundle,
43
46
  ),
44
47
  }
45
48
  }
46
49
 
50
+ // The rank `select` starts from; `offer_order` adds the yielded-first step for `backlog:plan`.
51
+ function rank_result(result: EpicNextResult, repo: string): EpicNextResult {
52
+ const pool = everything_in(result)
53
+
54
+ return reorder_repo(result, repo, (children) => rank(children, pool))
55
+ }
56
+
47
57
  // The standalone rows past the cap, in offer order.
48
58
  function overflow_of(
49
59
  offered: ReadonlyArray<EpicChild>,
50
60
  standalone: ReadonlySet<string>,
61
+ limit: number,
51
62
  ): ReadonlySet<string> {
52
63
  const keys = offered.map((child) => epic_graph.key_of(child)).filter((key) => standalone.has(key))
53
64
 
54
- return new Set(keys.slice(git_next_issues.DISPLAY_LIMIT))
65
+ return new Set(keys.slice(limit))
55
66
  }
56
67
 
57
68
  function is_in(child: EpicChild, keys: ReadonlySet<string>): boolean {
58
69
  return keys.has(epic_graph.key_of(child))
59
70
  }
60
71
 
61
- function cap(selection: SoloSelection, standalone: ReadonlySet<string>): SoloSelection {
62
- const overflow = overflow_of(selection.offered, standalone)
72
+ function cap(
73
+ selection: SoloSelection,
74
+ standalone: ReadonlySet<string>,
75
+ limit: number,
76
+ ): SoloSelection {
77
+ const overflow = overflow_of(selection.offered, standalone, limit)
63
78
 
64
79
  if (overflow.size === 0) return selection
65
80
 
@@ -85,12 +100,40 @@ interface OfferInput {
85
100
  declared?: Declared
86
101
  // The issues' lane state records; a clash with a paused holder says so. Absent, none is named.
87
102
  lanes?: LaneRecords
103
+ // The keys of the issues that gave their lane back (`lane-yield.ts`). Absent, none did.
104
+ yielded?: ReadonlySet<string>
105
+ // How many standalone rows the cap keeps. Absent, `DISPLAY_LIMIT` — what `backlog:next` prints.
106
+ limit?: number
107
+ }
108
+
109
+ // A yielded issue ahead of the rank: its lane was taken from it, so it takes the first free one
110
+ // once its holder ends rather than queueing behind the pool (joshuafolkken/kit#5107). While the
111
+ // holder runs, its kept plan still withholds it in the separation. It goes ahead of a `run:solo`
112
+ // issue too: the solo gate sees it first.
113
+ function yielded_first(
114
+ ranked: ReadonlyArray<EpicChild>,
115
+ yielded: ReadonlySet<string>,
116
+ ): Array<EpicChild> {
117
+ return [
118
+ ...ranked.filter((child) => is_in(child, yielded)),
119
+ ...ranked.filter((child) => !is_in(child, yielded)),
120
+ ]
121
+ }
122
+
123
+ // A ranked result in the order `select` walks it — a yielded issue ahead of the rank — so
124
+ // `backlog:plan`'s "Ready now" reads in the order each wave is offered in (joshuafolkken/kit#5299).
125
+ function offer_order(
126
+ result: EpicNextResult,
127
+ repo: string,
128
+ yielded: ReadonlySet<string> = new Set(),
129
+ ): EpicNextResult {
130
+ return reorder_repo(result, repo, (children) => yielded_first(children, yielded))
88
131
  }
89
132
 
90
133
  // The overlap separation runs before the cap, for the reason the gate does: the cap only ever
91
134
  // removes what would otherwise be offered.
92
135
  function select(input: OfferInput): SoloSelection {
93
- const ranked = rank(input.candidates, input.pool)
136
+ const ranked = yielded_first(rank(input.candidates, input.pool), input.yielded ?? new Set())
94
137
  const { read, repo, lanes } = input
95
138
  const gated = epic_solo.select(ranked, read, repo)
96
139
  const separated = backlog_overlap.separate(gated, input.declared ?? new Map(), {
@@ -99,13 +142,17 @@ function select(input: OfferInput): SoloSelection {
99
142
  lanes,
100
143
  })
101
144
 
102
- return cap(separated, input.standalone)
145
+ return cap(separated, input.standalone, input.limit ?? git_next_issues.DISPLAY_LIMIT)
103
146
  }
104
147
 
105
148
  interface GateScope {
106
149
  standalone: ReadonlySet<string>
107
150
  declared: Declared
108
151
  lanes?: LaneRecords
152
+ yielded?: ReadonlySet<string>
153
+ // How many standalone rows a planned wave holds — the lane limit; `backlog:next`'s offer ignores
154
+ // it. Absent, a wave is not capped (joshuafolkken/kit#5300).
155
+ wave_limit?: number | undefined
109
156
  }
110
157
 
111
158
  // `backlog:next`'s gate: `epic_solo.gate` with this selection in place of its bare one.
@@ -117,7 +164,7 @@ function gate(result: EpicNextResult, read: BusyRead, repo: string, scope: GateS
117
164
  )
118
165
  }
119
166
 
120
- const backlog_rank = { everything_in, gate, rank_result, select }
167
+ const backlog_rank = { everything_in, gate, offer_order, rank_result, select }
121
168
 
122
169
  export { backlog_rank }
123
170
  export type { GateScope, OfferInput }
@@ -1,4 +1,5 @@
1
1
  import { lane_ledger, type LedgerEntry } from '#scripts/lane/lane-ledger'
2
+ import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
2
3
  import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
3
4
  import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
4
5
  import { backlog_ready, type ReadyPorts } from './backlog-ready'
@@ -78,7 +79,7 @@ async function read_real_ledger(): Promise<ReadonlyArray<LedgerEntry>> {
78
79
  }
79
80
 
80
81
  async function real_mark_offerable(is_offerable: boolean): Promise<void> {
81
- await lane_ledger.record_offerable(is_offerable)
82
+ await lane_ledger_write.record_offerable(is_offerable)
82
83
  }
83
84
 
84
85
  function ports_over(ready: ReadyPorts): DetectPorts {
@@ -24,7 +24,7 @@ import { backlog_rank, type GateScope } from './backlog-rank'
24
24
  // **No rule is written twice.** The first wave is `backlog_rank.select` — rank, `run:solo` gate,
25
25
  // overlap separation, cap —
26
26
  // over the very candidates `backlog:next` resolves, asked of an idle repository — so it is what
27
- // `backlog:next` prints when nothing is running. Each later wave marks the earlier waves closed and re-classifies the same pool
27
+ // `backlog:next` prints when nothing is running, cut at the lane limit rather than the printed count. Each later wave marks the earlier waves closed and re-classifies the same pool
28
28
  // through `epic_classify.classify_children` with the resolver `epic:next` sorted it with the first
29
29
  // time — `epic_cross_repo.resolve_cross_repo`, so a blocker in another repository still waits for its
30
30
  // release rather than counting as done on close — then puts the runnable set through
@@ -33,9 +33,13 @@ import { backlog_rank, type GateScope } from './backlog-rank'
33
33
  const IDLE: BusyRead = { kind: 'idle' }
34
34
  const CLOSED = 'CLOSED'
35
35
  const EMPTY_SCOPE: GateScope = { standalone: new Set(), declared: new Map() }
36
+ // A wave is as wide as the lanes that run it, not `backlog:next`'s `DISPLAY_LIMIT` — that printed
37
+ // count pushed an issue whose blocker closed in wave k past wave k+1 (joshuafolkken/kit#5300).
38
+ const UNCAPPED = Infinity
36
39
 
37
40
  const HEADING_ASSUMPTION =
38
41
  'Assumes every issue in a wave merges before the next wave starts. Issues a run already has are left out and count as merged first, and only issues of this repository are planned.'
42
+ const UNCAPPED_WIDTH = 'A wave is not capped: the lane limit could not be read.'
39
43
  const UNREACHED_HEADING = 'Not reached — each names what it is waiting on:'
40
44
  const PARALLEL_NOTE = '(parallel)'
41
45
  const NEEDS_DECISION_NOTE = `waiting on a person (\`${NEEDS_DECISION_LABEL}\`)`
@@ -84,8 +88,10 @@ function offer(
84
88
  pool: WavePool,
85
89
  ): ReadonlyArray<EpicChild> {
86
90
  const { repo, scope } = pool
91
+ const limit = scope.wave_limit ?? UNCAPPED
87
92
 
88
- return backlog_rank.select({ candidates, pool: children, read: IDLE, repo, ...scope }).offered
93
+ return backlog_rank.select({ candidates, pool: children, read: IDLE, repo, ...scope, limit })
94
+ .offered
89
95
  }
90
96
 
91
97
  // Ranked against the whole result, exactly as `backlog:next` ranks it, so the two agree.
@@ -175,6 +181,12 @@ function unreached_lines(plan: WavePlan, context: PlanContext): Array<string> {
175
181
  )
176
182
  }
177
183
 
184
+ function width_line(wave_limit: number | undefined): string {
185
+ if (wave_limit === undefined) return UNCAPPED_WIDTH
186
+
187
+ return `A wave holds at most ${String(wave_limit)} standalone issues, the lane limit; an epic's children are not counted.`
188
+ }
189
+
178
190
  function format_waves(
179
191
  result: EpicNextResult,
180
192
  context: PlanContext,
@@ -188,6 +200,7 @@ function format_waves(
188
200
  return [
189
201
  `Backlog waves — ${context.repo}`,
190
202
  HEADING_ASSUMPTION,
203
+ width_line(scope.wave_limit),
191
204
  '',
192
205
  ...(waves.length === 0 ? [backlog_plan.NOTHING] : waves),
193
206
  '',
@@ -200,9 +213,11 @@ const backlog_waves = {
200
213
  NEEDS_DECISION_NOTE,
201
214
  PARALLEL_NOTE,
202
215
  run_stalled_note,
216
+ UNCAPPED_WIDTH,
203
217
  UNREACHED_HEADING,
204
218
  build,
205
219
  format_waves,
220
+ width_line,
206
221
  }
207
222
 
208
223
  export { backlog_waves }
@@ -6,6 +6,7 @@ import {
6
6
  type AgentProvider,
7
7
  } from '#scripts/agent/agent-role-profile'
8
8
  import { agent_session_environment } from '#scripts/josh/agent-session-environment'
9
+ import { durable_state } from '#scripts/josh/durable-state'
9
10
  import { process_identity } from '#scripts/josh/process-identity'
10
11
  import { stamp_file } from '#scripts/josh/stamp-file'
11
12
  import { json_value } from '#scripts/lib/json-value'
@@ -27,8 +28,8 @@ import { z } from 'zod'
27
28
  // session can still start a Codex parent. The same choice is handed to the parent as
28
29
  // `JOSH_AGENT_PROVIDER`, the key a detached child with no session of its own resolves from.
29
30
 
30
- const LAUNCH_PREFIX = 'josh-backlogrun-launch-'
31
- const LOG_PREFIX = 'josh-backlogrun-log-'
31
+ const LAUNCH_PREFIX = durable_state.PREFIX.BACKLOGRUN_LAUNCH
32
+ const LOG_PREFIX = durable_state.PREFIX.BACKLOGRUN_LOG
32
33
  const LOG_SUFFIX = '.log'
33
34
 
34
35
  const launch_schema = z.object({
@@ -64,11 +65,11 @@ interface Launched {
64
65
  type StartResult = { kind: 'launched'; launched: Launched } | { kind: 'failed'; note: string }
65
66
 
66
67
  function launch_path(git_directory: string): string {
67
- return stamp_file.stamp_path(LAUNCH_PREFIX, git_directory)
68
+ return durable_state.path(LAUNCH_PREFIX, git_directory)
68
69
  }
69
70
 
70
71
  function log_path(git_directory: string): string {
71
- return stamp_file.stamp_path(LOG_PREFIX, git_directory, LOG_SUFFIX)
72
+ return durable_state.path(LOG_PREFIX, git_directory, LOG_SUFFIX)
72
73
  }
73
74
 
74
75
  function read_launch(target: string): LaunchRecord | undefined {
@@ -248,6 +249,8 @@ const backlogrun_launch = {
248
249
  argv_of,
249
250
  chosen_environment,
250
251
  is_running,
252
+ launch_path,
253
+ log_path,
251
254
  pending_launch,
252
255
  running_parent,
253
256
  start,
@@ -19,55 +19,57 @@ import { cost_pricing } from './cost-pricing'
19
19
  const POST_CUT_CONTEXT = 60_000
20
20
 
21
21
  // The requests a resumed session is expected to still run — the horizon the break-even is measured
22
- // against. A cut only pays if enough requests remain to recoup the preamble rewrite; even the shortest
23
- // resume (into the gate, commit, PR and merge) runs on the order of ten. Ten is the conservative
24
- // horizon: it keeps the shared threshold near the empirically validated 150_000 (this lands at
25
- // 135_000, inside the simulated 100k–200k band) while pricing the decision from the cost model. It is the knob a before/after lane measurement tunes.
26
- const EXPECTED_REMAINING_REQUESTS = 10
22
+ // against. A cut only pays if enough requests remain to recoup the preamble rewrite. Measured, not
23
+ // chosen: the lane children resumed after a cut over the 14 days to 2026-10-28 ran a median of 23
24
+ // requests per resumed session (748 sessions; p25 11, p90 68 — #5341). The median, not the mean, so a
25
+ // few long resumes do not pull every cut earlier.
26
+ const EXPECTED_REMAINING_REQUESTS = 23
27
27
 
28
28
  // The price of a dropped token relative to a rewritten one, from `cost-pricing.ts`. A cache read bills
29
- // `DEFAULT_CACHE_READ_MULTIPLIER` of base input and a 5-minute cache write `CACHE_WRITE_5M_MULTIPLIER`,
30
- // so a preamble rewrite costs this many per-request cache reads. The base input rate is common to both
31
- // and cancels, which is why only the ratio appears.
32
- //
33
- // **The standard cache-read rate, deliberately, not the running model's.** Opus 5.5 reads cache at
34
- // 0.05x and Fable 5.1 at 0.025x, which would move the threshold from 135,000 to 210,000 and 360,000 —
35
- // outside the 100k–200k band the cut was validated in. That changes when a cut fires, a decision that
36
- // needs a lane measurement of its own rather than arriving as a side effect of a corrected price list.
37
- function write_over_read(): number {
38
- return cost_pricing.CACHE_WRITE_5M_MULTIPLIER / cost_pricing.DEFAULT_CACHE_READ_MULTIPLIER
29
+ // the running model's cache-read multiplier of base input and a 5-minute cache write
30
+ // `CACHE_WRITE_5M_MULTIPLIER`, so a preamble rewrite costs this many per-request cache reads. The base
31
+ // input rate is common to both and cancels, which is why only the ratio appears. A model that reads
32
+ // cache cheaper (Opus 5.5 at 0.05x, Fable 5.1 at 0.025x) saves less per dropped token, so its cut
33
+ // breaks even later; an unpriced model reads at the standard rate.
34
+ function write_over_read(model: string | undefined): number {
35
+ return cost_pricing.CACHE_WRITE_5M_MULTIPLIER / cost_pricing.cache_read_multiplier_of(model)
39
36
  }
40
37
 
41
- // The current per-request context at which a cut breaks even over exactly `remaining` requests. The
42
- // shared cut threshold is this evaluated at `EXPECTED_REMAINING_REQUESTS`; rounded to an integer so the
38
+ // The current per-request context at which a cut breaks even for `model` over exactly `remaining`
39
+ // requests. The cut threshold `pnpm josh cost --cut` applies is this evaluated at
40
+ // `EXPECTED_REMAINING_REQUESTS` for the session's newest request's model; rounded to an integer so the
43
41
  // `>` comparison against a per-request billed-input count has no floating-point seam.
44
- function break_even_context(remaining: number = EXPECTED_REMAINING_REQUESTS): number {
42
+ function break_even_context(
43
+ model: string | undefined,
44
+ remaining: number = EXPECTED_REMAINING_REQUESTS,
45
+ ): number {
45
46
  if (remaining <= 0) return Infinity
46
47
 
47
- return Math.round(POST_CUT_CONTEXT * (1 + write_over_read() / remaining))
48
+ return Math.round(POST_CUT_CONTEXT * (1 + write_over_read(model) / remaining))
48
49
  }
49
50
 
50
51
  // The requests a cut must save over before it recoups its own preamble rewrite, at a given current
51
52
  // per-request context. Decreasing in context: the more a request currently pays, the fewer resumed
52
53
  // requests are needed to earn the cut back. Exposed as the diagnostic the break-even is read in —
53
- // `break_even_requests(current) < remaining` is the same decision as `pays_back`.
54
- function break_even_requests(current_context: number): number {
54
+ // `break_even_requests(current, model) < remaining` is the same decision as `pays_back`.
55
+ function break_even_requests(current_context: number, model: string | undefined): number {
55
56
  const saved_per_request = current_context - POST_CUT_CONTEXT
56
57
 
57
58
  if (saved_per_request <= 0) return Infinity
58
59
 
59
- return (POST_CUT_CONTEXT * write_over_read()) / saved_per_request
60
+ return (POST_CUT_CONTEXT * write_over_read(model)) / saved_per_request
60
61
  }
61
62
 
62
63
  // Whether cutting now pays back: the current per-request context has grown past the break-even point
63
64
  // for the expected remaining requests. Compared against the rounded `break_even_context` with `>`, so
64
- // it agrees to the token with `cost_verdict.classify` reading the derived `CONTEXT_CUT_THRESHOLD` — the
65
- // same one decision, whether a caller holds the threshold or asks this function.
65
+ // it agrees to the token with `cost_verdict.classify` reading the same threshold — the same one
66
+ // decision, whether a caller holds the threshold or asks this function.
66
67
  function pays_back(
67
68
  current_context: number,
69
+ model: string | undefined,
68
70
  remaining: number = EXPECTED_REMAINING_REQUESTS,
69
71
  ): boolean {
70
- return current_context > break_even_context(remaining)
72
+ return current_context > break_even_context(model, remaining)
71
73
  }
72
74
 
73
75
  const context_cut_payback = {
@@ -1,7 +1,7 @@
1
1
  import { agent_role_profile, type AgentProvider } from '#scripts/agent/agent-role-profile'
2
2
  import { cli_flags } from '#scripts/lib/cli-flags'
3
3
  import { codex_usage } from './codex-usage'
4
- import { CONTEXT_CUT_THRESHOLD } from './context-cut-threshold'
4
+ import { context_cut_payback } from './context-cut-payback'
5
5
  import { cost_corpus } from './cost-corpus'
6
6
  import { cost_transcript, type SessionUsage } from './cost-transcript'
7
7
  import { cost_usage } from './cost-usage'
@@ -16,7 +16,7 @@ import { transcript_cwd } from './transcript-cwd'
16
16
  // (`--session` / `--issue` / `--all` / `--run` / `--json`) and the `--cap` counterfactual were
17
17
  // retired because no rule or decision read them — leaving only the entry judgment `fullrun` /
18
18
  // `backlogrun` / `halfrun` / `pre-gate-cut` ask, and the `josh time` run-timing report keeps the
19
- // hand-off aggregates used to compare the shared context-cut threshold.
19
+ // hand-off aggregates used to compare the `--cut` break-even.
20
20
 
21
21
  const FAILURE_EXIT_CODE = 1
22
22
  const USAGE = 'Usage: josh cost (--cut | --over <tokens-per-request>) [--path <dir>]'
@@ -32,8 +32,14 @@ interface SessionFigures {
32
32
  threshold: number
33
33
  }
34
34
 
35
+ // `--cut` is not a number at parse time: its threshold is the break-even of the model the session's
36
+ // newest request ran on, known only once the transcript is read.
37
+ const CUT_LIMIT = 'cut'
38
+
39
+ type Limit = number | typeof CUT_LIMIT
40
+
35
41
  interface Options {
36
- over?: number
42
+ over?: Limit
37
43
  // The target project whose transcripts to read, or absent for this process's own working
38
44
  // directory. From the kit checkout, `--path <dir>` reads another
39
45
  // project's cost.
@@ -49,7 +55,7 @@ interface RawValues {
49
55
  type Environment = Readonly<Record<string, string | undefined>>
50
56
 
51
57
  interface RunContext {
52
- over: number
58
+ over: Limit
53
59
  path: string | undefined
54
60
  provider: AgentProvider
55
61
  }
@@ -72,10 +78,14 @@ function is_unparsed(raw: string | undefined, parsed: number | undefined): boole
72
78
  return parsed === undefined && raw !== undefined
73
79
  }
74
80
 
75
- function selected_threshold(values: RawValues): number | undefined {
76
- if (values.cut === true) return CONTEXT_CUT_THRESHOLD
81
+ // The cut threshold of a measured session: the break-even for its newest request's model. With no
82
+ // measurement it is the standard-rate break-even, the figure a report quotes beside no cost.
83
+ function cut_threshold(measurement: OverMeasurement | undefined): number {
84
+ return context_cut_payback.break_even_context(measurement?.model)
85
+ }
77
86
 
78
- return to_threshold(values.over)
87
+ function limit_of(limit: Limit, measurement: OverMeasurement): number {
88
+ return limit === CUT_LIMIT ? cut_threshold(measurement) : limit
79
89
  }
80
90
 
81
91
  function threshold_option(over: number | undefined): Pick<Options, 'over'> {
@@ -86,12 +96,17 @@ function path_option(path: string | undefined): Pick<Options, 'path'> {
86
96
  return path === undefined ? {} : { path }
87
97
  }
88
98
 
99
+ // `--cut` and `--over` name the same limit, so giving both is a refusal.
100
+ function cut_options(values: RawValues): Options | undefined {
101
+ return values.over === undefined ? { over: CUT_LIMIT, ...path_option(values.path) } : undefined
102
+ }
103
+
89
104
  // `exactOptionalPropertyTypes` rejects `{ over: undefined }`, so an absent flag contributes no key
90
105
  // at all rather than an undefined one.
91
106
  function to_options(values: RawValues): Options | undefined {
92
- if (values.cut === true && values.over !== undefined) return undefined
107
+ if (values.cut === true) return cut_options(values)
93
108
 
94
- const over = selected_threshold(values)
109
+ const over = to_threshold(values.over)
95
110
 
96
111
  if (is_unparsed(values.over, over)) return undefined
97
112
 
@@ -147,6 +162,7 @@ function measurement_of(session: SessionUsage): OverMeasurement {
147
162
  billed_input_per_request: session.records.map((record) =>
148
163
  cost_usage.billed_input(record.totals),
149
164
  ),
165
+ model: session.records.at(-1)?.model,
150
166
  }
151
167
  }
152
168
 
@@ -206,15 +222,20 @@ function measure(
206
222
  // missing message; `empty` → the whole-corpus missing message. A measured session that billed
207
223
  // nothing falls to `report_over`'s "no requests", which keeps a 0-request transcript distinct from a
208
224
  // transcript that is not there at all.
209
- function run_over_anthropic(own: OwnMeasurement, target: string, limit: number): number {
210
- if (own.kind === 'measured') return cost_verdict.report_over(own.measurement, limit)
225
+ function run_over_anthropic(own: OwnMeasurement, target: string, limit: Limit): number {
226
+ if (own.kind === 'measured') {
227
+ return cost_verdict.report_over(own.measurement, limit_of(limit, own.measurement))
228
+ }
211
229
 
212
230
  return report_empty(target, own.kind === 'absent' ? own.session_id : undefined)
213
231
  }
214
232
 
215
- function run_over_codex(target: string, limit: number, environment: Environment): number {
233
+ function run_over_codex(target: string, limit: Limit, environment: Environment): number {
216
234
  const measurement = measure(target, 'openai', environment)
217
- if (measurement !== undefined) return cost_verdict.report_over(measurement, limit)
235
+
236
+ if (measurement !== undefined) {
237
+ return cost_verdict.report_over(measurement, limit_of(limit, measurement))
238
+ }
218
239
 
219
240
  console.error(`No Codex usage found for the current OpenAI thread under ${target}.`)
220
241
 
@@ -225,7 +246,7 @@ function run_over_codex(target: string, limit: number, environment: Environment)
225
246
  // alone — identified by id, not by mtime.
226
247
  function run_over(
227
248
  target: string,
228
- limit: number,
249
+ limit: Limit,
229
250
  provider: AgentProvider,
230
251
  environment: Environment,
231
252
  ): number {
@@ -243,7 +264,7 @@ function verdict_of(measurement: OverMeasurement | undefined): CostVerdict {
243
264
  return UNMEASURABLE_VERDICT
244
265
  }
245
266
 
246
- return cost_verdict.classify(measurement, CONTEXT_CUT_THRESHOLD)
267
+ return cost_verdict.classify(measurement, cut_threshold(measurement))
247
268
  }
248
269
 
249
270
  // The verdict with the figures it was decided on — the newest request's billed input and the shared
@@ -254,7 +275,7 @@ function figures_of(measurement: OverMeasurement | undefined): SessionFigures {
254
275
  const is_priced = measurement !== undefined && verdict !== UNMEASURABLE_VERDICT
255
276
  const cost = is_priced ? cost_verdict.per_request_cost(measurement) : undefined
256
277
 
257
- return { verdict, cost, threshold: CONTEXT_CUT_THRESHOLD }
278
+ return { verdict, cost, threshold: cut_threshold(measurement) }
258
279
  }
259
280
 
260
281
  // The `--cut` verdict and its figures as a value, printing nothing. `run:report --handoff` quotes the
@@ -111,6 +111,14 @@ function cache_read_multiplier(price: ModelPrice): number {
111
111
  return price.cache_read_multiplier ?? DEFAULT_CACHE_READ_MULTIPLIER
112
112
  }
113
113
 
114
+ // The cache-read multiplier a model bills, by id. A model with no price row — an unpriced id, or no
115
+ // model at all — reads at the standard rate.
116
+ function cache_read_multiplier_of(model: string | undefined): number {
117
+ const price = model === undefined ? undefined : resolve_price(model)
118
+
119
+ return price === undefined ? DEFAULT_CACHE_READ_MULTIPLIER : cache_read_multiplier(price)
120
+ }
121
+
114
122
  function estimate_cost(totals: UsageTotals, price: ModelPrice): number {
115
123
  const weighted_input =
116
124
  totals.input_tokens +
@@ -225,6 +233,7 @@ const cost_pricing = {
225
233
  DEFAULT_CACHE_READ_MULTIPLIER,
226
234
  LONG_PROMPT_THRESHOLD,
227
235
  resolve_price,
236
+ cache_read_multiplier_of,
228
237
  estimate_cost,
229
238
  estimate_composition,
230
239
  cost_by_model,
@@ -9,9 +9,12 @@ const UNDER_VERDICT = 'under'
9
9
 
10
10
  // What `--over` reads of a session: the billed input each request paid, oldest first. The sequence
11
11
  // is carried rather than a single sum because the hand-off prices the newest request alone
12
- // — a sum could not answer what the session's current context costs.
12
+ // — a sum could not answer what the session's current context costs. `model` is the newest request's
13
+ // model, which prices the cut threshold (`context-cut-payback.ts`); a provider that records none
14
+ // leaves it absent and is read at the standard cache rate.
13
15
  interface OverMeasurement {
14
16
  billed_input_per_request: ReadonlyArray<number>
17
+ model?: string | undefined
15
18
  }
16
19
 
17
20
  // What a turn costs is decided by the accumulated preamble, not by what the turn does, so the
@@ -46,7 +46,7 @@ const INVESTIGATOR_AGENT = 'investigator'
46
46
  // them, so a run told the step up front is told the same step the refusal would name.
47
47
  const INVESTIGATION_LIMIT = `${String(INVESTIGATION_FILE_THRESHOLD)} files read and not edited, or ${String(INVESTIGATION_SEARCH_TURN_THRESHOLD)} search turns, since the last delegated unit`
48
48
  // Only the dispatch resets the count, so the step names no `josh delegate` call before it (#4345).
49
- const INVESTIGATION_STEP = `Dispatch the \`${INVESTIGATOR_AGENT}\` agent (\`kit:${INVESTIGATOR_AGENT}\` in a consumer); it returns \`file:line\` citations, never the file text.`
49
+ const INVESTIGATION_STEP = `Dispatch the \`${INVESTIGATOR_AGENT}\` agent (\`kit:${INVESTIGATOR_AGENT}\` in a consumer) with \`run_in_background: false\`; it returns \`file:line\` citations, never the file text.`
50
50
 
51
51
  // The enumeration. Short on purpose: every entry had to name a verifier, and most candidates could
52
52
  // not (below).
@@ -75,7 +75,7 @@ const DELEGATABLE_STEPS: ReadonlyArray<DelegatableStep> = [
75
75
  // chain at a small context, and the review round cap already composed the finding text, so what
76
76
  // the unit is handed is mechanical: run the commands and act on their deterministic answers. This
77
77
  // is not the batching guard's business — delegation only moves the chain to a cheaper context.
78
- does: 'run the late-run follow-up filing chain in an isolated unit — issue:file (scout, lint, labels, epic:bundle), then epic --add — given the finding text the parent already composed, and return the new Issue number and the epic:bundle answer',
78
+ does: 'run the late-run follow-up filing chain in an isolated unit — issue:file (lint, labels, scout, epic:bundle), then epic --add — given the finding text the parent already composed, and return the new Issue number and the epic:bundle answer',
79
79
  verifier:
80
80
  "the parent reads the filed Issue from GitHub with `pnpm josh issue:state <new>`, not the unit's summary; a follow-up reported filed but not created is still absent, the way `epic-child`'s reported-done-but-not-merged child is still open. The finding text came from the parent and `issue:file`'s steps are deterministic, so the unit's work is the mechanical execution `issue:state` confirms",
81
81
  },
@@ -210,6 +210,7 @@ const notice_refusal_path = hook_decision.create_refusal_stamp(NOTICE_STAMP_PREF
210
210
  const { deny_envelope, DISABLED_VALUES } = hook_decision
211
211
 
212
212
  const duplicate_reads = {
213
+ is_successful_read,
213
214
  REASON,
214
215
  NOTICE,
215
216
  }
@@ -24,8 +24,9 @@ const STAMP_PREFIX = 'josh-investigation-guard-'
24
24
  // **How the guard behaves for the session in hand, decided from the one-place enumeration**. It is
25
25
  // `refuse` in a dispatched lane child too (joshuafolkken/kit#3927): a hook denial returns to the model
26
26
  // as guidance, and the notice it replaced was measured not to move the reading. It wraps
27
- // `investigation_reads.should_block` here rather than inside it so the pure counting rule stays free of
28
- // process state and its own suite keeps testing it directly. A reviewer session stands down: reading
27
+ // `investigation_reads.should_block` here rather than inside it so the counting rule stays free of the
28
+ // session's switch state and its own suite keeps testing it directly — its only process read is the
29
+ // branch diff it asks before a refusal. A reviewer session stands down: reading
29
30
  // the diff's files is its whole task, and it carries the lane's marker, so it would otherwise be refused
30
31
  // like the child it reviews — the stand-down `implementation-cut.ts` takes for the same session.
31
32
  function should_block(
@@ -56,6 +57,16 @@ const investigation_refusal = GUARD.refusal
56
57
  const investigation_outcome = GUARD.outcome
57
58
  const { is_enabled, refusal_path } = GUARD
58
59
  const { deny_envelope, load_environment_file, DISABLED_VALUES } = hook_decision
60
+ const STAMP = hook_decision.create_refusal_stamp(STAMP_PREFIX)
61
+
62
+ // Whether this guard refuses the call in hand, asked with its real record — so the batching guard's
63
+ // lane-child search refusal can stand aside for it: only one reason surfaces per call, and delegating
64
+ // the reading supersedes batching it.
65
+ function will_refuse(tail: string, call: GuardedCall, run: GuardRun): boolean {
66
+ if (!is_enabled() || !investigation_reads.is_refusable_call(call)) return false
67
+
68
+ return should_block(tail, call, STAMP.last_ms(STAMP.path(run.transcript)), run)
69
+ }
59
70
 
60
71
  export {
61
72
  deny_envelope,
@@ -64,6 +75,7 @@ export {
64
75
  is_enabled,
65
76
  load_environment_file,
66
77
  refusal_path,
78
+ will_refuse,
67
79
  DISABLED_VALUES,
68
80
  SWITCH_ENV_KEY,
69
81
  }