@joshuafolkken/kit 1.1035.0 → 1.1094.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 (605) hide show
  1. package/.claude/settings.json +9 -8
  2. package/.claude/skills/epic-commands/SKILL.md +5 -7
  3. package/.claude/skills/verify-ui/SKILL.md +1 -1
  4. package/.claude/skills/workflow-commands/SKILL.md +21 -13
  5. package/.claude/skills/workflow-commands/background-commands.md +5 -4
  6. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -3
  7. package/.claude/skills/workflow-commands/backlogrun-lanes.md +9 -7
  8. package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
  9. package/.claude/skills/workflow-commands/backlogrun-progress.md +31 -37
  10. package/.claude/skills/workflow-commands/backlogrun-steps.md +25 -25
  11. package/.claude/skills/workflow-commands/backlogrun.md +2 -2
  12. package/.claude/skills/workflow-commands/chain-rule.md +6 -4
  13. package/.claude/skills/workflow-commands/delegation.md +3 -3
  14. package/.claude/skills/workflow-commands/followup-reference.md +3 -3
  15. package/.claude/skills/workflow-commands/followup.md +7 -3
  16. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -2
  17. package/.claude/skills/workflow-commands/fullrun.md +3 -2
  18. package/.claude/skills/workflow-commands/halfrun.md +16 -19
  19. package/.claude/skills/workflow-commands/issue-comments.md +2 -2
  20. package/.claude/skills/workflow-commands/issue-fold-existing.md +10 -10
  21. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +9 -4
  23. package/.claude/skills/workflow-commands/latest-gate.md +1 -1
  24. package/.claude/skills/workflow-commands/needs-human-review.md +1 -1
  25. package/.claude/skills/workflow-commands/observation-filing.md +44 -89
  26. package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
  27. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  28. package/.claude/skills/workflow-commands/prrun.md +36 -0
  29. package/.claude/skills/workflow-commands/retrospective.md +1 -1
  30. package/.claude/skills/workflow-commands/working-tree-hold.md +24 -4
  31. package/.codex/hooks.json +16 -20
  32. package/.github/actions/setup-pnpm/action.yml +93 -0
  33. package/.github/dependabot.yml +5 -1
  34. package/.github/workflows/auto-tag.yml +21 -0
  35. package/.github/workflows/ci.yml +26 -128
  36. package/.github/workflows/pr-classification.yml +5 -41
  37. package/.github/workflows/publish.yml +42 -48
  38. package/.github/workflows/sonar-qube.yml +37 -4
  39. package/AGENTS.md +12 -2
  40. package/CLAUDE.md +15 -16
  41. package/GEMINI.md +14 -2
  42. package/README.md +59 -18
  43. package/SECURITY.md +36 -6
  44. package/cspell/index.yaml +2 -1
  45. package/dist/CLAUDE.md +15 -16
  46. package/dist/config-merge/index.js +15 -3
  47. package/dist/hooks/batch-guard.js +4 -4
  48. package/dist/hooks/{chunk-Y27M6A7X.js → chunk-3C4YCCEF.js} +5 -13
  49. package/dist/hooks/{chunk-D67KVGGY.js → chunk-7ZPFAVR7.js} +17 -88
  50. package/dist/hooks/{chunk-HOYJU5RM.js → chunk-E54KGFEQ.js} +11 -4
  51. package/dist/hooks/{chunk-KG6PZD72.js → chunk-IL27YTPH.js} +2 -2
  52. package/dist/hooks/{chunk-GJT4BNVE.js → chunk-KHWHKJOM.js} +3 -3
  53. package/dist/hooks/{chunk-HU66PWWL.js → chunk-M2XDCUKD.js} +1076 -509
  54. package/dist/hooks/{chunk-GLBDBJOK.js → chunk-NKOG3XAV.js} +199 -304
  55. package/dist/hooks/{chunk-ZXNE6NY3.js → chunk-O4LL4HTI.js} +2 -7
  56. package/dist/hooks/{chunk-XPLP27WA.js → chunk-OIZUHPJI.js} +5 -27
  57. package/dist/hooks/{chunk-EIFCAJRB.js → chunk-UD24EAIL.js} +2546 -1636
  58. package/dist/hooks/codex-hook-adapter.js +10 -11
  59. package/dist/hooks/duplicate-read-guard.js +5 -5
  60. package/dist/hooks/format-edited.js +15 -34
  61. package/dist/hooks/inputs.json +279 -0
  62. package/dist/hooks/investigation-guard.js +5 -5
  63. package/dist/hooks/pretool-guard.js +23 -16
  64. package/dist/hooks/session-lang.js +0 -1
  65. package/dist/hooks/stop-guard.js +188 -509
  66. package/dist/josh.js +166 -225
  67. package/dist/managed-marker/index.js +2 -1
  68. package/dist/version/index.js +412 -64
  69. package/eslint/rules/promise.js +5 -0
  70. package/package.json +13 -8
  71. package/prompts/collaboration-workflow/file-edits.md +6 -53
  72. package/prompts/collaboration-workflow/issue-citation.md +5 -5
  73. package/prompts/collaboration-workflow/operating-rules.md +4 -4
  74. package/prompts/collaboration-workflow/overview.md +13 -1
  75. package/prompts/collaboration-workflow/report-format.md +1 -1
  76. package/prompts/collaboration-workflow/residency.md +4 -4
  77. package/prompts/collaboration-workflow/rule-delivery.md +5 -36
  78. package/prompts/collaboration-workflow/shell-body.md +7 -9
  79. package/prompts/collaboration-workflow/turn-batching.md +21 -37
  80. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  81. package/prompts/collaboration-workflow/wip-cap.md +36 -36
  82. package/prompts/collaboration-workflow.md +5 -8
  83. package/prompts/review-rubric.md +1 -1
  84. package/prompts/review.md +3 -3
  85. package/prompts/sonar-hotspot-handling.md +12 -4
  86. package/prompts/testing-guide.md +3 -3
  87. package/scripts/adopt/adopt-toolkits.ts +2 -2
  88. package/scripts/adopt/adopt.ts +0 -2
  89. package/scripts/agent/agent-argv.ts +0 -2
  90. package/scripts/agent/agent-diagnostics.ts +1 -1
  91. package/scripts/agent/agent-role-profile.ts +0 -7
  92. package/scripts/agent/claude-agent-argv.ts +0 -2
  93. package/scripts/agent/codex-agent-argv.ts +1 -1
  94. package/scripts/agent/codex-hooks.ts +92 -0
  95. package/scripts/agent/interactive-ask.ts +1 -1
  96. package/scripts/auto-ok/auto-ok-cli.ts +15 -25
  97. package/scripts/backlog/backlog-budget.ts +0 -2
  98. package/scripts/backlog/backlog-defect-priority.ts +2 -2
  99. package/scripts/backlog/backlog-drive-cli.ts +10 -8
  100. package/scripts/backlog/backlog-drive-named-offer.ts +16 -3
  101. package/scripts/backlog/backlog-drive-named.ts +2 -1
  102. package/scripts/backlog/backlog-drive.ts +76 -16
  103. package/scripts/backlog/backlog-named.ts +0 -3
  104. package/scripts/backlog/backlog-next.ts +32 -22
  105. package/scripts/backlog/backlog-offer-cli.ts +0 -5
  106. package/scripts/backlog/backlog-plan-cli.ts +6 -8
  107. package/scripts/backlog/backlog-plan.ts +1 -1
  108. package/scripts/backlog/backlog-pool.ts +34 -17
  109. package/scripts/backlog/backlog-rank.ts +106 -0
  110. package/scripts/backlog/backlog-ready.ts +19 -3
  111. package/scripts/backlog/backlog-scope.ts +1 -3
  112. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  113. package/scripts/backlog/backlog-stalled.ts +0 -1
  114. package/scripts/backlog/backlog-waves.ts +47 -31
  115. package/scripts/behavior/behavior-cli.ts +1 -1
  116. package/scripts/build/build-codex-hooks.ts +26 -0
  117. package/scripts/build/build-hooks.ts +92 -11
  118. package/scripts/build/hook-bundle-stamp.ts +111 -0
  119. package/scripts/cases/cases-cli.ts +1 -1
  120. package/scripts/cases/cases-logic.ts +1 -1
  121. package/scripts/ci/composite-actions.ts +30 -0
  122. package/scripts/ci/pr-classification.ts +3 -3
  123. package/scripts/claude/workflow-destination.ts +10 -1
  124. package/scripts/clone/clone-fingerprint.ts +0 -1
  125. package/scripts/clone/clone-scan-cli.ts +1 -1
  126. package/scripts/clone/clone-scan.ts +1 -1
  127. package/scripts/config-merge/json-format.ts +0 -1
  128. package/scripts/config-merge/patch-yaml-list.ts +1 -1
  129. package/scripts/cost-runtime/codex-usage.ts +2 -9
  130. package/scripts/cost-runtime/cost-attribute.ts +0 -1
  131. package/scripts/cost-runtime/cost-cli.ts +0 -3
  132. package/scripts/cost-runtime/cost-corpus.ts +0 -2
  133. package/scripts/cost-runtime/cost-format.ts +0 -3
  134. package/scripts/cost-runtime/cost-pricing.ts +0 -1
  135. package/scripts/cost-runtime/cost-tokens.ts +0 -1
  136. package/scripts/delegation/delegation-cli.ts +1 -1
  137. package/scripts/delegation/delegation-policy.ts +11 -1
  138. package/scripts/delegation/duplicate-read-guard.ts +0 -8
  139. package/scripts/delegation/fanout-cli.ts +1 -1
  140. package/scripts/delegation/fanout.ts +0 -2
  141. package/scripts/delegation/investigation-reads.ts +1 -164
  142. package/scripts/discovery/repo-map-logic.ts +0 -1
  143. package/scripts/discovery/repo-party-cli.ts +1 -1
  144. package/scripts/doctor/doctor-consumer.ts +4 -6
  145. package/scripts/doctor/doctor-logic.ts +0 -4
  146. package/scripts/doctor/doctor.ts +14 -9
  147. package/scripts/document/document-byte-budget.ts +8 -9
  148. package/scripts/document/document-byte-check.ts +0 -1
  149. package/scripts/document/document-read-cli.ts +0 -2
  150. package/scripts/document/document-section-cli.ts +1 -1
  151. package/scripts/document/document-section.ts +0 -3
  152. package/scripts/document/edit-files-cli.ts +0 -1
  153. package/scripts/document/entry-read-budget.ts +13 -4
  154. package/scripts/document/lane-child-read-set.ts +1 -6
  155. package/scripts/document/read-files-cli.ts +0 -4
  156. package/scripts/document/read-set-cli.ts +0 -2
  157. package/scripts/document/read-set-point-of-use.ts +1 -0
  158. package/scripts/document/read-set-pricing.ts +0 -1
  159. package/scripts/document/read-set-trim.ts +1 -1
  160. package/scripts/epic/epic-add.ts +1 -1
  161. package/scripts/epic/epic-audit-cli.ts +1 -4
  162. package/scripts/epic/epic-audit-orphans.ts +1 -3
  163. package/scripts/epic/epic-audit-rationale.ts +0 -1
  164. package/scripts/epic/epic-audit-report.ts +0 -2
  165. package/scripts/epic/epic-audit.ts +5 -4
  166. package/scripts/epic/epic-bundle-cli.ts +1 -1
  167. package/scripts/epic/epic-bundle-evidence.ts +0 -1
  168. package/scripts/epic/epic-bundle-referenced.ts +1 -5
  169. package/scripts/epic/epic-bundle.ts +0 -4
  170. package/scripts/epic/epic-busy.ts +4 -5
  171. package/scripts/epic/epic-candidate-confirm.ts +0 -5
  172. package/scripts/epic/epic-classify.ts +2 -2
  173. package/scripts/epic/epic-cli-argv.ts +0 -1
  174. package/scripts/epic/epic-close-comment.ts +1 -1
  175. package/scripts/epic/epic-close.ts +5 -2
  176. package/scripts/epic/epic-cross-repo.ts +2 -4
  177. package/scripts/epic/epic-decision.ts +0 -1
  178. package/scripts/epic/epic-fetch.ts +2 -2
  179. package/scripts/epic/epic-index.ts +2 -3
  180. package/scripts/epic/epic-issue.ts +1 -15
  181. package/scripts/epic/epic-lane-offer.ts +20 -30
  182. package/scripts/epic/epic-nested.ts +1 -1
  183. package/scripts/epic/epic-next-read.ts +1 -3
  184. package/scripts/epic/epic-next-views.ts +0 -1
  185. package/scripts/epic/epic-next.ts +1 -10
  186. package/scripts/epic/epic-parse.ts +0 -1
  187. package/scripts/epic/epic-promote.ts +0 -9
  188. package/scripts/epic/epic-rank.ts +37 -0
  189. package/scripts/epic/epic-read.ts +1 -2
  190. package/scripts/epic/epic-reconcile.ts +1 -1
  191. package/scripts/epic/epic-reference.ts +1 -13
  192. package/scripts/epic/epic-relation-recheck.ts +1 -1
  193. package/scripts/epic/epic-relations.ts +1 -1
  194. package/scripts/epic/epic-remove.ts +1 -1
  195. package/scripts/epic/epic-report.ts +0 -1
  196. package/scripts/epic/epic-run.ts +2 -3
  197. package/scripts/epic/epic-shape.ts +1 -1
  198. package/scripts/epic/epic-solo-stale.ts +107 -0
  199. package/scripts/epic/epic-solo.ts +32 -30
  200. package/scripts/epic/epic-triage.ts +1 -1
  201. package/scripts/epic/epic-validate.ts +1 -1
  202. package/scripts/epic/epic.ts +1 -1
  203. package/scripts/exports/unused-members-cli.ts +53 -0
  204. package/scripts/{git → followup}/followup-issue-number.ts +1 -6
  205. package/scripts/{git → followup}/git-followup-cleanup.ts +2 -1
  206. package/scripts/{git → followup}/git-followup-finish.ts +1 -3
  207. package/scripts/{git → followup}/git-followup-flush.ts +2 -2
  208. package/scripts/{git → followup}/git-followup-issue-close.ts +2 -2
  209. package/scripts/{git → followup}/git-followup-label.ts +4 -4
  210. package/scripts/followup/git-followup-merged.ts +112 -0
  211. package/scripts/{git → followup}/git-followup-pending.ts +1 -1
  212. package/scripts/{git → followup}/git-followup-workflow.ts +19 -11
  213. package/scripts/{git → followup}/git-pr-followup-wrapup.ts +9 -3
  214. package/scripts/{git → followup}/git-pr-followup.ts +53 -16
  215. package/scripts/gate/core-budget.ts +3 -9
  216. package/scripts/gate/gate-plan.ts +7 -4
  217. package/scripts/gate/gate-report.ts +0 -6
  218. package/scripts/gate/gate-tree.ts +1 -1
  219. package/scripts/gate/pre-commit-type-check.ts +0 -6
  220. package/scripts/gate/project-checks.ts +7 -8
  221. package/scripts/gate/scoped-green.ts +0 -1
  222. package/scripts/gate/type-check-step.ts +1 -1
  223. package/scripts/gate/verification-gate.ts +0 -2
  224. package/scripts/gh/fix-gh-packages.ts +15 -12
  225. package/scripts/gh/gh-spawn.ts +2 -2
  226. package/scripts/{git → gh}/git-ai-review-scan.ts +1 -5
  227. package/scripts/{git → gh}/git-gh-check.ts +2 -1
  228. package/scripts/{git → gh}/git-gh-exec.ts +1 -1
  229. package/scripts/{git → gh}/git-gh-failure.ts +1 -1
  230. package/scripts/{git → gh}/git-gh-helpers.ts +0 -1
  231. package/scripts/{git → gh}/git-gh-issue-list.ts +2 -0
  232. package/scripts/{git → gh}/git-gh-issue-read.ts +1 -1
  233. package/scripts/{git → gh}/git-gh-issue-rest.ts +6 -3
  234. package/scripts/{git → gh}/git-gh-pr-read.ts +24 -0
  235. package/scripts/{git → gh}/git-gh-pr-rest.ts +5 -2
  236. package/scripts/{git → gh}/git-gh-pr-review.ts +2 -2
  237. package/scripts/{git → gh}/git-gh-pr-rollup.ts +1 -3
  238. package/scripts/{git → gh}/git-gh-pr.ts +3 -1
  239. package/scripts/{git → gh}/git-pr-ai-review.ts +2 -5
  240. package/scripts/{git → gh}/git-pr-checks-eval.ts +0 -10
  241. package/scripts/{git → gh}/git-pr-checks-parse.ts +4 -20
  242. package/scripts/{git → gh}/git-pr-checks.ts +2 -1
  243. package/scripts/{git → gh}/git-pr-coderabbit.ts +2 -5
  244. package/scripts/{git → gh}/git-pr-confirmation.ts +1 -3
  245. package/scripts/{git → gh}/git-pr-managed-config.ts +2 -3
  246. package/scripts/{git → gh}/git-pr.ts +3 -3
  247. package/scripts/git/changed-file-scope.ts +0 -1
  248. package/scripts/git/git-branch.ts +3 -6
  249. package/scripts/git/git-command.ts +1 -1
  250. package/scripts/git/git-commit.ts +1 -1
  251. package/scripts/git/git-common-directory.ts +80 -24
  252. package/scripts/git/git-error.ts +66 -9
  253. package/scripts/git/git-execa-error.ts +6 -1
  254. package/scripts/git/git-issue.ts +1 -1
  255. package/scripts/git/git-preflight.ts +24 -6
  256. package/scripts/git/git-prompt.ts +6 -31
  257. package/scripts/git/git-push-transport.ts +12 -69
  258. package/scripts/git/git-push.ts +1 -1
  259. package/scripts/git/git-spawn.ts +79 -3
  260. package/scripts/git/git-ssh-keepalive.ts +74 -0
  261. package/scripts/git/git-staging.ts +1 -0
  262. package/scripts/git/git-status.ts +1 -1
  263. package/scripts/git/git-workflow.ts +1 -1
  264. package/scripts/git/gone-branch.ts +4 -2
  265. package/scripts/git/listing-cutoff.ts +1 -1
  266. package/scripts/git/main-merge.ts +1 -1
  267. package/scripts/git/main-sync.ts +19 -1
  268. package/scripts/git/parse-json-array.ts +0 -3
  269. package/scripts/git/repository-lock.ts +16 -16
  270. package/scripts/git/{git-stash.ts → stash/git-stash.ts} +1 -1
  271. package/scripts/git/{stash-pop-args.ts → stash/stash-pop-args.ts} +1 -1
  272. package/scripts/git/{stash-pop-cli.ts → stash/stash-pop-cli.ts} +0 -1
  273. package/scripts/git/{stash-sweep-lock.ts → stash/stash-sweep-lock.ts} +3 -3
  274. package/scripts/hooks/codex-hook-adapter.ts +0 -1
  275. package/scripts/hooks/edited-cspell.ts +1 -1
  276. package/scripts/hooks/format-edited-cli.ts +15 -0
  277. package/scripts/hooks/format-edited-file.ts +4 -9
  278. package/scripts/hooks/pre-push-unit.ts +0 -4
  279. package/scripts/hooks/pretool-guard-cli.ts +21 -0
  280. package/scripts/hooks/pretool-guard.ts +36 -19
  281. package/scripts/hooks/reserved-run.ts +1 -1
  282. package/scripts/hooks/step-zero-notice.ts +76 -0
  283. package/scripts/hooks/stop-guard.ts +47 -21
  284. package/scripts/init/claude-plugin-config.ts +4 -5
  285. package/scripts/init/hook-command-rewrite.ts +16 -8
  286. package/scripts/init/hook-launch.ts +41 -15
  287. package/scripts/init/init-basic.ts +2 -5
  288. package/scripts/init/init-logic.ts +7 -0
  289. package/scripts/init/init.ts +0 -3
  290. package/scripts/init/project-profile.ts +6 -11
  291. package/scripts/init/start-exec.ts +8 -2
  292. package/scripts/init/start-plan.ts +18 -5
  293. package/scripts/init/start-setup-pr.ts +52 -25
  294. package/scripts/init/start-steps.ts +42 -9
  295. package/scripts/init/start.ts +40 -10
  296. package/scripts/issue/behavior-change-lint.ts +0 -1
  297. package/scripts/issue/defect-rate-cli.ts +3 -3
  298. package/scripts/issue/defect-rate.ts +1 -2
  299. package/scripts/{git → issue}/git-next-issues.ts +37 -8
  300. package/scripts/issue/issue-backlinks-cli.ts +1 -5
  301. package/scripts/issue/issue-cite-cli.ts +2 -6
  302. package/scripts/issue/issue-cite.ts +19 -3
  303. package/scripts/issue/issue-classification.ts +1 -9
  304. package/scripts/issue/issue-closing-pr.ts +4 -4
  305. package/scripts/issue/issue-comment-cli.ts +2 -2
  306. package/scripts/issue/issue-file-cli.ts +5 -5
  307. package/scripts/issue/issue-file.ts +2 -2
  308. package/scripts/issue/issue-fold-cli.ts +1 -1
  309. package/scripts/issue/issue-fold-existing-cli.ts +1 -1
  310. package/scripts/{git → issue}/issue-labels.ts +20 -0
  311. package/scripts/issue/issue-lint-cli.ts +1 -1
  312. package/scripts/issue/issue-merged.ts +2 -2
  313. package/scripts/issue/issue-rank.ts +89 -0
  314. package/scripts/issue/issue-read-cli.ts +8 -47
  315. package/scripts/issue/issue-read.ts +0 -2
  316. package/scripts/issue/issue-report-failures.ts +80 -0
  317. package/scripts/issue/issue-scout-cli.ts +1 -7
  318. package/scripts/issue/issue-scout-closed.ts +2 -2
  319. package/scripts/issue/issue-scout.ts +0 -2
  320. package/scripts/issue/issue-state-cli.ts +5 -46
  321. package/scripts/issue/issue-state.ts +1 -5
  322. package/scripts/issue/markdown-section.ts +1 -1
  323. package/scripts/issue/measure-rerun-cli.ts +1 -1
  324. package/scripts/issue/reproduction-measure.ts +1 -1
  325. package/scripts/josh/cli-body.ts +1 -1
  326. package/scripts/josh/file-map-stamp.ts +0 -3
  327. package/scripts/josh/josh-command-map.ts +2 -102
  328. package/scripts/josh/josh-commands-ai.ts +9 -102
  329. package/scripts/josh/josh-commands-backlog.ts +5 -1
  330. package/scripts/josh/josh-commands-development.ts +10 -3
  331. package/scripts/josh/josh-commands-issue.ts +2 -2
  332. package/scripts/josh/josh-commands-maintenance.ts +7 -0
  333. package/scripts/josh/josh-commands-project.ts +7 -1
  334. package/scripts/josh/josh-commands-run.ts +114 -0
  335. package/scripts/josh/josh-commands-versioning.ts +8 -0
  336. package/scripts/josh/josh-commands-workflow.ts +2 -2
  337. package/scripts/josh/path-decision.ts +0 -3
  338. package/scripts/josh/process-owner.ts +16 -0
  339. package/scripts/josh/session-language-cli.ts +0 -1
  340. package/scripts/lane/lane-await.ts +3 -6
  341. package/scripts/lane/lane-capacity.ts +6 -3
  342. package/scripts/lane/lane-child-invocation.ts +20 -7
  343. package/scripts/lane/lane-child-marker.ts +16 -2
  344. package/scripts/lane/lane-cli.ts +33 -18
  345. package/scripts/lane/lane-close.ts +1 -3
  346. package/scripts/lane/lane-dispatch.ts +3 -4
  347. package/scripts/lane/lane-environment.ts +0 -9
  348. package/scripts/lane/lane-handoff.ts +38 -0
  349. package/scripts/lane/lane-launch-cli.ts +21 -11
  350. package/scripts/lane/lane-open.ts +0 -3
  351. package/scripts/lane/lane-output.ts +0 -1
  352. package/scripts/lane/lane-registry.ts +9 -0
  353. package/scripts/lane/lane-report.ts +10 -4
  354. package/scripts/lane/openai-lane-ship.ts +2 -0
  355. package/scripts/lane/openai-lane-supervisor-decision.ts +2 -1
  356. package/scripts/lane/openai-lane-supervisor.ts +6 -4
  357. package/scripts/lane/openai-review-broker.ts +3 -0
  358. package/scripts/lib/bounded-pool.ts +1 -0
  359. package/scripts/lib/json-value.ts +5 -4
  360. package/scripts/lib/poll.ts +3 -1
  361. package/scripts/lib/timed-fetch.ts +15 -0
  362. package/scripts/lib/timeouts.ts +13 -0
  363. package/scripts/{yaml → lib}/yaml-document.ts +3 -1
  364. package/scripts/{bytes → lines}/bytes-command.ts +0 -2
  365. package/scripts/lines/lines-command.ts +0 -1
  366. package/scripts/lint/lint-related-scope.ts +0 -1
  367. package/scripts/lint/lint-related.ts +1 -1
  368. package/scripts/{git → notify}/telegram-notify.ts +2 -2
  369. package/scripts/{git → notify}/telegram-test.ts +4 -4
  370. package/scripts/observations/observation-ledger-home.ts +0 -1
  371. package/scripts/observations/observation-ledger-line.ts +0 -1
  372. package/scripts/observations/observation-ledger-migrate.ts +1 -1
  373. package/scripts/observations/observations-flush-landing.ts +3 -3
  374. package/scripts/observations/observations-flush.ts +2 -2
  375. package/scripts/overrides/lockfile-overrides.ts +1 -1
  376. package/scripts/overrides/overrides-logic.ts +1 -1
  377. package/scripts/package/package-scout-cli.ts +2 -7
  378. package/scripts/package/package-scout.ts +0 -2
  379. package/scripts/propagate/propagate-git-failure.ts +0 -1
  380. package/scripts/propagate/propagate-git.ts +0 -2
  381. package/scripts/propagate/propagate-plan.ts +185 -0
  382. package/scripts/propagate/propagate-publish.ts +2 -3
  383. package/scripts/propagate/propagate-run.ts +5 -3
  384. package/scripts/propagate/propagate-steps.ts +19 -6
  385. package/scripts/propagate/propagate-targets.ts +13 -6
  386. package/scripts/propagate/propagate.ts +60 -46
  387. package/scripts/refactor/import-graph.ts +0 -1
  388. package/scripts/refactor/refactor-lint.ts +0 -2
  389. package/scripts/refactor/refactor-scan.ts +0 -1
  390. package/scripts/refactor/refactor-targets.ts +0 -1
  391. package/scripts/registry-migration/migrate-logic.ts +1 -2
  392. package/scripts/registry-migration/migrate.ts +19 -9
  393. package/scripts/release/github-release-cli.ts +8 -6
  394. package/scripts/release/github-release-environment.ts +46 -0
  395. package/scripts/release/github-release.ts +151 -94
  396. package/scripts/release/release-history.ts +0 -1
  397. package/scripts/release/release-plan.ts +0 -1
  398. package/scripts/release/release-publish.ts +2 -3
  399. package/scripts/release/release-scope-cli.ts +1 -2
  400. package/scripts/release/release-tag.ts +2 -4
  401. package/scripts/repo/auto-merge-setting-logic.ts +1 -4
  402. package/scripts/repo/repo-setting.ts +1 -1
  403. package/scripts/repo/repository-labels.ts +3 -3
  404. package/scripts/repo/required-checks-logic.ts +206 -0
  405. package/scripts/repo/required-checks-report.ts +71 -0
  406. package/scripts/repo/ruleset-check-cli.ts +53 -0
  407. package/scripts/repo/ruleset-check.ts +147 -0
  408. package/scripts/report/report-lint-cli.ts +0 -4
  409. package/scripts/review/disposition-cli.ts +1 -1
  410. package/scripts/review/live-evidence.ts +1 -1
  411. package/scripts/review/review-attest-cli.ts +0 -6
  412. package/scripts/review/review-attest.ts +48 -16
  413. package/scripts/review/review-brief-cli.ts +72 -8
  414. package/scripts/review/review-brief.ts +67 -9
  415. package/scripts/review/review-diff-parts.ts +89 -0
  416. package/scripts/review/review-diff-write.ts +97 -0
  417. package/scripts/review/review-finding-ledger.ts +0 -1
  418. package/scripts/review/review-record-cli.ts +1 -1
  419. package/scripts/review/review-record.ts +0 -1
  420. package/scripts/review/review-round2-cli.ts +1 -1
  421. package/scripts/review/review-stamps.ts +6 -2
  422. package/scripts/review/review-tree.ts +14 -2
  423. package/scripts/rules/decision-oracle-stage.ts +18 -0
  424. package/scripts/rules/decision-oracle.ts +10 -7
  425. package/scripts/rules/delivered-rules.ts +9 -1
  426. package/scripts/rules/destructive-command.ts +119 -0
  427. package/scripts/rules/direct-filing.ts +1 -1
  428. package/scripts/rules/file-body.ts +0 -3
  429. package/scripts/rules/filing-cap.ts +0 -2
  430. package/scripts/rules/gh-api.ts +7 -2
  431. package/scripts/rules/git-argv.ts +18 -7
  432. package/scripts/rules/git-force.ts +0 -2
  433. package/scripts/rules/implementation-cut.ts +1 -2
  434. package/scripts/rules/index-guard.ts +71 -0
  435. package/scripts/rules/issue-citation.ts +50 -25
  436. package/scripts/rules/issue-fold.ts +1 -2
  437. package/scripts/rules/lane-background.ts +15 -7
  438. package/scripts/rules/lane-carry-conflict.ts +1 -2
  439. package/scripts/rules/lane-park.ts +14 -8
  440. package/scripts/rules/last-prompt.ts +1 -0
  441. package/scripts/rules/oracle-consulted.ts +1 -1
  442. package/scripts/rules/oracle-firing.ts +1 -0
  443. package/scripts/rules/oracle-list-cli.ts +0 -3
  444. package/scripts/rules/permission-guards.ts +14 -0
  445. package/scripts/rules/pre-gate-cut.ts +2 -2
  446. package/scripts/rules/protected-files.ts +74 -0
  447. package/scripts/rules/reply-language.ts +1 -1
  448. package/scripts/rules/rule-body-guard.ts +0 -3
  449. package/scripts/rules/rule-value-cli.ts +0 -1
  450. package/scripts/rules/shell-body-trigger.ts +0 -1
  451. package/scripts/rules/shell-segments.ts +0 -1
  452. package/scripts/rules/stop-rules.ts +13 -5
  453. package/scripts/rules/tail-commands.ts +1 -1
  454. package/scripts/rules/test-declared-commit.ts +1 -1
  455. package/scripts/rules/third-party-write.ts +0 -1
  456. package/scripts/rules/worktree-guard.ts +26 -9
  457. package/scripts/run/run-carry-cli.ts +0 -2
  458. package/scripts/run/run-carry-stash.ts +2 -2
  459. package/scripts/run/run-carry-streak.ts +1 -1
  460. package/scripts/run/run-cut-cli.ts +0 -7
  461. package/scripts/run/run-cut.ts +8 -54
  462. package/scripts/run/run-ending-cli.ts +1 -1
  463. package/scripts/run/run-ending.ts +1 -4
  464. package/scripts/run/run-entry-cli.ts +101 -24
  465. package/scripts/run/run-entry.ts +0 -1
  466. package/scripts/run/run-event-cli.ts +0 -2
  467. package/scripts/run/run-event-follow.ts +1 -0
  468. package/scripts/run/run-event-render.ts +1 -1
  469. package/scripts/run/run-event-scope.ts +53 -4
  470. package/scripts/run/run-event-stream.ts +0 -3
  471. package/scripts/run/run-event-watch.ts +1 -0
  472. package/scripts/run/run-halfrun-resume.ts +20 -7
  473. package/scripts/run/run-headless.ts +24 -8
  474. package/scripts/run/run-hold-cli.ts +33 -10
  475. package/scripts/run/run-hold.ts +25 -5
  476. package/scripts/run/run-issue-number.ts +1 -1
  477. package/scripts/run/run-liveness-cli.ts +1 -1
  478. package/scripts/run/run-liveness.ts +2 -3
  479. package/scripts/run/run-merge-cli.ts +5 -13
  480. package/scripts/run/run-merge-steps.ts +5 -5
  481. package/scripts/run/run-merge.ts +1 -1
  482. package/scripts/run/run-next-cli.ts +1 -1
  483. package/scripts/run/run-parent-cut-hook.ts +143 -0
  484. package/scripts/run/run-preflight.ts +2 -9
  485. package/scripts/run/run-prep-cli.ts +2 -2
  486. package/scripts/run/run-prep-locate.ts +1 -1
  487. package/scripts/run/run-progress-cli.ts +4 -7
  488. package/scripts/run/run-progress-clock.ts +6 -34
  489. package/scripts/run/run-progress-config.ts +0 -4
  490. package/scripts/run/run-progress-read.ts +3 -3
  491. package/scripts/run/run-progress.ts +22 -10
  492. package/scripts/run/run-prrun-resume.ts +117 -0
  493. package/scripts/run/run-report-cli.ts +1 -2
  494. package/scripts/run/run-report.ts +0 -1
  495. package/scripts/run/run-retrospective.ts +1 -1
  496. package/scripts/run/run-review-cli.ts +0 -3
  497. package/scripts/run/run-review-steps.ts +2 -0
  498. package/scripts/run/run-review.ts +0 -2
  499. package/scripts/run/run-ship-cli.ts +27 -77
  500. package/scripts/run/run-ship-detach.ts +1 -0
  501. package/scripts/run/run-ship-next.ts +106 -0
  502. package/scripts/run/run-ship-preflight.ts +87 -0
  503. package/scripts/run/run-ship-return.ts +8 -3
  504. package/scripts/run/run-ship-review-steps.ts +23 -30
  505. package/scripts/run/run-ship-review.ts +20 -0
  506. package/scripts/run/run-ship-scoped.ts +45 -0
  507. package/scripts/run/run-ship-stage.ts +5 -1
  508. package/scripts/run/run-ship-steps.ts +105 -0
  509. package/scripts/run/run-ship.ts +2 -1
  510. package/scripts/run/run-stage-read.ts +62 -0
  511. package/scripts/run/run-stage.ts +160 -0
  512. package/scripts/run/run-status-cli.ts +1 -1
  513. package/scripts/run/run-step-cli.ts +21 -14
  514. package/scripts/run/run-step.ts +1 -1
  515. package/scripts/run/run-stop-notify.ts +1 -3
  516. package/scripts/run/run-stranded-cli.ts +0 -4
  517. package/scripts/run/run-stranded-detect.ts +2 -2
  518. package/scripts/run/run-tail-cli.ts +17 -11
  519. package/scripts/run/run-tail.ts +2 -0
  520. package/scripts/run/run-tidy-lanes.ts +2 -1
  521. package/scripts/run/run-tidy-stashes.ts +12 -12
  522. package/scripts/run/run-wake-cli.ts +4 -5
  523. package/scripts/run/run-wake-describe.ts +19 -1
  524. package/scripts/run/run-wake-loop.ts +2 -2
  525. package/scripts/run/run-wake.ts +2 -4
  526. package/scripts/run/run-watcher-guard-cli.ts +0 -4
  527. package/scripts/run/run-watcher-guard.ts +9 -7
  528. package/scripts/run/run-watcher-hook.ts +1 -1
  529. package/scripts/safe-chain/ci-installer-pin.ts +11 -5
  530. package/scripts/safe-chain/project-config.ts +1 -1
  531. package/scripts/security/security-audit-provision-logic.ts +0 -1
  532. package/scripts/security/security-audit-provision.ts +3 -2
  533. package/scripts/security/security-audit.ts +0 -1
  534. package/scripts/security/security-updates-logic.ts +0 -1
  535. package/scripts/sonar/sonar-hotspots-cli.ts +10 -52
  536. package/scripts/sonar/sonar-hotspots.ts +0 -1
  537. package/scripts/sonar/sonar-new-code-cli.ts +150 -0
  538. package/scripts/sonar/sonar-new-code.ts +79 -0
  539. package/scripts/sonar/sonar-project.ts +97 -0
  540. package/scripts/split/split-assess-cli.ts +1 -1
  541. package/scripts/split/split-assess.ts +2 -5
  542. package/scripts/sync/managed-config-scope-cli.ts +1 -1
  543. package/scripts/sync/skill-migration.ts +0 -1
  544. package/scripts/sync/sync-hook-safety.ts +2 -3
  545. package/scripts/sync/sync.ts +1 -8
  546. package/scripts/sync/template-source-logic.ts +0 -1
  547. package/scripts/sync/workflow-pin-logic.ts +16 -28
  548. package/scripts/test/test-declared-logic.ts +0 -1
  549. package/scripts/test/test-declared.ts +0 -2
  550. package/scripts/test/test-red-commit.ts +2 -2
  551. package/scripts/test/test-red.ts +14 -7
  552. package/scripts/test/test-type-logic.ts +1 -1
  553. package/scripts/test/unit-worker-share.ts +7 -16
  554. package/scripts/time-runtime/time-batch-guard.ts +1 -2
  555. package/scripts/time-runtime/time-bundle-call.ts +2 -3
  556. package/scripts/time-runtime/time-bundles.ts +0 -4
  557. package/scripts/time-runtime/time-density-hook.ts +0 -2
  558. package/scripts/time-runtime/time-followup-stage.ts +1 -1
  559. package/scripts/time-runtime/time-format.ts +0 -73
  560. package/scripts/time-runtime/time-round-trips.ts +0 -4
  561. package/scripts/time-runtime/time-single-check.ts +1 -1
  562. package/scripts/time-runtime/time-spans.ts +1 -1
  563. package/scripts/time-runtime/time-transcript-line.ts +1 -2
  564. package/scripts/ui/ui-routes-cli.ts +0 -5
  565. package/scripts/ui/ui-routes.ts +0 -5
  566. package/scripts/version/effective-upstream.ts +2 -2
  567. package/scripts/version/latest-corepack.ts +7 -9
  568. package/scripts/version/latest-scope-cli.ts +0 -2
  569. package/scripts/version/latest-update.ts +0 -2
  570. package/scripts/version/npm-registry.ts +97 -0
  571. package/scripts/version/publishable-range-check.ts +1 -1
  572. package/scripts/version/release-hold.ts +0 -4
  573. package/scripts/version/running-binary.ts +2 -2
  574. package/scripts/version/version-check-logic.ts +0 -2
  575. package/scripts/version/version-commands.ts +5 -4
  576. package/scripts/version/version-remote.ts +28 -14
  577. package/scripts/version/version-targets.ts +4 -4
  578. package/templates/sonar-project.properties +28 -1
  579. package/templates/workflows/dependabot-auto-merge.yml +5 -3
  580. package/templates/workflows/github-release.yml +81 -0
  581. package/prompts/collaboration-workflow/cross-repo-epic.md +0 -5
  582. package/prompts/collaboration-workflow/epic-audit.md +0 -5
  583. package/prompts/collaboration-workflow/epic-bundle.md +0 -5
  584. package/scripts/init/plugin-install-hint.ts +0 -47
  585. package/scripts/version/parse-json.ts +0 -13
  586. /package/scripts/{git → followup}/git-followup-stages.ts +0 -0
  587. /package/scripts/{git → gh}/git-gh-api-path.ts +0 -0
  588. /package/scripts/{git → gh}/git-gh-command.ts +0 -0
  589. /package/scripts/{git → gh}/git-gh-issue-write.ts +0 -0
  590. /package/scripts/{git → gh}/git-gh-issue.ts +0 -0
  591. /package/scripts/{git → gh}/git-gh-pr-auto-merge.ts +0 -0
  592. /package/scripts/{git → gh}/git-gh-pr-snapshot.ts +0 -0
  593. /package/scripts/{git → gh}/git-gh-reachability.ts +0 -0
  594. /package/scripts/{git → gh}/git-gh-repo.ts +0 -0
  595. /package/scripts/{git → gh}/git-gh-rest-state.ts +0 -0
  596. /package/scripts/{git → gh}/git-pr-checks-watch.ts +0 -0
  597. /package/scripts/{git → gh}/git-pr-error.ts +0 -0
  598. /package/scripts/{git → gh}/git-pr-messages.ts +0 -0
  599. /package/scripts/{git → gh}/github-issue-url.ts +0 -0
  600. /package/scripts/git/{stash-orphans.ts → stash/stash-orphans.ts} +0 -0
  601. /package/scripts/{git → issue}/issue-reference.ts +0 -0
  602. /package/scripts/{git → lib}/animation-helpers.ts +0 -0
  603. /package/scripts/{git → lib}/git-animation.ts +0 -0
  604. /package/scripts/{git → notify}/git-notify.ts +0 -0
  605. /package/scripts/{git → notify}/telegram-test-logic.ts +0 -0
@@ -9,13 +9,14 @@ import { epic_next } from '#scripts/epic/epic-next'
9
9
  import { epic_next_read, type EpicRead } from '#scripts/epic/epic-next-read'
10
10
  import type { EpicView } from '#scripts/epic/epic-next-views'
11
11
  import { epic_report, type EpicNextResult, type EpicVerdict } from '#scripts/epic/epic-report'
12
- import { epic_solo } from '#scripts/epic/epic-solo'
12
+ import { epic_solo_stale } from '#scripts/epic/epic-solo-stale'
13
13
  import { epic_triage, type TriageVerdict } from '#scripts/epic/epic-triage'
14
- import { git_gh_command } from '#scripts/git/git-gh-command'
14
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
15
15
  import { PROJECT_ROOT } from '#scripts/init/init-paths'
16
16
  import { issue_citation } from '#scripts/rules/issue-citation'
17
17
  import { backlog_defect_priority } from './backlog-defect-priority'
18
18
  import { backlog_pool } from './backlog-pool'
19
+ import { backlog_rank } from './backlog-rank'
19
20
 
20
21
  // `josh backlog:next` — what the whole opted-in backlog may run next (joshuafolkken/kit#1630).
21
22
  //
@@ -235,12 +236,15 @@ async function resolve(context: PoolContext): Promise<EpicNextResult | undefined
235
236
  return undefined
236
237
  }
237
238
 
238
- // The defect priority is applied here rather than when printing, so `backlog:plan` shows the order
239
- // the run takes (joshuafolkken/kit#2455).
240
- return await backlog_defect_priority.prioritize(
239
+ // The defect priority and the ranking keys are applied here rather than when printing, so
240
+ // `backlog:plan` shows the order the run takes (joshuafolkken/kit#2455, joshuafolkken/kit#2928). The
241
+ // ranking goes last: its sort is stable, so the defect order survives as its final key.
242
+ const ordered = await backlog_defect_priority.prioritize(
241
243
  combine(views_from(reads, context), context),
242
244
  context.repo,
243
245
  )
246
+
247
+ return backlog_rank.rank_result(ordered, context.repo)
244
248
  }
245
249
 
246
250
  // Whether the `error` verdict is really a transport failure wearing the graph's clothes.
@@ -274,13 +278,31 @@ function report_retry(context: PoolContext): number {
274
278
  return SUCCESS_EXIT_CODE
275
279
  }
276
280
 
281
+ // The rows the offer's cap bounds — the standalone half, which `backlog:plan --waves` reads too.
282
+ function standalone_keys(context: PoolContext): ReadonlySet<string> {
283
+ return backlog_pool.standalone_keys(context.opted_in.issues, {
284
+ tracked: epic_index.withheld_children(context.tracking.index, context.opted_in.issues),
285
+ exclude: context.exclude,
286
+ repo: context.repo,
287
+ })
288
+ }
289
+
277
290
  // The `run:solo` gate is applied here rather than in `resolve`, because `backlog:plan` shares
278
291
  // `resolve` and a plan made before the run lists everything (joshuafolkken/kit#2776). The holders are
279
- // read only on a `run` answer — the one answer the gate can change.
280
- async function gate_solo(result: EpicNextResult, repo: string): Promise<EpicNextResult> {
292
+ // read only on a `run` answer — the one answer the gate can change. The cap follows the gate
293
+ // (`backlog-rank.ts`, joshuafolkken/kit#2928).
294
+ async function gate_solo(result: EpicNextResult, context: PoolContext): Promise<EpicNextResult> {
281
295
  if (result.verdict !== 'run') return result
282
296
 
283
- const gated = epic_solo.gate(result, await epic_busy.read_repository(repo), repo)
297
+ const { repo } = context
298
+ const { read, notice } = await epic_solo_stale.release(
299
+ await epic_busy.read_repository(repo),
300
+ repo,
301
+ )
302
+
303
+ if (notice !== undefined) console.error(notice)
304
+
305
+ const gated = backlog_rank.gate(result, read, repo, standalone_keys(context))
284
306
 
285
307
  if (gated.notice !== undefined) console.error(gated.notice)
286
308
 
@@ -314,7 +336,7 @@ async function answer_pool(context: PoolContext): Promise<number> {
314
336
 
315
337
  if (untriaged.length > 0) return report_triage(context, untriaged)
316
338
 
317
- return report(await gate_solo(result, context.repo), context)
339
+ return report(await gate_solo(result, context), context)
318
340
  }
319
341
 
320
342
  // A backlog nobody has opted into answers `none` before either listing below is asked for.
@@ -395,22 +417,10 @@ const backlog_next = {
395
417
  RETRY_MESSAGE,
396
418
  READ_FAILURES,
397
419
  VERDICT_TOKENS,
398
- tokens_of,
399
- warn_epic_gap,
400
- warn_gaps,
401
- report,
402
- combine,
403
- views_from,
404
420
  resolve,
405
- is_transport_failure,
406
- report_retry,
407
- report_triage,
421
+ standalone_keys,
408
422
  context_of,
409
- answer_pool,
410
- report_none,
411
- answer,
412
423
  run,
413
- main,
414
424
  }
415
425
 
416
426
  if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
@@ -262,16 +262,11 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
262
262
 
263
263
  const backlog_offer_cli = {
264
264
  JSON_KEY,
265
- USAGE,
266
265
  budget_argv,
267
266
  counts_of,
268
- emit,
269
- main,
270
- mark_drain,
271
267
  next_argv,
272
268
  read_values,
273
269
  run,
274
- to_tokens,
275
270
  }
276
271
 
277
272
  if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url'
3
3
  import { auto_ok_cli } from '#scripts/auto-ok/auto-ok-cli'
4
4
  import { epic_index } from '#scripts/epic/epic-index'
5
5
  import type { EpicNextResult } from '#scripts/epic/epic-report'
6
- import { git_gh_command } from '#scripts/git/git-gh-command'
6
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
7
7
  import { read_json_listing } from '#scripts/git/parse-json-array'
8
8
  import { open_issue_schema, type OpenIssueData } from '#scripts/git/schemas'
9
9
  import { run_invocation } from '#scripts/run/run-invocation'
@@ -104,6 +104,9 @@ interface Plan {
104
104
  // past the listing cap. Carrying the *whole* index here instead would move that misreport rather
105
105
  // than remove it: the sentence would name an epic that is withholding nothing.
106
106
  tracked: ReadonlyMap<number, number>
107
+ // The rows the offer's cap bounds, so `--waves` cuts each wave where `backlog:next` does
108
+ // (joshuafolkken/kit#2928).
109
+ standalone: ReadonlySet<string>
107
110
  }
108
111
 
109
112
  // The listing plus whether it was cut. Carried together because the rows alone cannot say whether
@@ -138,6 +141,7 @@ async function classify(
138
141
  repo: context.repo,
139
142
  exclude,
140
143
  tracked: epic_index.withheld_children(context.tracking.index, context.opted_in.issues),
144
+ standalone: backlog_next.standalone_keys(context),
141
145
  }
142
146
  }
143
147
 
@@ -168,7 +172,7 @@ function print_plan(plan: Plan, listing: OpenListing, view: PlanView): void {
168
172
  const context = context_of(plan, listing)
169
173
 
170
174
  if (view.is_waves) {
171
- console.info(backlog_waves.format_waves(plan.result, context))
175
+ console.info(backlog_waves.format_waves(plan.result, context, plan.standalone))
172
176
 
173
177
  return
174
178
  }
@@ -259,13 +263,7 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
259
263
  const backlog_plan_cli = {
260
264
  OPEN_TRUNCATED_MESSAGE,
261
265
  OPEN_UNREADABLE_MESSAGE,
262
- USAGE,
263
266
  WAVES_WITH_NAMED_MESSAGE,
264
- classify,
265
- fetch_open,
266
- main,
267
- print_plan,
268
- report,
269
267
  run,
270
268
  }
271
269
 
@@ -3,7 +3,7 @@ import type { IssueReference } from '#scripts/epic/epic-reference'
3
3
  import { epic_report, type EpicNextResult } from '#scripts/epic/epic-report'
4
4
  import { epic_solo } from '#scripts/epic/epic-solo'
5
5
  import { epic_triage } from '#scripts/epic/epic-triage'
6
- import { has_label_name, IN_PROGRESS_LABEL, RUN_SOLO_LABEL } from '#scripts/git/issue-labels'
6
+ import { has_label_name, IN_PROGRESS_LABEL, RUN_SOLO_LABEL } from '#scripts/issue/issue-labels'
7
7
  import type { OutOfScopeRow } from './backlog-scope'
8
8
 
9
9
  // The plan a person reads before a `backlogrun` starts (joshuafolkken/kit#1652).
@@ -5,14 +5,14 @@ import { epic_index } from '#scripts/epic/epic-index'
5
5
  import { epic_issue } from '#scripts/epic/epic-issue'
6
6
  import type { EpicView } from '#scripts/epic/epic-next-views'
7
7
  import { epic_outside_blocker } from '#scripts/epic/epic-outside-blocker'
8
- import { git_next_issues } from '#scripts/git/git-next-issues'
8
+ import type { OpenIssueData } from '#scripts/git/schemas'
9
+ import { git_next_issues } from '#scripts/issue/git-next-issues'
9
10
  import {
10
11
  ALREADY_DONE_LABEL,
11
12
  EPIC_LABEL,
12
13
  has_any_label,
13
14
  NEEDS_DECISION_LABEL,
14
- } from '#scripts/git/issue-labels'
15
- import type { OpenIssueData } from '#scripts/git/schemas'
15
+ } from '#scripts/issue/issue-labels'
16
16
 
17
17
  // The candidate pool `josh backlog:next` answers from (joshuafolkken/kit#1630).
18
18
  //
@@ -37,6 +37,9 @@ const DECISION_LABELS: ReadonlySet<string> = new Set([NEEDS_DECISION_LABEL, ALRE
37
37
  // `tracked` is the narrowed set `epic_index.withheld_children` builds, not every child an epic
38
38
  // tracks (joshuafolkken/kit#1668) — the narrowing itself is defined there, once, for this half and
39
39
  // `auto-ok:next` alike.
40
+ // The part of the context that decides which rows the standalone half considers at all.
41
+ type ConsideredContext = Pick<StandaloneContext, 'tracked' | 'exclude' | 'repo'>
42
+
40
43
  interface StandaloneContext {
41
44
  tracked: ReadonlyMap<number, number>
42
45
  exclude: ReadonlyArray<number>
@@ -135,26 +138,28 @@ function standalone_rows(issues: ReadonlyArray<OpenIssueData>): ReadonlyArray<Op
135
138
  // are actually going to offer their children and applied here for the same reason. Which rows those
136
139
  // are is `epic_index.withheld_children`'s answer, not a second test made here. A row the caller
137
140
  // excluded is out of the pool entirely rather than merely unoffered: it has just merged.
138
- function is_considered(issue: OpenIssueData, context: StandaloneContext): boolean {
141
+ function is_considered(issue: OpenIssueData, context: ConsideredContext): boolean {
139
142
  return !context.tracked.has(issue.number) && !context.exclude.includes(issue.number)
140
143
  }
141
144
 
142
145
  // The standalone half, in the buckets the verdict is read from.
143
146
  //
144
- // The order is `git_next_issues.prioritize`'s and not a second one — the same ranking the
145
- // `🗒 Next issues` display and `auto-ok:next` already answer with, cap included, so the three cannot
146
- // disagree about what comes next. Anything it withholds is still open work: `needs-decision` is
147
- // waiting on a person, and everything else — a standing blocker, a run already in progress, a row
148
- // past the cap — resolves on its own and is waiting on time. **The cap is one of those**: a sixth
149
- // runnable standalone issue is reported as waiting rather than offered, and the next ask offers it
150
- // once one of the five above it merges, so the backlog drains without it ever being lost. That is
151
- // the cost of having one ranking rather than two, and `docs/josh-commands.md` states it.
147
+ // The order is `git_next_issues.order`'s and not a second one — the same ranking the
148
+ // `🗒 Next issues` display and `auto-ok:next` already answer with, so the three cannot disagree about
149
+ // what comes next. Anything it withholds is still open work: `needs-decision` is waiting on a person,
150
+ // and everything else — a standing blocker, a run already in progress — resolves on its own and is
151
+ // waiting on time.
152
+ //
153
+ // **The display's cap is not applied here** (joshuafolkken/kit#2928). Cut at this point, a sixth
154
+ // runnable `run:solo` issue never reached the gate that holds back what is ranked below it, so the
155
+ // cap is applied after that gate instead (`backlog-rank.ts`). A row past it is still reported as
156
+ // waiting and offered by a later ask, so the backlog drains without it ever being lost.
152
157
  function classify_standalone(
153
158
  issues: ReadonlyArray<OpenIssueData>,
154
159
  context: StandaloneContext,
155
160
  ): Classification {
156
161
  const considered = issues.filter((issue) => is_considered(issue, context))
157
- const runnable = git_next_issues.prioritize(
162
+ const runnable = git_next_issues.order(
158
163
  considered.filter((issue) => auto_ok_cli.is_runnable(issue, context.tracked, context.exclude)),
159
164
  )
160
165
  const offered = new Set(runnable.map((issue) => issue.number))
@@ -301,22 +306,34 @@ function merge_classifications(left: Classification, right: Classification): Cla
301
306
  }
302
307
  }
303
308
 
309
+ // The keys of the rows the standalone half considers — the rows the offer's cap bounds. An issue an
310
+ // opted-in epic tracks is that epic's child here, and never capped, exactly as before.
311
+ function standalone_keys(
312
+ issues: ReadonlyArray<OpenIssueData>,
313
+ context: ConsideredContext,
314
+ ): ReadonlySet<string> {
315
+ return keys_of(
316
+ to_children(
317
+ standalone_rows(issues).filter((issue) => is_considered(issue, context)),
318
+ context.repo,
319
+ ),
320
+ )
321
+ }
322
+
304
323
  const backlog_pool = {
305
324
  classify_standalone,
306
325
  cross_epic_cycles,
307
326
  running_set,
308
327
  settle_standalone,
328
+ standalone_keys,
309
329
  drop_excluded,
310
330
  epic_classification,
311
- is_epic_row,
312
- keys_of,
313
331
  merge_classifications,
314
332
  opted_in_epics,
315
333
  standalone_rows,
316
334
  to_child,
317
335
  to_children,
318
- unique_children,
319
336
  }
320
337
 
321
338
  export { backlog_pool }
322
- export type { StandaloneContext }
339
+ export type { ConsideredContext, StandaloneContext }
@@ -0,0 +1,106 @@
1
+ import type { BusyRead } from '#scripts/epic/epic-busy'
2
+ import { epic_graph, type EpicChild } from '#scripts/epic/epic-graph'
3
+ import { epic_rank } from '#scripts/epic/epic-rank'
4
+ import type { EpicNextResult } from '#scripts/epic/epic-report'
5
+ import { epic_solo, type SoloGate, type SoloSelection } from '#scripts/epic/epic-solo'
6
+ import { git_next_issues } from '#scripts/issue/git-next-issues'
7
+
8
+ // What the backlog offers, in the order it is decided (joshuafolkken/kit#2928): rank, then the
9
+ // `run:solo` gate, then the cap. `backlog:next` and `backlog:plan --waves` both call `select`, so the
10
+ // plan's first wave is what the run starts into an idle repository rather than an order the run never
11
+ // takes.
12
+ //
13
+ // **The cap comes last.** It used to be applied while the standalone half was classified, before the
14
+ // gate, so a `run:solo` issue ranked sixth or lower was cut before the gate could see it — and the
15
+ // issues below it were offered past it. Cut after the gate, the cap only ever removes what the gate
16
+ // would have offered, so it cannot hide a waiting `run:solo` issue from it. It still bounds only the
17
+ // standalone rows, as it always has; an epic's children were never capped.
18
+
19
+ // Every child the classification placed, in the order it placed them: the ranked candidates first,
20
+ // then the waiting ones, then the ones waiting on a person.
21
+ function everything_in(result: EpicNextResult): ReadonlyArray<EpicChild> {
22
+ const candidates = result.candidates.flatMap((bundle) => bundle.children)
23
+
24
+ return [...candidates, ...result.waiting, ...result.blocked_on_people]
25
+ }
26
+
27
+ // The ranking is `epic_rank.rank`, shared with a named epic's lanes.
28
+ const { rank } = epic_rank
29
+
30
+ // This repository's candidates re-ranked inside the result, so `backlog:plan` prints the order the
31
+ // offer is decided in.
32
+ function rank_result(result: EpicNextResult, repo: string): EpicNextResult {
33
+ if (result.verdict !== 'run') return result
34
+
35
+ const pool = everything_in(result)
36
+
37
+ return {
38
+ ...result,
39
+ candidates: result.candidates.map((bundle) =>
40
+ bundle.repo === repo ? { ...bundle, children: rank(bundle.children, pool) } : bundle,
41
+ ),
42
+ }
43
+ }
44
+
45
+ // The standalone rows past the cap, in offer order.
46
+ function overflow_of(
47
+ offered: ReadonlyArray<EpicChild>,
48
+ standalone: ReadonlySet<string>,
49
+ ): ReadonlySet<string> {
50
+ const keys = offered.map((child) => epic_graph.key_of(child)).filter((key) => standalone.has(key))
51
+
52
+ return new Set(keys.slice(git_next_issues.DISPLAY_LIMIT))
53
+ }
54
+
55
+ function is_in(child: EpicChild, keys: ReadonlySet<string>): boolean {
56
+ return keys.has(epic_graph.key_of(child))
57
+ }
58
+
59
+ function cap(selection: SoloSelection, standalone: ReadonlySet<string>): SoloSelection {
60
+ const overflow = overflow_of(selection.offered, standalone)
61
+
62
+ if (overflow.size === 0) return selection
63
+
64
+ return {
65
+ ...selection,
66
+ offered: selection.offered.filter((child) => !is_in(child, overflow)),
67
+ withheld: [
68
+ ...selection.offered.filter((child) => is_in(child, overflow)),
69
+ ...selection.withheld,
70
+ ],
71
+ }
72
+ }
73
+
74
+ interface OfferInput {
75
+ candidates: ReadonlyArray<EpicChild>
76
+ pool: ReadonlyArray<EpicChild>
77
+ read: BusyRead
78
+ repo: string
79
+ // The keys of the standalone rows — the ones the cap bounds.
80
+ standalone: ReadonlySet<string>
81
+ }
82
+
83
+ function select(input: OfferInput): SoloSelection {
84
+ const ranked = rank(input.candidates, input.pool)
85
+
86
+ return cap(epic_solo.select(ranked, input.read, input.repo), input.standalone)
87
+ }
88
+
89
+ // `backlog:next`'s gate: `epic_solo.gate` with this selection in place of its bare one.
90
+ function gate(
91
+ result: EpicNextResult,
92
+ read: BusyRead,
93
+ repo: string,
94
+ standalone: ReadonlySet<string>,
95
+ ): SoloGate {
96
+ const pool = everything_in(result)
97
+
98
+ return epic_solo.gate(result, read, repo, (candidates) =>
99
+ select({ candidates, pool, read, repo, standalone }),
100
+ )
101
+ }
102
+
103
+ const backlog_rank = { everything_in, gate, rank_result, select }
104
+
105
+ export { backlog_rank }
106
+ export type { OfferInput }
@@ -21,6 +21,13 @@ interface ReadyPorts {
21
21
  ready_issues: () => Promise<ReadonlyArray<string>>
22
22
  }
23
23
 
24
+ async function free_under(limit: number): Promise<number> {
25
+ const lanes = await lane_registry.list_lanes()
26
+ const live = lanes.filter((lane) => !lane.is_stranded).length
27
+
28
+ return lane_capacity.free_lanes(limit, live)
29
+ }
30
+
24
31
  // Free lanes right now: the limit less the live lanes. An unreadable limit reads as no free lane — the
25
32
  // safe direction, since both the stall and the ready line need a free lane and reporting one that is
26
33
  // not there is the false positive neither may raise.
@@ -35,10 +42,18 @@ async function free_lane_count(): Promise<number> {
35
42
 
36
43
  if (limit.kind !== 'limit') return NO_FREE
37
44
 
38
- const lanes = await lane_registry.list_lanes()
39
- const live = lanes.filter((lane) => !lane.is_stranded).length
45
+ return await free_under(limit.limit)
46
+ }
47
+
48
+ // **The drive's count throws on an unreadable limit** (joshuafolkken/kit#3027). The drive launches no
49
+ // more than this count, so the safe-direction zero above would leave it launching nothing and reporting
50
+ // nothing for good; a bad setting is the hard error `lane_capacity` says it is, ending the drive loudly.
51
+ async function drive_free_lane_count(): Promise<number> {
52
+ const limit = lane_capacity.lane_limit()
53
+
54
+ if (limit.kind !== 'limit') throw new Error(limit.problem)
40
55
 
41
- return lane_capacity.free_lanes(limit.limit, live)
56
+ return await free_under(limit.limit)
42
57
  }
43
58
 
44
59
  // **A `--only` run has no pool to drain** (joshuafolkken/kit#2472). `backlog:next` lists the opted-in
@@ -135,6 +150,7 @@ const backlog_ready = {
135
150
  read_backlog_next,
136
151
  print_offer_hint,
137
152
  free_lane_count,
153
+ drive_free_lane_count,
138
154
  drains_pool,
139
155
  print_ready_line,
140
156
  read_ready,
@@ -1,6 +1,6 @@
1
1
  import type { EpicNextResult } from '#scripts/epic/epic-report'
2
- import { AUTO_OK_LABEL, EPIC_LABEL, has_any_label } from '#scripts/git/issue-labels'
3
2
  import type { OpenIssueData } from '#scripts/git/schemas'
3
+ import { AUTO_OK_LABEL, EPIC_LABEL, has_any_label } from '#scripts/issue/issue-labels'
4
4
 
5
5
  // Which open issues the backlog will not run, and why (joshuafolkken/kit#1652).
6
6
  //
@@ -135,8 +135,6 @@ const backlog_scope = {
135
135
  OPTED_IN_UNPLACED_REASON,
136
136
  open_numbers_of,
137
137
  out_of_scope,
138
- planned_numbers,
139
- reason_for,
140
138
  titles_of,
141
139
  }
142
140
 
@@ -1,4 +1,4 @@
1
- import { telegram_notify } from '#scripts/git/telegram-notify'
1
+ import { telegram_notify } from '#scripts/notify/telegram-notify'
2
2
  import { run_event_stream, type RunEvent } from '#scripts/run/run-event-stream'
3
3
  import { run_event_stream_emit } from '#scripts/run/run-event-stream-emit'
4
4
  import { backlog_ready, type ReadyPorts } from './backlog-ready'
@@ -137,7 +137,7 @@ async function run_stall_check(ready: ReadyPorts = backlog_ready.DEFAULT_PORTS):
137
137
  }
138
138
  }
139
139
 
140
- const backlog_stalled_detect = { DEFAULT_PORTS, detect_and_report, gather, report, run_stall_check }
140
+ const backlog_stalled_detect = { detect_and_report, run_stall_check }
141
141
 
142
142
  export { backlog_stalled_detect }
143
143
  export type { DetectPorts }
@@ -118,7 +118,6 @@ const backlog_stalled = {
118
118
  count_ready_tokens,
119
119
  describe,
120
120
  dispatch_age_ms,
121
- is_stalled,
122
121
  needs_triage,
123
122
  ready_tokens,
124
123
  }
@@ -4,9 +4,13 @@ import { epic_cross_repo } from '#scripts/epic/epic-cross-repo'
4
4
  import { epic_graph, type EpicChild } from '#scripts/epic/epic-graph'
5
5
  import { epic_outside_blocker } from '#scripts/epic/epic-outside-blocker'
6
6
  import { epic_report, type EpicNextResult } from '#scripts/epic/epic-report'
7
- import { epic_solo } from '#scripts/epic/epic-solo'
8
- import { has_label_name, IN_PROGRESS_LABEL, NEEDS_DECISION_LABEL } from '#scripts/git/issue-labels'
7
+ import {
8
+ has_label_name,
9
+ IN_PROGRESS_LABEL,
10
+ NEEDS_DECISION_LABEL,
11
+ } from '#scripts/issue/issue-labels'
9
12
  import { backlog_plan, type PlanContext } from './backlog-plan'
13
+ import { backlog_rank } from './backlog-rank'
10
14
 
11
15
  // The order a `backlogrun` takes, wave by wave, before it starts (joshuafolkken/kit#2778).
12
16
  //
@@ -14,13 +18,13 @@ import { backlog_plan, type PlanContext } from './backlog-plan'
14
18
  // made from, so the order past the first answer was left for a person to work out by hand. This plays
15
19
  // the asks forward under one assumption: **every issue of a wave merges before the next wave starts**.
16
20
  //
17
- // **No rule is written twice.** The first wave is `epic_solo.select` over the very candidates
18
- // `backlog:next` resolves, asked of an idle repository — so it is what `backlog:next` prints when
19
- // nothing is running. Each later wave marks the earlier waves closed and re-classifies the same pool
21
+ // **No rule is written twice.** The first wave is `backlog_rank.select` — rank, `run:solo` gate, cap —
22
+ // over the very candidates `backlog:next` resolves, asked of an idle repository — so it is what
23
+ // `backlog:next` prints when nothing is running (joshuafolkken/kit#2928). Each later wave marks the earlier waves closed and re-classifies the same pool
20
24
  // through `epic_classify.classify_children` with the resolver `epic:next` sorted it with the first
21
25
  // time — `epic_cross_repo.resolve_cross_repo`, so a blocker in another repository still waits for its
22
26
  // release rather than counting as done on close — then puts the runnable set through
23
- // `epic_solo.select` again.
27
+ // `backlog_rank.select` again.
24
28
 
25
29
  const IDLE: BusyRead = { kind: 'idle' }
26
30
  const CLOSED = 'CLOSED'
@@ -45,14 +49,6 @@ function is_running(child: EpicChild): boolean {
45
49
  return has_label_name(child.labels, IN_PROGRESS_LABEL)
46
50
  }
47
51
 
48
- // Every child the classification placed, in the order it placed them: the ranked candidates first,
49
- // then the waiting ones, then the ones waiting on a person.
50
- function everything_in(result: EpicNextResult): ReadonlyArray<EpicChild> {
51
- const candidates = result.candidates.flatMap((bundle) => bundle.children)
52
-
53
- return [...candidates, ...result.waiting, ...result.blocked_on_people]
54
- }
55
-
56
52
  function as_closed(child: EpicChild, done: ReadonlySet<string>): EpicChild {
57
53
  return done.has(epic_graph.key_of(child)) ? { ...child, state: CLOSED } : child
58
54
  }
@@ -63,24 +59,35 @@ interface WavePool {
63
59
  children: ReadonlyArray<EpicChild>
64
60
  running: ReadonlySet<string>
65
61
  repo: string
62
+ standalone: ReadonlySet<string>
66
63
  }
67
64
 
68
- function pool_of(result: EpicNextResult, repo: string): WavePool {
69
- const all = everything_in(result)
65
+ function pool_of(result: EpicNextResult, repo: string, standalone: ReadonlySet<string>): WavePool {
66
+ const all = backlog_rank.everything_in(result)
70
67
 
71
68
  return {
72
69
  children: all.filter((child) => !is_running(child)),
73
70
  running: epic_outside_blocker.running_keys(all),
74
71
  repo,
72
+ standalone,
75
73
  }
76
74
  }
77
75
 
78
- function offer(runnable: ReadonlyArray<EpicChild>, repo: string): ReadonlyArray<EpicChild> {
79
- return epic_solo.select(runnable, IDLE, repo).offered
76
+ function offer(
77
+ candidates: ReadonlyArray<EpicChild>,
78
+ children: ReadonlyArray<EpicChild>,
79
+ pool: WavePool,
80
+ ): ReadonlyArray<EpicChild> {
81
+ const { repo, standalone } = pool
82
+
83
+ return backlog_rank.select({ candidates, pool: children, read: IDLE, repo, standalone }).offered
80
84
  }
81
85
 
82
- function first_wave(result: EpicNextResult, repo: string): ReadonlyArray<EpicChild> {
83
- return offer(epic_report.candidates_for_repo(result, repo), repo)
86
+ // Ranked against the whole result, exactly as `backlog:next` ranks it, so the two agree.
87
+ function first_wave(result: EpicNextResult, pool: WavePool): ReadonlyArray<EpicChild> {
88
+ const candidates = epic_report.candidates_for_repo(result, pool.repo)
89
+
90
+ return offer(candidates, backlog_rank.everything_in(result), pool)
84
91
  }
85
92
 
86
93
  function next_wave(pool: WavePool, done: ReadonlySet<string>): ReadonlyArray<EpicChild> {
@@ -93,25 +100,31 @@ function next_wave(pool: WavePool, done: ReadonlySet<string>): ReadonlyArray<Epi
93
100
 
94
101
  return offer(
95
102
  runnable.filter((child) => child.repo === pool.repo),
96
- pool.repo,
103
+ children,
104
+ pool,
97
105
  )
98
106
  }
99
107
 
100
108
  // Each wave is closed before the next is asked for; a wave that offers nothing ends the plan, which
101
- // the pool's size bounds because every wave closes at least one child.
102
- function build(result: EpicNextResult, repo: string): WavePlan {
103
- const pool = pool_of(result, repo)
109
+ // the pool's size bounds because every wave closes at least one child. `standalone` names the rows the
110
+ // offer's cap bounds; left empty, no wave is capped.
111
+ function build(
112
+ result: EpicNextResult,
113
+ repo: string,
114
+ standalone: ReadonlySet<string> = new Set(),
115
+ ): WavePlan {
116
+ const pool = pool_of(result, repo, standalone)
104
117
  const done = new Set<string>()
105
118
  const waves: Array<ReadonlyArray<EpicChild>> = []
106
119
 
107
- for (let wave = first_wave(result, repo); wave.length > 0; wave = next_wave(pool, done)) {
120
+ for (let wave = first_wave(result, pool); wave.length > 0; wave = next_wave(pool, done)) {
108
121
  waves.push(wave)
109
122
  for (const child of wave) done.add(epic_graph.key_of(child))
110
123
  }
111
124
 
112
- const unreached = everything_in(result).filter(
113
- (child) => child.repo === repo && !done.has(epic_graph.key_of(child)),
114
- )
125
+ const unreached = backlog_rank
126
+ .everything_in(result)
127
+ .filter((child) => child.repo === repo && !done.has(epic_graph.key_of(child)))
115
128
 
116
129
  return { waves, unreached }
117
130
  }
@@ -139,10 +152,14 @@ function unreached_lines(plan: WavePlan, context: PlanContext): Array<string> {
139
152
  )
140
153
  }
141
154
 
142
- function format_waves(result: EpicNextResult, context: PlanContext): string {
155
+ function format_waves(
156
+ result: EpicNextResult,
157
+ context: PlanContext,
158
+ standalone: ReadonlySet<string> = new Set(),
159
+ ): string {
143
160
  if (result.verdict === 'error') return backlog_plan.format_unusable(result)
144
161
 
145
- const plan = build(result, context.repo)
162
+ const plan = build(result, context.repo, standalone)
146
163
  const waves = plan.waves.map((wave, index) => wave_line(wave, index, context.repo))
147
164
 
148
165
  return [
@@ -158,7 +175,6 @@ function format_waves(result: EpicNextResult, context: PlanContext): string {
158
175
  const backlog_waves = {
159
176
  HEADING_ASSUMPTION,
160
177
  NEEDS_DECISION_NOTE,
161
- NOT_REACHED_NOTE,
162
178
  PARALLEL_NOTE,
163
179
  UNREACHED_HEADING,
164
180
  build,
@@ -81,7 +81,7 @@ function run(cwd: string = process.cwd()): number {
81
81
  return file === undefined ? report_no_transcript() : check_session(file)
82
82
  }
83
83
 
84
- const behavior_cli = { LABEL, check_session, current_session, run }
84
+ const behavior_cli = { check_session, run }
85
85
 
86
86
  if (process.argv[1] === fileURLToPath(import.meta.url)) process.exitCode = run()
87
87