@joshuafolkken/kit 1.1050.0 → 1.1128.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 (630) hide show
  1. package/.claude/settings.json +9 -8
  2. package/.claude/skills/epic-commands/SKILL.md +5 -7
  3. package/.claude/skills/workflow-commands/SKILL.md +20 -12
  4. package/.claude/skills/workflow-commands/background-commands.md +36 -84
  5. package/.claude/skills/workflow-commands/backlogrun-child.md +1 -1
  6. package/.claude/skills/workflow-commands/backlogrun-lanes.md +8 -6
  7. package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -33
  8. package/.claude/skills/workflow-commands/backlogrun-steps.md +12 -12
  9. package/.claude/skills/workflow-commands/backlogrun.md +2 -2
  10. package/.claude/skills/workflow-commands/chain-rule.md +4 -2
  11. package/.claude/skills/workflow-commands/delegation.md +3 -3
  12. package/.claude/skills/workflow-commands/followup-reference.md +1 -1
  13. package/.claude/skills/workflow-commands/followup.md +7 -3
  14. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -2
  15. package/.claude/skills/workflow-commands/fullrun.md +8 -6
  16. package/.claude/skills/workflow-commands/halfrun.md +19 -21
  17. package/.claude/skills/workflow-commands/issue-comments.md +1 -1
  18. package/.claude/skills/workflow-commands/issue-fold-existing.md +10 -10
  19. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  20. package/.claude/skills/workflow-commands/kickoff.md +9 -4
  21. package/.claude/skills/workflow-commands/latest-gate.md +8 -16
  22. package/.claude/skills/workflow-commands/needs-human-review.md +1 -1
  23. package/.claude/skills/workflow-commands/observation-filing.md +3 -3
  24. package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
  25. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  26. package/.claude/skills/workflow-commands/prrun.md +36 -0
  27. package/.claude/skills/workflow-commands/retrospective.md +1 -1
  28. package/.claude/skills/workflow-commands/split-assessment.md +4 -18
  29. package/.claude/skills/workflow-commands/working-tree-hold.md +22 -2
  30. package/.codex/hooks.json +16 -20
  31. package/.github/actions/setup-node/action.yml +33 -0
  32. package/.github/actions/setup-pnpm/action.yml +93 -0
  33. package/.github/dependabot.yml +5 -1
  34. package/.github/workflows/auto-tag.yml +2 -3
  35. package/.github/workflows/ci.yml +31 -134
  36. package/.github/workflows/dependabot-auto-merge.yml +1 -0
  37. package/.github/workflows/pr-classification.yml +6 -44
  38. package/.github/workflows/production.yml +1 -0
  39. package/.github/workflows/publish.yml +43 -47
  40. package/.github/workflows/sonar-qube.yml +41 -4
  41. package/.pnpmfile.mjs +29 -0
  42. package/AGENTS.md +5 -2
  43. package/CLAUDE.md +33 -27
  44. package/GEMINI.md +5 -2
  45. package/README.md +66 -20
  46. package/SECURITY.md +37 -6
  47. package/cspell/index.yaml +6 -1
  48. package/dist/CLAUDE.basic.md +6 -6
  49. package/dist/CLAUDE.md +33 -27
  50. package/dist/CLAUDE.static.md +6 -6
  51. package/dist/config-merge/index.js +48 -12
  52. package/dist/hooks/batch-guard.js +4 -4
  53. package/dist/hooks/{chunk-7VPDUBRN.js → chunk-4JZDP5X5.js} +152 -155
  54. package/dist/hooks/{chunk-HJIISEAC.js → chunk-5SD2YM7Z.js} +5 -13
  55. package/dist/hooks/{chunk-NAS4C7GP.js → chunk-77J7IEQM.js} +2 -2
  56. package/dist/hooks/{chunk-DMQN45FN.js → chunk-7HVB7SZG.js} +3 -3
  57. package/dist/hooks/{chunk-RLDX5SOC.js → chunk-AIOIOMPX.js} +4413 -1970
  58. package/dist/hooks/{chunk-5BK3K7V4.js → chunk-M7QKEKWO.js} +873 -2049
  59. package/dist/hooks/{chunk-WQ4OSBSS.js → chunk-MKVGBQPA.js} +2 -2
  60. package/dist/hooks/{chunk-EJDX2TOB.js → chunk-NXYMNPUQ.js} +7 -88
  61. package/dist/hooks/{chunk-NJQOBE6L.js → chunk-QVLT52OV.js} +2 -7
  62. package/dist/hooks/{chunk-GQ7KP45M.js → chunk-XYUIQOW3.js} +2 -2
  63. package/dist/hooks/codex-hook-adapter.js +12 -48
  64. package/dist/hooks/duplicate-read-guard.js +5 -5
  65. package/dist/hooks/format-edited.js +2 -2
  66. package/dist/hooks/inputs.json +280 -0
  67. package/dist/hooks/investigation-guard.js +5 -5
  68. package/dist/hooks/pretool-guard.js +9 -9
  69. package/dist/hooks/session-lang.js +0 -1
  70. package/dist/hooks/stop-guard.js +192 -333
  71. package/dist/josh.js +240 -148
  72. package/dist/managed-marker/index.js +2 -1
  73. package/dist/version/index.js +70 -78
  74. package/eslint/rules/promise.js +5 -0
  75. package/eslint/rules/unicorn.js +3 -2
  76. package/package.json +26 -5
  77. package/pnpm-workspace.yaml +5 -7
  78. package/prompts/collaboration-workflow/glossary.md +17 -0
  79. package/prompts/collaboration-workflow/issue-citation.md +5 -5
  80. package/prompts/collaboration-workflow/issue-template.md +1 -1
  81. package/prompts/collaboration-workflow/operating-rules.md +5 -5
  82. package/prompts/collaboration-workflow/overview.md +13 -1
  83. package/prompts/collaboration-workflow/principles.md +9 -0
  84. package/prompts/collaboration-workflow/report-format.md +1 -1
  85. package/prompts/collaboration-workflow/residency.md +3 -3
  86. package/prompts/collaboration-workflow/rule-delivery.md +1 -1
  87. package/prompts/collaboration-workflow/turn-batching.md +3 -3
  88. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  89. package/prompts/collaboration-workflow/wip-cap.md +25 -22
  90. package/prompts/collaboration-workflow.md +6 -8
  91. package/prompts/review-rubric.md +1 -1
  92. package/prompts/review.md +3 -3
  93. package/prompts/sonar-hotspot-handling.md +11 -3
  94. package/prompts/testing-guide.md +3 -3
  95. package/scripts/adopt/adopt-toolkits.ts +0 -2
  96. package/scripts/adopt/adopt.ts +0 -2
  97. package/scripts/agent/agent-argv.ts +0 -2
  98. package/scripts/agent/agent-diagnostics.ts +1 -1
  99. package/scripts/agent/agent-role-profile.ts +0 -7
  100. package/scripts/agent/claude-agent-argv.ts +0 -2
  101. package/scripts/agent/codex-agent-argv.ts +1 -1
  102. package/scripts/agent/codex-hooks.ts +92 -0
  103. package/scripts/agent/interactive-ask.ts +1 -1
  104. package/scripts/auto-ok/auto-ok-cli.ts +6 -16
  105. package/scripts/backlog/backlog-arrival.ts +30 -10
  106. package/scripts/backlog/backlog-budget-cli.ts +15 -16
  107. package/scripts/backlog/backlog-budget.ts +0 -2
  108. package/scripts/backlog/backlog-defect-priority.ts +2 -2
  109. package/scripts/backlog/backlog-drive-cli.ts +10 -8
  110. package/scripts/backlog/backlog-drive-named-offer.ts +16 -3
  111. package/scripts/backlog/backlog-drive-named.ts +2 -1
  112. package/scripts/backlog/backlog-drive.ts +76 -16
  113. package/scripts/backlog/backlog-named.ts +0 -3
  114. package/scripts/backlog/backlog-next.ts +9 -15
  115. package/scripts/backlog/backlog-offer-cli.ts +6 -19
  116. package/scripts/backlog/backlog-plan-cli.ts +1 -7
  117. package/scripts/backlog/backlog-plan.ts +30 -19
  118. package/scripts/backlog/backlog-pool.ts +3 -6
  119. package/scripts/backlog/backlog-rank.ts +2 -2
  120. package/scripts/backlog/backlog-ready.ts +39 -10
  121. package/scripts/backlog/backlog-scope.ts +1 -3
  122. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  123. package/scripts/backlog/backlog-stalled.ts +0 -1
  124. package/scripts/backlog/backlog-waves.ts +5 -2
  125. package/scripts/behavior/behavior-cli.ts +1 -1
  126. package/scripts/build/build-codex-hooks.ts +26 -0
  127. package/scripts/build/build-hooks.ts +85 -9
  128. package/scripts/build/hook-bundle-stamp.ts +111 -0
  129. package/scripts/cases/cases-cli.ts +1 -1
  130. package/scripts/cases/cases-logic.ts +1 -1
  131. package/scripts/ci/composite-actions.ts +30 -0
  132. package/scripts/ci/pr-classification.ts +3 -3
  133. package/scripts/claude/workflow-destination.ts +10 -1
  134. package/scripts/clone/clone-fingerprint.ts +0 -1
  135. package/scripts/clone/clone-scan-cli.ts +1 -1
  136. package/scripts/clone/clone-scan.ts +1 -1
  137. package/scripts/config-merge/json-format.ts +0 -1
  138. package/scripts/config-merge/parse-jsonc.ts +33 -3
  139. package/scripts/config-merge/patch-json-key.ts +13 -6
  140. package/scripts/config-merge/patch-yaml-list.ts +1 -1
  141. package/scripts/cost-runtime/codex-usage.ts +2 -9
  142. package/scripts/cost-runtime/cost-attribute.ts +0 -1
  143. package/scripts/cost-runtime/cost-cli.ts +3 -10
  144. package/scripts/cost-runtime/cost-corpus.ts +0 -2
  145. package/scripts/cost-runtime/cost-format.ts +0 -3
  146. package/scripts/cost-runtime/cost-pricing.ts +0 -1
  147. package/scripts/cost-runtime/cost-tokens.ts +0 -1
  148. package/scripts/delegation/delegation-cli.ts +1 -1
  149. package/scripts/delegation/delegation-policy.ts +0 -1
  150. package/scripts/delegation/duplicate-read-guard.ts +0 -8
  151. package/scripts/delegation/fanout-cli.ts +1 -1
  152. package/scripts/delegation/fanout.ts +0 -2
  153. package/scripts/delegation/investigation-reads.ts +1 -164
  154. package/scripts/discovery/repo-map-logic.ts +0 -1
  155. package/scripts/discovery/repo-party-cli.ts +1 -1
  156. package/scripts/doctor/doctor-consumer.ts +6 -10
  157. package/scripts/doctor/doctor-io.ts +9 -13
  158. package/scripts/doctor/doctor-logic.ts +0 -4
  159. package/scripts/doctor/doctor.ts +14 -9
  160. package/scripts/document/backlogrun-parent-read-set.ts +12 -1
  161. package/scripts/document/document-byte-budget.ts +5 -5
  162. package/scripts/document/document-byte-check.ts +0 -1
  163. package/scripts/document/document-read-cli.ts +0 -2
  164. package/scripts/document/document-section-cli.ts +1 -1
  165. package/scripts/document/document-section.ts +0 -3
  166. package/scripts/document/edit-files-cli.ts +0 -1
  167. package/scripts/document/entry-read-budget.ts +21 -7
  168. package/scripts/document/entry-read-set.ts +32 -8
  169. package/scripts/document/lane-child-read-set.ts +8 -6
  170. package/scripts/document/read-files-cli.ts +0 -4
  171. package/scripts/document/read-set-cli.ts +0 -2
  172. package/scripts/document/read-set-point-of-use.ts +9 -3
  173. package/scripts/document/read-set-pricing.ts +0 -1
  174. package/scripts/document/read-set-trim.ts +4 -2
  175. package/scripts/epic/epic-add.ts +1 -1
  176. package/scripts/epic/epic-audit-cli.ts +1 -4
  177. package/scripts/epic/epic-audit-orphans.ts +1 -3
  178. package/scripts/epic/epic-audit-rationale.ts +0 -1
  179. package/scripts/epic/epic-audit-report.ts +0 -2
  180. package/scripts/epic/epic-audit.ts +8 -22
  181. package/scripts/epic/epic-bundle-cli.ts +1 -1
  182. package/scripts/epic/epic-bundle-evidence.ts +0 -1
  183. package/scripts/epic/epic-bundle-referenced.ts +1 -5
  184. package/scripts/epic/epic-bundle.ts +0 -4
  185. package/scripts/epic/epic-busy.ts +4 -5
  186. package/scripts/epic/epic-candidate-confirm.ts +0 -5
  187. package/scripts/epic/epic-classify.ts +2 -2
  188. package/scripts/epic/epic-cli-argv.ts +0 -1
  189. package/scripts/epic/epic-close-comment.ts +1 -1
  190. package/scripts/epic/epic-close.ts +5 -2
  191. package/scripts/epic/epic-cross-repo.ts +2 -4
  192. package/scripts/epic/epic-decision.ts +0 -1
  193. package/scripts/epic/epic-fetch.ts +2 -2
  194. package/scripts/epic/epic-index.ts +2 -3
  195. package/scripts/epic/epic-issue.ts +1 -15
  196. package/scripts/epic/epic-lane-offer.ts +9 -8
  197. package/scripts/epic/epic-nested.ts +1 -1
  198. package/scripts/epic/epic-next-read.ts +1 -3
  199. package/scripts/epic/epic-next-views.ts +0 -1
  200. package/scripts/epic/epic-next.ts +1 -10
  201. package/scripts/epic/epic-parse.ts +0 -1
  202. package/scripts/epic/epic-promote.ts +0 -9
  203. package/scripts/epic/epic-rank.ts +2 -2
  204. package/scripts/epic/epic-read.ts +1 -2
  205. package/scripts/epic/epic-reconcile.ts +1 -1
  206. package/scripts/epic/epic-reference.ts +1 -13
  207. package/scripts/epic/epic-relation-recheck.ts +1 -1
  208. package/scripts/epic/epic-relations.ts +1 -1
  209. package/scripts/epic/epic-remove.ts +1 -1
  210. package/scripts/epic/epic-report.ts +0 -1
  211. package/scripts/epic/epic-run.ts +2 -3
  212. package/scripts/epic/epic-shape.ts +1 -1
  213. package/scripts/epic/epic-solo-stale.ts +107 -0
  214. package/scripts/epic/epic-solo.ts +3 -2
  215. package/scripts/epic/epic-triage.ts +1 -1
  216. package/scripts/epic/epic-validate.ts +1 -1
  217. package/scripts/epic/epic.ts +1 -1
  218. package/scripts/exports/unused-members-cli.ts +53 -0
  219. package/scripts/{git → followup}/followup-issue-number.ts +1 -6
  220. package/scripts/{git → followup}/git-followup-cleanup.ts +2 -1
  221. package/scripts/{git → followup}/git-followup-finish.ts +1 -3
  222. package/scripts/{git → followup}/git-followup-flush.ts +2 -2
  223. package/scripts/{git → followup}/git-followup-issue-close.ts +2 -2
  224. package/scripts/{git → followup}/git-followup-label.ts +4 -4
  225. package/scripts/followup/git-followup-merged.ts +112 -0
  226. package/scripts/{git → followup}/git-followup-pending.ts +1 -1
  227. package/scripts/{git → followup}/git-followup-workflow.ts +19 -11
  228. package/scripts/{git → followup}/git-pr-followup-wrapup.ts +9 -3
  229. package/scripts/{git → followup}/git-pr-followup.ts +53 -16
  230. package/scripts/gate/core-budget.ts +3 -9
  231. package/scripts/gate/gate-plan.ts +7 -4
  232. package/scripts/gate/gate-report.ts +0 -6
  233. package/scripts/gate/gate-tree.ts +1 -1
  234. package/scripts/gate/pre-commit-type-check.ts +0 -6
  235. package/scripts/gate/project-checks.ts +7 -8
  236. package/scripts/gate/scoped-green.ts +0 -1
  237. package/scripts/gate/type-check-step.ts +1 -1
  238. package/scripts/gate/verification-gate.ts +0 -2
  239. package/scripts/gh/fix-gh-packages.ts +15 -12
  240. package/scripts/gh/gh-spawn.ts +2 -2
  241. package/scripts/{git → gh}/git-ai-review-scan.ts +1 -5
  242. package/scripts/{git → gh}/git-gh-check.ts +2 -1
  243. package/scripts/{git → gh}/git-gh-exec.ts +1 -1
  244. package/scripts/{git → gh}/git-gh-failure.ts +1 -1
  245. package/scripts/{git → gh}/git-gh-helpers.ts +0 -1
  246. package/scripts/{git → gh}/git-gh-issue-list.ts +13 -10
  247. package/scripts/{git → gh}/git-gh-issue-read.ts +1 -1
  248. package/scripts/{git → gh}/git-gh-issue-rest.ts +6 -3
  249. package/scripts/{git → gh}/git-gh-issue-write.ts +4 -1
  250. package/scripts/{git → gh}/git-gh-pr-auto-merge.ts +4 -1
  251. package/scripts/{git → gh}/git-gh-pr-read.ts +24 -0
  252. package/scripts/{git → gh}/git-gh-pr-rest.ts +5 -2
  253. package/scripts/{git → gh}/git-gh-pr-review.ts +2 -2
  254. package/scripts/{git → gh}/git-gh-pr-rollup.ts +1 -3
  255. package/scripts/{git → gh}/git-gh-pr.ts +3 -1
  256. package/scripts/{git → gh}/git-pr-ai-review.ts +2 -5
  257. package/scripts/{git → gh}/git-pr-checks-eval.ts +0 -10
  258. package/scripts/{git → gh}/git-pr-checks-parse.ts +4 -20
  259. package/scripts/{git → gh}/git-pr-checks.ts +2 -1
  260. package/scripts/{git → gh}/git-pr-coderabbit.ts +2 -5
  261. package/scripts/{git → gh}/git-pr-confirmation.ts +1 -3
  262. package/scripts/{git → gh}/git-pr-managed-config.ts +2 -3
  263. package/scripts/{git → gh}/git-pr.ts +3 -3
  264. package/scripts/git/changed-file-scope.ts +0 -1
  265. package/scripts/git/git-branch.ts +3 -6
  266. package/scripts/git/git-command.ts +6 -1
  267. package/scripts/git/git-commit.ts +1 -1
  268. package/scripts/git/git-common-directory.ts +80 -24
  269. package/scripts/git/git-error.ts +66 -9
  270. package/scripts/git/git-issue.ts +1 -1
  271. package/scripts/git/git-preflight.ts +2 -2
  272. package/scripts/git/git-prompt.ts +6 -31
  273. package/scripts/git/git-push.ts +1 -1
  274. package/scripts/git/git-spawn-sync.ts +65 -0
  275. package/scripts/git/git-spawn.ts +5 -4
  276. package/scripts/git/git-staging.ts +1 -0
  277. package/scripts/git/git-status.ts +1 -1
  278. package/scripts/git/git-workflow.ts +1 -1
  279. package/scripts/git/gone-branch.ts +1 -0
  280. package/scripts/git/listing-cutoff.ts +1 -1
  281. package/scripts/git/main-merge.ts +1 -1
  282. package/scripts/git/main-sync.ts +19 -1
  283. package/scripts/git/parse-json-array.ts +0 -3
  284. package/scripts/git/repository-lock.ts +16 -16
  285. package/scripts/git/{git-stash.ts → stash/git-stash.ts} +1 -1
  286. package/scripts/git/{stash-pop-args.ts → stash/stash-pop-args.ts} +2 -7
  287. package/scripts/git/{stash-pop-cli.ts → stash/stash-pop-cli.ts} +0 -1
  288. package/scripts/git/{stash-sweep-lock.ts → stash/stash-sweep-lock.ts} +3 -3
  289. package/scripts/hooks/codex-hook-adapter.ts +0 -1
  290. package/scripts/hooks/edited-cspell.ts +1 -1
  291. package/scripts/hooks/format-edited-file.ts +2 -0
  292. package/scripts/hooks/pre-push-unit.ts +0 -4
  293. package/scripts/hooks/pretool-guard.ts +22 -4
  294. package/scripts/hooks/reserved-run.ts +1 -1
  295. package/scripts/hooks/step-zero-notice.ts +76 -0
  296. package/scripts/hooks/stop-guard.ts +47 -21
  297. package/scripts/init/claude-plugin-config.ts +4 -5
  298. package/scripts/init/hook-command-rewrite.ts +16 -8
  299. package/scripts/init/hook-launch.ts +41 -15
  300. package/scripts/init/init-basic.ts +2 -5
  301. package/scripts/init/init-file-action.ts +54 -0
  302. package/scripts/init/init-logic-migrate.ts +35 -17
  303. package/scripts/init/init-logic-secretlint.ts +1 -1
  304. package/scripts/init/init-logic.ts +10 -19
  305. package/scripts/init/init.ts +11 -71
  306. package/scripts/init/kit-written-paths.ts +2 -0
  307. package/scripts/init/project-profile.ts +9 -17
  308. package/scripts/init/start-exec.ts +1 -1
  309. package/scripts/init/start-setup-pr.ts +6 -6
  310. package/scripts/init/start-steps.ts +3 -1
  311. package/scripts/issue/behavior-change-lint.ts +0 -1
  312. package/scripts/issue/defect-rate-cli.ts +64 -23
  313. package/scripts/issue/defect-rate.ts +1 -2
  314. package/scripts/{git → issue}/git-next-issues.ts +3 -3
  315. package/scripts/issue/issue-backlinks-cli.ts +1 -5
  316. package/scripts/issue/issue-backlinks.ts +7 -14
  317. package/scripts/issue/issue-cite-cli.ts +2 -6
  318. package/scripts/issue/issue-cite.ts +12 -8
  319. package/scripts/issue/issue-classification.ts +1 -9
  320. package/scripts/issue/issue-closing-pr.ts +4 -4
  321. package/scripts/issue/issue-comment-cli.ts +2 -2
  322. package/scripts/issue/issue-file-cli.ts +5 -5
  323. package/scripts/issue/issue-file.ts +7 -14
  324. package/scripts/issue/issue-fold-cli.ts +1 -1
  325. package/scripts/issue/issue-fold-existing-cli.ts +1 -1
  326. package/scripts/{git → issue}/issue-labels.ts +8 -0
  327. package/scripts/issue/issue-lint-cli.ts +1 -1
  328. package/scripts/issue/issue-merged.ts +2 -2
  329. package/scripts/{git → issue}/issue-rank.ts +1 -1
  330. package/scripts/issue/issue-read-cli.ts +8 -47
  331. package/scripts/issue/issue-read.ts +0 -2
  332. package/scripts/issue/issue-report-failures.ts +80 -0
  333. package/scripts/issue/issue-scout-cli.ts +1 -7
  334. package/scripts/issue/issue-scout-closed.ts +2 -2
  335. package/scripts/issue/issue-scout.ts +0 -2
  336. package/scripts/issue/issue-state-cli.ts +5 -46
  337. package/scripts/issue/issue-state.ts +1 -5
  338. package/scripts/issue/markdown-section.ts +21 -6
  339. package/scripts/issue/measure-rerun-cli.ts +74 -15
  340. package/scripts/issue/reproduction-measure.ts +1 -1
  341. package/scripts/josh/cli-body.ts +1 -1
  342. package/scripts/josh/file-map-stamp.ts +0 -3
  343. package/scripts/josh/josh-command-map.ts +2 -0
  344. package/scripts/josh/josh-commands-ai.ts +9 -102
  345. package/scripts/josh/josh-commands-backlog.ts +5 -1
  346. package/scripts/josh/josh-commands-development.ts +8 -1
  347. package/scripts/josh/josh-commands-issue.ts +2 -2
  348. package/scripts/josh/josh-commands-maintenance.ts +7 -0
  349. package/scripts/josh/josh-commands-project.ts +7 -1
  350. package/scripts/josh/josh-commands-run.ts +114 -0
  351. package/scripts/josh/josh-commands-versioning.ts +8 -0
  352. package/scripts/josh/josh-commands-workflow.ts +3 -3
  353. package/scripts/josh/path-decision.ts +0 -3
  354. package/scripts/josh/process-owner.ts +16 -0
  355. package/scripts/josh/run-github-prerequisite.ts +3 -6
  356. package/scripts/josh/session-language-cli.ts +0 -1
  357. package/scripts/lane/lane-await.ts +46 -19
  358. package/scripts/lane/lane-capacity.ts +6 -3
  359. package/scripts/lane/lane-child-invocation.ts +20 -7
  360. package/scripts/lane/lane-cli.ts +1 -4
  361. package/scripts/lane/lane-close.ts +5 -4
  362. package/scripts/lane/lane-dispatch.ts +3 -4
  363. package/scripts/lane/lane-environment.ts +0 -9
  364. package/scripts/lane/lane-handoff.ts +38 -0
  365. package/scripts/lane/lane-launch-cli.ts +31 -27
  366. package/scripts/lane/lane-open.ts +0 -3
  367. package/scripts/lane/lane-output.ts +0 -1
  368. package/scripts/lane/lane-registry.ts +9 -0
  369. package/scripts/lane/openai-lane-ship.ts +2 -0
  370. package/scripts/lane/openai-lane-supervisor-decision.ts +2 -1
  371. package/scripts/lane/openai-lane-supervisor.ts +6 -4
  372. package/scripts/lane/openai-review-broker.ts +7 -1
  373. package/scripts/lib/bounded-pool.ts +1 -0
  374. package/scripts/lib/cli-flags.ts +31 -2
  375. package/scripts/lib/error-message.ts +12 -1
  376. package/scripts/lib/json-value.ts +5 -4
  377. package/scripts/lib/poll.ts +3 -1
  378. package/scripts/lib/timed-fetch.ts +15 -0
  379. package/scripts/lib/timeouts.ts +13 -0
  380. package/scripts/{yaml → lib}/yaml-document.ts +3 -1
  381. package/scripts/{bytes → lines}/bytes-command.ts +0 -2
  382. package/scripts/lines/line-targets.ts +0 -11
  383. package/scripts/lines/lines-command.ts +5 -4
  384. package/scripts/lint/lint-related-scope.ts +0 -1
  385. package/scripts/lint/lint-related.ts +1 -1
  386. package/scripts/{git → notify}/telegram-notify.ts +2 -2
  387. package/scripts/{git → notify}/telegram-test.ts +4 -4
  388. package/scripts/observations/observation-ledger-home.ts +0 -1
  389. package/scripts/observations/observation-ledger-line.ts +0 -1
  390. package/scripts/observations/observation-ledger-migrate.ts +1 -1
  391. package/scripts/observations/observations-flush-landing.ts +3 -3
  392. package/scripts/observations/observations-flush.ts +2 -2
  393. package/scripts/overrides/lockfile-overrides.ts +1 -1
  394. package/scripts/overrides/overrides-logic.ts +1 -1
  395. package/scripts/package/package-scout-cli.ts +2 -7
  396. package/scripts/package/package-scout.ts +0 -2
  397. package/scripts/propagate/propagate-git-failure.ts +0 -1
  398. package/scripts/propagate/propagate-git.ts +3 -10
  399. package/scripts/propagate/propagate-publish.ts +2 -3
  400. package/scripts/propagate/propagate-run.ts +0 -2
  401. package/scripts/propagate/propagate-steps.ts +3 -5
  402. package/scripts/propagate/propagate-targets.ts +0 -4
  403. package/scripts/propagate/propagate.ts +0 -4
  404. package/scripts/refactor/import-graph.ts +0 -1
  405. package/scripts/refactor/refactor-lint.ts +0 -2
  406. package/scripts/refactor/refactor-scan.ts +2 -2
  407. package/scripts/refactor/refactor-targets.ts +0 -1
  408. package/scripts/registry-migration/migrate-logic.ts +28 -21
  409. package/scripts/registry-migration/migrate.ts +25 -14
  410. package/scripts/release/github-release-cli.ts +19 -6
  411. package/scripts/release/github-release-environment.ts +46 -0
  412. package/scripts/release/github-release.ts +151 -94
  413. package/scripts/release/release-history.ts +0 -1
  414. package/scripts/release/release-plan.ts +0 -1
  415. package/scripts/release/release-publish.ts +38 -5
  416. package/scripts/release/release-scope-cli.ts +1 -2
  417. package/scripts/release/release-tag.ts +2 -4
  418. package/scripts/release/release-worktree.ts +97 -2
  419. package/scripts/repo/auto-merge-setting-logic.ts +1 -4
  420. package/scripts/repo/repo-setting.ts +1 -1
  421. package/scripts/repo/repository-labels.ts +3 -3
  422. package/scripts/repo/required-checks-logic.ts +206 -0
  423. package/scripts/repo/required-checks-report.ts +71 -0
  424. package/scripts/repo/ruleset-check-cli.ts +53 -0
  425. package/scripts/repo/ruleset-check.ts +147 -0
  426. package/scripts/report/report-lint-cli.ts +0 -4
  427. package/scripts/review/disposition-cli.ts +1 -1
  428. package/scripts/review/live-evidence.ts +1 -1
  429. package/scripts/review/review-attest-cli.ts +0 -6
  430. package/scripts/review/review-attest.ts +4 -16
  431. package/scripts/review/review-brief-cli.ts +23 -6
  432. package/scripts/review/review-brief.ts +21 -8
  433. package/scripts/review/review-checkout.ts +3 -2
  434. package/scripts/review/review-diff-parts.ts +89 -0
  435. package/scripts/review/review-diff-write.ts +97 -0
  436. package/scripts/review/review-finding-ledger.ts +0 -1
  437. package/scripts/review/review-record-cli.ts +10 -17
  438. package/scripts/review/review-record.ts +0 -1
  439. package/scripts/review/review-round2-cli.ts +1 -1
  440. package/scripts/review/review-stamps.ts +0 -3
  441. package/scripts/review/review-tree.ts +14 -2
  442. package/scripts/rules/decision-oracle-stage.ts +18 -0
  443. package/scripts/rules/decision-oracle.ts +10 -7
  444. package/scripts/rules/delivered-rules.ts +9 -1
  445. package/scripts/rules/destructive-command.ts +119 -0
  446. package/scripts/rules/direct-filing.ts +1 -1
  447. package/scripts/rules/early-heartbeat.ts +4 -3
  448. package/scripts/rules/file-body.ts +0 -3
  449. package/scripts/rules/filing-cap.ts +0 -2
  450. package/scripts/rules/gh-api.ts +7 -2
  451. package/scripts/rules/git-argv.ts +18 -7
  452. package/scripts/rules/git-force.ts +0 -2
  453. package/scripts/rules/implementation-cut.ts +0 -1
  454. package/scripts/rules/index-guard.ts +71 -0
  455. package/scripts/rules/issue-citation.ts +50 -25
  456. package/scripts/rules/lane-background.ts +15 -7
  457. package/scripts/rules/lane-park.ts +22 -6
  458. package/scripts/rules/last-prompt.ts +1 -0
  459. package/scripts/rules/oracle-consulted.ts +1 -1
  460. package/scripts/rules/oracle-firing.ts +1 -0
  461. package/scripts/rules/oracle-list-cli.ts +0 -3
  462. package/scripts/rules/permission-guards.ts +14 -0
  463. package/scripts/rules/protected-files.ts +74 -0
  464. package/scripts/rules/reply-language.ts +1 -1
  465. package/scripts/rules/rule-body-guard.ts +0 -3
  466. package/scripts/rules/rule-value-cli.ts +0 -1
  467. package/scripts/rules/shell-body-trigger.ts +0 -1
  468. package/scripts/rules/shell-segments.ts +0 -1
  469. package/scripts/rules/stop-rules.ts +20 -9
  470. package/scripts/rules/tail-commands.ts +1 -1
  471. package/scripts/rules/test-declared-commit.ts +1 -1
  472. package/scripts/rules/third-party-write.ts +0 -1
  473. package/scripts/rules/worktree-guard.ts +26 -9
  474. package/scripts/run/run-carry-args.ts +8 -12
  475. package/scripts/run/run-carry-cli.ts +0 -2
  476. package/scripts/run/run-carry-stash.ts +2 -2
  477. package/scripts/run/run-carry-streak.ts +1 -1
  478. package/scripts/run/run-cut-args.ts +1 -6
  479. package/scripts/run/run-cut-cli.ts +0 -7
  480. package/scripts/run/run-cut.ts +8 -54
  481. package/scripts/run/run-ending-cli.ts +2 -6
  482. package/scripts/run/run-ending.ts +1 -4
  483. package/scripts/run/run-entry-cli.ts +133 -25
  484. package/scripts/run/run-entry-stop.ts +104 -0
  485. package/scripts/run/run-entry.ts +0 -1
  486. package/scripts/run/run-event-cli.ts +0 -2
  487. package/scripts/run/run-event-follow.ts +1 -0
  488. package/scripts/run/run-event-render.ts +1 -1
  489. package/scripts/run/run-event-scope.ts +53 -4
  490. package/scripts/run/run-event-stream-emit.ts +7 -2
  491. package/scripts/run/run-event-stream.ts +0 -3
  492. package/scripts/run/run-event-watch.ts +1 -0
  493. package/scripts/run/run-halfrun-resume.ts +20 -7
  494. package/scripts/run/run-headless.ts +2 -2
  495. package/scripts/run/run-hold-cli.ts +33 -10
  496. package/scripts/run/run-hold.ts +29 -6
  497. package/scripts/run/run-issue-number.ts +1 -1
  498. package/scripts/run/run-liveness-cli.ts +2 -6
  499. package/scripts/run/run-liveness.ts +2 -3
  500. package/scripts/run/run-merge-cli.ts +7 -23
  501. package/scripts/run/run-merge-steps.ts +5 -5
  502. package/scripts/run/run-merge.ts +1 -1
  503. package/scripts/run/run-next-cli.ts +1 -1
  504. package/scripts/run/run-parent-cut-hook.ts +4 -2
  505. package/scripts/run/run-preflight.ts +2 -9
  506. package/scripts/run/run-prep-cli.ts +6 -3
  507. package/scripts/run/run-prep-locate.ts +1 -1
  508. package/scripts/run/run-progress-args.ts +20 -4
  509. package/scripts/run/run-progress-cli.ts +35 -36
  510. package/scripts/run/run-progress-clock.ts +10 -35
  511. package/scripts/run/run-progress-config.ts +0 -4
  512. package/scripts/run/run-progress-read.ts +1 -2
  513. package/scripts/run/run-progress.ts +1 -2
  514. package/scripts/run/run-prrun-resume.ts +117 -0
  515. package/scripts/run/run-report-cli.ts +1 -2
  516. package/scripts/run/run-report.ts +0 -1
  517. package/scripts/run/run-retrospective.ts +1 -1
  518. package/scripts/run/run-review-cli.ts +0 -3
  519. package/scripts/run/run-review-steps.ts +2 -0
  520. package/scripts/run/run-review.ts +0 -2
  521. package/scripts/run/run-ship-cli.ts +29 -19
  522. package/scripts/run/run-ship-detach.ts +1 -0
  523. package/scripts/run/run-ship-next.ts +106 -0
  524. package/scripts/run/run-ship-preflight.ts +1 -1
  525. package/scripts/run/run-ship-probe.ts +4 -1
  526. package/scripts/run/run-ship-return.ts +8 -3
  527. package/scripts/run/run-ship-review-steps.ts +18 -4
  528. package/scripts/run/run-ship-review.ts +11 -0
  529. package/scripts/run/run-ship-scoped.ts +1 -0
  530. package/scripts/run/run-ship-steps.ts +1 -1
  531. package/scripts/run/run-ship.ts +0 -1
  532. package/scripts/run/run-stage-read.ts +62 -0
  533. package/scripts/run/run-stage.ts +160 -0
  534. package/scripts/run/run-status-cli.ts +3 -16
  535. package/scripts/run/run-step-cli.ts +21 -14
  536. package/scripts/run/run-step.ts +1 -1
  537. package/scripts/run/run-stop-notify.ts +1 -3
  538. package/scripts/run/run-stranded-cli.ts +0 -4
  539. package/scripts/run/run-stranded-detect.ts +2 -2
  540. package/scripts/run/run-tail-cli.ts +17 -11
  541. package/scripts/run/run-tail.ts +2 -0
  542. package/scripts/run/run-tidy-lanes.ts +6 -2
  543. package/scripts/run/run-tidy-stashes.ts +12 -12
  544. package/scripts/run/run-wake-cli.ts +4 -13
  545. package/scripts/run/run-wake-loop.ts +2 -2
  546. package/scripts/run/run-wake.ts +2 -4
  547. package/scripts/run/run-watcher-guard-cli.ts +0 -4
  548. package/scripts/run/run-watcher-guard.ts +18 -12
  549. package/scripts/run/run-watcher-hook.ts +1 -1
  550. package/scripts/safe-chain/ci-installer-pin.ts +11 -5
  551. package/scripts/safe-chain/pack-hook.ts +92 -0
  552. package/scripts/safe-chain/project-config.ts +1 -1
  553. package/scripts/security/security-audit-provision-logic.ts +0 -1
  554. package/scripts/security/security-audit-provision.ts +3 -2
  555. package/scripts/security/security-audit.ts +0 -1
  556. package/scripts/security/security-updates-logic.ts +0 -1
  557. package/scripts/sonar/sonar-hotspots-cli.ts +10 -52
  558. package/scripts/sonar/sonar-hotspots.ts +0 -1
  559. package/scripts/sonar/sonar-new-code-cli.ts +150 -0
  560. package/scripts/sonar/sonar-new-code.ts +79 -0
  561. package/scripts/sonar/sonar-project.ts +97 -0
  562. package/scripts/split/split-assess-cli.ts +1 -1
  563. package/scripts/split/split-assess.ts +2 -5
  564. package/scripts/sync/managed-config-scope-cli.ts +1 -1
  565. package/scripts/sync/skill-migration.ts +0 -1
  566. package/scripts/sync/sync-ai-files.ts +4 -0
  567. package/scripts/sync/sync-hook-safety.ts +2 -3
  568. package/scripts/sync/sync.ts +1 -8
  569. package/scripts/sync/template-source-logic.ts +0 -1
  570. package/scripts/sync/workflow-pin-logic.ts +16 -28
  571. package/scripts/test/test-declared-changed.ts +13 -16
  572. package/scripts/test/test-declared-logic.ts +0 -1
  573. package/scripts/test/test-declared.ts +3 -9
  574. package/scripts/test/test-red-commit.ts +2 -2
  575. package/scripts/test/test-red.ts +14 -7
  576. package/scripts/test/test-type-logic.ts +1 -1
  577. package/scripts/test/unit-worker-share.ts +7 -16
  578. package/scripts/time-runtime/time-batch-guard.ts +1 -2
  579. package/scripts/time-runtime/time-bundle-call.ts +0 -1
  580. package/scripts/time-runtime/time-bundles.ts +0 -4
  581. package/scripts/time-runtime/time-density-hook.ts +0 -2
  582. package/scripts/time-runtime/time-followup-stage.ts +1 -1
  583. package/scripts/time-runtime/time-format.ts +0 -73
  584. package/scripts/time-runtime/time-round-trips.ts +0 -4
  585. package/scripts/time-runtime/time-single-check.ts +1 -1
  586. package/scripts/time-runtime/time-spans.ts +1 -1
  587. package/scripts/time-runtime/time-transcript-line.ts +1 -2
  588. package/scripts/ui/ui-routes-cli.ts +0 -5
  589. package/scripts/ui/ui-routes.ts +0 -5
  590. package/scripts/version/effective-upstream.ts +2 -2
  591. package/scripts/version/latest-corepack.ts +7 -9
  592. package/scripts/version/latest-scope-cli.ts +0 -2
  593. package/scripts/version/latest-update.ts +0 -2
  594. package/scripts/version/npm-registry.ts +2 -2
  595. package/scripts/version/publishable-range-check.ts +1 -1
  596. package/scripts/version/release-hold.ts +0 -4
  597. package/scripts/version/running-binary.ts +2 -2
  598. package/scripts/version/version-check-logic.ts +0 -2
  599. package/scripts/version/version-commands.ts +11 -1
  600. package/scripts/version/version-remote.ts +3 -3
  601. package/scripts/version/version-targets.ts +4 -4
  602. package/templates/CLAUDE.basic.md +6 -6
  603. package/templates/sonar-project.properties +28 -1
  604. package/templates/workflows/ci.yml +16 -82
  605. package/templates/workflows/dependabot-auto-merge.yml +6 -3
  606. package/templates/workflows/github-release.yml +79 -0
  607. package/tsconfig.sonar.json +4 -22
  608. package/prompts/collaboration-workflow/cross-repo-epic.md +0 -5
  609. package/prompts/collaboration-workflow/epic-audit.md +0 -5
  610. package/prompts/collaboration-workflow/epic-bundle.md +0 -5
  611. package/scripts/init/plugin-install-hint.ts +0 -47
  612. package/scripts/version/parse-json.ts +0 -13
  613. /package/scripts/{git → followup}/git-followup-stages.ts +0 -0
  614. /package/scripts/{git → gh}/git-gh-api-path.ts +0 -0
  615. /package/scripts/{git → gh}/git-gh-command.ts +0 -0
  616. /package/scripts/{git → gh}/git-gh-issue.ts +0 -0
  617. /package/scripts/{git → gh}/git-gh-pr-snapshot.ts +0 -0
  618. /package/scripts/{git → gh}/git-gh-reachability.ts +0 -0
  619. /package/scripts/{git → gh}/git-gh-repo.ts +0 -0
  620. /package/scripts/{git → gh}/git-gh-rest-state.ts +0 -0
  621. /package/scripts/{git → gh}/git-pr-checks-watch.ts +0 -0
  622. /package/scripts/{git → gh}/git-pr-error.ts +0 -0
  623. /package/scripts/{git → gh}/git-pr-messages.ts +0 -0
  624. /package/scripts/{git → gh}/github-issue-url.ts +0 -0
  625. /package/scripts/git/{stash-orphans.ts → stash/stash-orphans.ts} +0 -0
  626. /package/scripts/{git → issue}/issue-reference.ts +0 -0
  627. /package/scripts/{git → lib}/animation-helpers.ts +0 -0
  628. /package/scripts/{git → lib}/git-animation.ts +0 -0
  629. /package/scripts/{git → notify}/git-notify.ts +0 -0
  630. /package/scripts/{git → notify}/telegram-test-logic.ts +0 -0
@@ -3,19 +3,19 @@ import { fileURLToPath } from 'node:url'
3
3
  import { epic_bundle_gaps } from '#scripts/epic/epic-bundle-gaps'
4
4
  import { epic_index, type FetchedEpics } from '#scripts/epic/epic-index'
5
5
  import { epic_issue } from '#scripts/epic/epic-issue'
6
- import { git_gh_command } from '#scripts/git/git-gh-command'
7
- import { SUMMARY_FIELDS } from '#scripts/git/git-gh-issue'
8
- import { git_next_issues } from '#scripts/git/git-next-issues'
9
- import { AUTO_OK_LABEL } from '#scripts/git/issue-labels'
6
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
7
+ import { SUMMARY_FIELDS } from '#scripts/gh/git-gh-issue'
10
8
  import { cutoff_cause, cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
11
9
  import { read_json_listing } from '#scripts/git/parse-json-array'
12
10
  import { open_issue_schema, type OpenIssueData } from '#scripts/git/schemas'
11
+ import { git_next_issues } from '#scripts/issue/git-next-issues'
12
+ import { AUTO_OK_LABEL } from '#scripts/issue/issue-labels'
13
13
 
14
14
  // `josh auto-ok:next` — which issue outside the epic an unattended run picks up next
15
15
  // (joshuafolkken/kit#906).
16
16
  //
17
17
  // A command rather than a documented `gh` invocation, for the reason the label constant exists: the
18
- // name `auto-ok` is single-sourced in `scripts/git/issue-labels.ts`, and a procedure that told an
18
+ // name `auto-ok` is single-sourced in `scripts/issue/issue-labels.ts`, and a procedure that told an
19
19
  // agent to type `gh issue list --label auto-ok` would put a second copy of it in prose, where
20
20
  // nothing checks it. The contract is `epic:next`'s: one token on standard output, every explanation
21
21
  // on standard error, so `answer=$(pnpm josh auto-ok:next)` reads it.
@@ -48,7 +48,7 @@ const LISTING_LIMIT = 200
48
48
  // CLI's version cannot be what changed the row shape — the same misdirection the message below
49
49
  // already refuses to repeat for the blocker relations, left standing one constant above it.
50
50
  const UNREADABLE_MESSAGE = `Could not read the \`${AUTO_OK_LABEL}\` listing. That is not "nothing is opted in" — check \`gh auth status\` and ask again.`
51
- const UNEXPECTED_SHAPE_MESSAGE = `Read the \`${AUTO_OK_LABEL}\` listing but could not parse it. That is not "nothing is opted in" — the rows came back in a shape this command does not recognize, so check the fields it asks for and the REST field mapping in \`scripts/git/git-gh-issue-rest.ts\` rather than your authentication.`
51
+ const UNEXPECTED_SHAPE_MESSAGE = `Read the \`${AUTO_OK_LABEL}\` listing but could not parse it. That is not "nothing is opted in" — the rows came back in a shape this command does not recognize, so check the fields it asks for and the REST field mapping in \`scripts/gh/git-gh-issue-rest.ts\` rather than your authentication.`
52
52
  // The blocker relations failing takes the whole listing with them, and `issue_list` swallows
53
53
  // the error — so the read looks exactly like an access failure and sends the reader to
54
54
  // `gh auth status`, which is green. That is the misdirection joshuafolkken/kit#996 added the message
@@ -491,23 +491,13 @@ const auto_ok_cli = {
491
491
  TRUNCATED_WITHOUT_ANSWER,
492
492
  NONE_OPTED_IN_MESSAGE,
493
493
  truncated_cause,
494
- parse_issue_number,
495
- parse_exclude,
496
- parse_pair,
497
494
  parse_options,
498
495
  fetch_opted_in,
499
- parse_listing,
500
- is_page_complete,
501
- is_closed,
502
- is_unblocked,
503
496
  fetch_tracking,
504
497
  is_runnable,
505
498
  pick_next,
506
499
  truncation_note,
507
- none_reason,
508
- report,
509
500
  run,
510
- main,
511
501
  }
512
502
 
513
503
  if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
@@ -18,10 +18,11 @@ import { backlog_stalled } from './backlog-stalled'
18
18
  //
19
19
  // **"New" is measured against the watcher's own start**, so one issue wakes the parent once: the next
20
20
  // `--wait` the parent starts takes the pool as it stands then — the woken issue included, dispatched or
21
- // not — as its baseline. A reading that fails is not an arrival; the probe says nothing and the
22
- // interval still wakes the parent. A baseline that cannot be read leaves that one wait inert rather than
23
- // reading it as empty — an empty one would wake the parent for the whole pool — and the next `--wait`
24
- // reads it again.
21
+ // not — as its baseline. A reading that fails is not an arrival; the probe says nothing and reads again
22
+ // a minute later. A baseline that cannot be read is never read as empty — an empty one would wake the
23
+ // parent for the whole pool — but it is not given up on either: the scheduled report no longer ends the
24
+ // wait (joshuafolkken/kit#3102), so an inert probe would leave the parent without a wake until `--hours`. The
25
+ // probe reads the baseline again each minute until one answers.
25
26
  //
26
27
  // **Every read is bounded.** The probe is awaited inside the watcher's tick loop, so a `gh` call that
27
28
  // hangs would otherwise freeze the loop — its `--hours` bound, its liveness ping and the exit that is
@@ -116,19 +117,40 @@ async function never_arrives(): Promise<boolean> {
116
117
 
117
118
  const INERT: ArrivalProbe = { has_arrived: never_arrives }
118
119
 
119
- // Only the next-probe instant changes between probes, and it is set before the read is awaited.
120
+ // A probe spent on the baseline reports no arrival, whether or not the baseline answered.
121
+ async function retry_baseline(
122
+ answered: Array<ReadonlySet<string>>,
123
+ ports: ReadyPorts,
124
+ ): Promise<boolean> {
125
+ const late = await read_baseline(ports)
126
+
127
+ if (late !== undefined) answered.push(late)
128
+
129
+ return false
130
+ }
131
+
132
+ // The next-probe instant is set before any read is awaited. A baseline that did not answer is read again
133
+ // at the next probe instead of the reading, and the first one that answers is the baseline from then on
134
+ // — never an arrival itself, since it is the pool as it stands.
120
135
  function probe_of(
121
- baseline: ReadonlySet<string>,
136
+ initial: ReadonlySet<string> | undefined,
122
137
  first_ms: number,
123
138
  ports: ReadyPorts,
124
139
  ): ArrivalProbe {
125
140
  let next_ms = first_ms
141
+ // At most one entry, held in a list so the baseline that answers after an await is added rather than
142
+ // reassigned over the value read before it.
143
+ const answered: Array<ReadonlySet<string>> = initial === undefined ? [] : [initial]
126
144
 
127
145
  async function has_arrived(now_ms: number): Promise<boolean> {
128
146
  if (now_ms < next_ms) return false
129
147
 
130
148
  next_ms = now_ms + PROBE_INTERVAL_MS
131
149
 
150
+ const [baseline] = answered
151
+
152
+ if (baseline === undefined) return await retry_baseline(answered, ports)
153
+
132
154
  const reading = await read_reading(ports)
133
155
 
134
156
  return reading !== undefined && arrivals(baseline, reading).length > NONE
@@ -146,12 +168,10 @@ async function start(
146
168
  ): Promise<ArrivalProbe> {
147
169
  if (!(await is_parent_safely(is_parent))) return INERT
148
170
 
149
- const baseline = await read_baseline(ports)
150
-
151
- return baseline === undefined ? INERT : probe_of(baseline, now_ms + PROBE_INTERVAL_MS, ports)
171
+ return probe_of(await read_baseline(ports), now_ms + PROBE_INTERVAL_MS, ports)
152
172
  }
153
173
 
154
- const backlog_arrival = { INERT, PROBE_INTERVAL_MS, answered_issues, arrivals, start }
174
+ const backlog_arrival = { PROBE_INTERVAL_MS, answered_issues, arrivals, start }
155
175
 
156
176
  export type { ArrivalProbe }
157
177
  export { backlog_arrival }
@@ -32,11 +32,7 @@ type ParsedValues = Partial<Record<OptionName, string | boolean>>
32
32
  // A misspelled flag is refused rather than defaulted, the contract `path_decision.has_unknown_flag`
33
33
  // states for the path-decided commands: an invocation nobody can read is never answered.
34
34
  function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
35
- return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, strict: true })?.values
36
- }
37
-
38
- function text_of(value: string | boolean | undefined): string | undefined {
39
- return typeof value === 'string' ? value : undefined
35
+ return cli_flags.values_of(argv, OPTIONS)
40
36
  }
41
37
 
42
38
  // `find` rather than a cast: an unrecognized word has to be refused, and a type assertion would
@@ -79,15 +75,15 @@ type Counts = Record<(typeof COUNT_NAMES)[number], number | undefined>
79
75
 
80
76
  function counts_of(values: ParsedValues): Counts {
81
77
  return {
82
- idle: to_count(text_of(values.idle)),
83
- max: to_count(text_of(values.max)),
84
- merged: to_count(text_of(values.merged)),
85
- running: to_count(text_of(values.running)),
78
+ idle: to_count(cli_flags.string_of(values.idle)),
79
+ max: to_count(cli_flags.string_of(values.max)),
80
+ merged: to_count(cli_flags.string_of(values.merged)),
81
+ running: to_count(cli_flags.string_of(values.running)),
86
82
  }
87
83
  }
88
84
 
89
85
  function are_counts_readable(values: ParsedValues, counts: Counts): boolean {
90
- return COUNT_NAMES.every((name) => is_readable(text_of(values[name]), counts[name]))
86
+ return COUNT_NAMES.every((name) => is_readable(cli_flags.string_of(values[name]), counts[name]))
91
87
  }
92
88
 
93
89
  // **`--idle 0` is how the watch is turned off, and omitting the flag takes the default**
@@ -116,14 +112,17 @@ function to_idle_ms(idle_minutes: number | undefined): number | undefined {
116
112
  // lands on the very first ask, before the run has started anything, and the fix is one flag the loop
117
113
  // already holds: it always knows when it last had work.
118
114
  function is_watch_readable(values: ParsedValues): boolean {
119
- if (to_idle_ms(to_count(text_of(values.idle))) === undefined) return true
115
+ if (to_idle_ms(to_count(cli_flags.string_of(values.idle))) === undefined) return true
120
116
 
121
- return text_of(values.active) !== undefined
117
+ return cli_flags.string_of(values.active) !== undefined
122
118
  }
123
119
 
124
120
  function are_values_readable(values: ParsedValues): boolean {
125
121
  return (
126
- is_readable(text_of(values.active), to_time_ms(text_of(values.active))) &&
122
+ is_readable(
123
+ cli_flags.string_of(values.active),
124
+ to_time_ms(cli_flags.string_of(values.active)),
125
+ ) &&
127
126
  is_watch_readable(values) &&
128
127
  are_counts_readable(values, counts_of(values))
129
128
  )
@@ -138,7 +137,7 @@ function input_of(values: ParsedValues, base: InputBase): BudgetInput {
138
137
 
139
138
  return {
140
139
  ...base,
141
- active_at_ms: to_time_ms(text_of(values.active)) ?? base.started_at_ms,
140
+ active_at_ms: to_time_ms(cli_flags.string_of(values.active)) ?? base.started_at_ms,
142
141
  merged: counts.merged ?? 0,
143
142
  running: counts.running ?? 0,
144
143
  max_issues: counts.max,
@@ -147,11 +146,11 @@ function input_of(values: ParsedValues, base: InputBase): BudgetInput {
147
146
  }
148
147
 
149
148
  function build_input(values: ParsedValues, now_ms: number): BudgetInput | undefined {
150
- const answer = to_answer(text_of(values.answer))
149
+ const answer = to_answer(cli_flags.string_of(values.answer))
151
150
 
152
151
  if (answer === undefined) return undefined
153
152
 
154
- const started_at_ms = to_time_ms(text_of(values.started))
153
+ const started_at_ms = to_time_ms(cli_flags.string_of(values.started))
155
154
 
156
155
  if (started_at_ms === undefined) return undefined
157
156
  if (!are_values_readable(values)) return undefined
@@ -268,7 +268,6 @@ function decide(input: BudgetInput): BudgetDecision {
268
268
 
269
269
  const backlog_budget = {
270
270
  ANSWERS,
271
- ANSWER_STOP_REASONS,
272
271
  BLOCKED_REASON,
273
272
  DEFAULT_IDLE_MINUTES,
274
273
  DEFAULT_IDLE_MS,
@@ -283,7 +282,6 @@ const backlog_budget = {
283
282
  UNREADABLE_REASON,
284
283
  WATCH_VERDICT,
285
284
  WHOLE_RUN_BUDGET_HOURS,
286
- WHOLE_RUN_BUDGET_MINUTES,
287
285
  WHOLE_RUN_BUDGET_MS,
288
286
  WHOLE_RUN_REASON,
289
287
  decide,
@@ -1,6 +1,6 @@
1
1
  import type { EpicChild } from '#scripts/epic/epic-graph'
2
2
  import type { EpicNextResult, RepoCandidates } from '#scripts/epic/epic-report'
3
- import { git_gh_command } from '#scripts/git/git-gh-command'
3
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
4
4
  import { defect_rate, type DefectRate, type IssueKind } from '#scripts/issue/defect-rate'
5
5
  import { defect_rate_cli } from '#scripts/issue/defect-rate-cli'
6
6
 
@@ -72,6 +72,6 @@ async function prioritize(
72
72
  return await reorder(result, repo)
73
73
  }
74
74
 
75
- const backlog_defect_priority = { UNMEASURED_MESSAGE, KIND_RANK, prioritize }
75
+ const backlog_defect_priority = { UNMEASURED_MESSAGE, prioritize }
76
76
 
77
77
  export { backlog_defect_priority }
@@ -5,13 +5,13 @@ import { CONTEXT_CUT_THRESHOLD } from '#scripts/cost-runtime/context-cut-thresho
5
5
  import { hook_decision } from '#scripts/josh/hook-decision'
6
6
  import { josh_command } from '#scripts/josh/josh-run'
7
7
  import { lane_await, type AwaitState } from '#scripts/lane/lane-await'
8
- import { lane_launch_cli } from '#scripts/lane/lane-launch-cli'
8
+ import { lane_launch_cli, type LaunchOutcome } from '#scripts/lane/lane-launch-cli'
9
9
  import { lane_registry } from '#scripts/lane/lane-registry'
10
10
  import { cli_flags } from '#scripts/lib/cli-flags'
11
11
  import { error_text } from '#scripts/lib/error-message'
12
12
  import { run_carry, type RunCarry } from '#scripts/run/run-carry'
13
13
  import { run_event_stream_emit } from '#scripts/run/run-event-stream-emit'
14
- import { run_merge_cli } from '#scripts/run/run-merge-cli'
14
+ import { run_merge_cli, type MergeResult } from '#scripts/run/run-merge-cli'
15
15
  import { z } from 'zod'
16
16
  import {
17
17
  backlog_drive,
@@ -28,6 +28,7 @@ import { backlog_drive_owner } from './backlog-drive-owner'
28
28
  import { backlog_drive_restore } from './backlog-drive-restore'
29
29
  import { backlog_drive_retrospective } from './backlog-drive-retrospective'
30
30
  import { backlog_offer_cli } from './backlog-offer-cli'
31
+ import { backlog_ready } from './backlog-ready'
31
32
 
32
33
  // `josh backlog:drive` — the backlogrun parent's loop as one resident wait (joshuafolkken/kit#2499).
33
34
  // The parent issues it once, in the background, and is woken only when it exits — with a token the model
@@ -217,7 +218,7 @@ function merge_token(out: string, code: number): string {
217
218
 
218
219
  // The child's transcript, where its lane recorded one, so `run:merge` can tell an API outage from a
219
220
  // failure exactly as it does for the parent that passes `--output` by hand.
220
- async function merge(issue: string, owner: string): Promise<string> {
221
+ async function merge(issue: string, owner: string): Promise<MergeResult> {
221
222
  await backlog_drive_owner.assert_current(owner)
222
223
  const lane = await lane_registry.find_open_lane(issue)
223
224
  const result = await run_merge_cli.merge_child({
@@ -231,13 +232,15 @@ async function merge(issue: string, owner: string): Promise<string> {
231
232
 
232
233
  await backlog_drive_named.mark_done(issue, result, owner)
233
234
 
234
- return result.token
235
+ return result
235
236
  }
236
237
 
237
- async function launch(issue: string, owner: string): Promise<boolean> {
238
+ async function launch(issue: string, owner: string): Promise<LaunchOutcome['kind']> {
238
239
  await backlog_drive_owner.assert_current(owner)
239
240
 
240
- return (await lane_launch_cli.launch_lane({ issue, stash: undefined })) !== undefined
241
+ const outcome = await lane_launch_cli.launch_lane({ issue, stash: undefined })
242
+
243
+ return outcome.kind
241
244
  }
242
245
 
243
246
  // `lane:await`'s re-confirmed check, one state per child. A child already gone when the loop starts —
@@ -304,6 +307,7 @@ function ports_of(
304
307
  merge: async (issue) => await merge(issue, context.owner),
305
308
  offer: async (state) => await read_offer(state, context),
306
309
  launch: async (issue) => await launch(issue, context.owner),
310
+ free_lanes: backlog_ready.drive_free_lane_count,
307
311
  now: () => new Date(),
308
312
  sleep: async (milliseconds) => {
309
313
  await sleep(milliseconds)
@@ -375,14 +379,12 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
375
379
  }
376
380
 
377
381
  const backlog_drive_cli = {
378
- USAGE,
379
382
  end_line,
380
383
  finish: backlog_drive_finish.finish,
381
384
  merge_token,
382
385
  offer_argv: backlog_drive_offer_argv.offer_argv,
383
386
  parse,
384
387
  resume_line,
385
- run,
386
388
  to_offer,
387
389
  }
388
390
 
@@ -1,6 +1,8 @@
1
- import { EPIC_LABEL, has_label_name } from '#scripts/git/issue-labels'
1
+ import { EPIC_LABEL, has_label_name, NEEDS_DECISION_LABEL } from '#scripts/issue/issue-labels'
2
+ import type { IssueState } from '#scripts/issue/issue-state'
2
3
  import { issue_state_cli } from '#scripts/issue/issue-state-cli'
3
4
  import type { RunCarry } from '#scripts/run/run-carry'
5
+ import type { MergeResult } from '#scripts/run/run-merge-cli'
4
6
  import { backlog_budget } from './backlog-budget'
5
7
  import type { DriveState, OfferRead } from './backlog-drive'
6
8
  import { backlog_drive_named } from './backlog-drive-named'
@@ -44,6 +46,15 @@ function budget_offer(
44
46
  }
45
47
  }
46
48
 
49
+ // The outcome a named issue has already reached without this run dispatching it: closed is merged, and
50
+ // a `needs-decision` park waits on a person, so re-dispatching it only repeats the stop it parked on
51
+ // (joshuafolkken/kit#2965). Either is booked done rather than offered.
52
+ function settled_outcome(state: IssueState): MergeResult['outcome'] | undefined {
53
+ if (state.state.toUpperCase() === 'CLOSED') return 'merged'
54
+
55
+ return has_label_name(state.labels, NEEDS_DECISION_LABEL) ? 'parked' : undefined
56
+ }
57
+
47
58
  async function classify(issue: string, named: OfferRead, owner: string): Promise<OfferRead> {
48
59
  const result = await issue_state_cli.read_issue(issue)
49
60
 
@@ -51,8 +62,10 @@ async function classify(issue: string, named: OfferRead, owner: string): Promise
51
62
  return { verdict: 'issue-state', issues: [], retries: NO_RETRIES }
52
63
  }
53
64
 
54
- if (result.state.state.toUpperCase() === 'CLOSED') {
55
- await backlog_drive_named.mark_done(issue, { outcome: 'merged', code: 0, token: 'none' }, owner)
65
+ const outcome = settled_outcome(result.state)
66
+
67
+ if (outcome !== undefined) {
68
+ await backlog_drive_named.mark_done(issue, { outcome, code: 0, token: 'none' }, owner)
56
69
 
57
70
  return { verdict: 'wait', issues: [], retries: NO_RETRIES }
58
71
  }
@@ -1,4 +1,4 @@
1
- import { has_label_name, NEEDS_DECISION_LABEL } from '#scripts/git/issue-labels'
1
+ import { has_label_name, NEEDS_DECISION_LABEL } from '#scripts/issue/issue-labels'
2
2
  import { issue_state_cli } from '#scripts/issue/issue-state-cli'
3
3
  import { run_carry, type RunCarry } from '#scripts/run/run-carry'
4
4
  import type { RunEvent } from '#scripts/run/run-event-stream'
@@ -94,6 +94,7 @@ async function reconcile(
94
94
  for (const issue of completed) {
95
95
  const outcome = parked.has(issue) ? 'parked' : 'merged'
96
96
 
97
+ // eslint-disable-next-line no-await-in-loop -- each mark rewrites the same carry file
97
98
  await mark_done(String(issue), { outcome, code: 0, token: 'none' }, owner)
98
99
  }
99
100
  }
@@ -16,6 +16,9 @@
16
16
  // **Everything it touches is a port**, the pattern `run-wake-loop.ts` set, so the sequencing is pinned
17
17
  // without a process, a network or a clock.
18
18
 
19
+ import type { LaunchOutcome } from '#scripts/lane/lane-launch-cli'
20
+ import type { MergeResult } from '#scripts/run/run-merge-cli'
21
+
19
22
  const RUN_VERDICT = 'run'
20
23
  const STOP_VERDICT = 'stop'
21
24
  // The `run:merge` tokens that end the loop at once: a hand-off, a person's stop, the environment down,
@@ -32,6 +35,7 @@ const IMMEDIATE_TOKENS: ReadonlySet<string> = new Set([
32
35
  // and the children still in flight are collected before the loop ends (joshuafolkken/kit#2881).
33
36
  const HANDOFF_TOKENS: ReadonlySet<string> = new Set([...IMMEDIATE_TOKENS, STOP_VERDICT])
34
37
  const RESUMED_TOKEN = 'resumed'
38
+ const MERGED_OUTCOME = 'merged'
35
39
  const WATCH_VERDICT = 'watch'
36
40
  // The verdicts the loop acts on without the parent. Any other is handed back as it was printed.
37
41
  const KNOWN_VERDICTS: ReadonlySet<string> = new Set([
@@ -41,6 +45,9 @@ const KNOWN_VERDICTS: ReadonlySet<string> = new Set([
41
45
  'wait',
42
46
  ])
43
47
  const NO_RETRIES = 0
48
+ const NO_FREE_LANES = 0
49
+ const LAUNCHED = 'launched'
50
+ const UNOPENED = 'unopened'
44
51
 
45
52
  interface OfferRead {
46
53
  verdict: string
@@ -57,7 +64,9 @@ interface OfferRead {
57
64
  interface DriveState {
58
65
  // The children this run has in flight — seeded from the open lanes, so a restarted loop resumes them.
59
66
  in_flight: Array<string>
60
- // Every child collected, fed back to `backlog:offer` because GitHub closes a merged issue late.
67
+ // Every child merged, fed back to `backlog:offer` because GitHub closes a merged issue late. A parked
68
+ // child is left out: its labels classify it, so one a person released is offered again
69
+ // (joshuafolkken/kit#3041).
61
70
  exclude: Array<string>
62
71
  // When the run last did something, handed to `backlog:budget` as `--active`.
63
72
  active: string
@@ -80,14 +89,20 @@ interface DriveEnd {
80
89
  is_finish?: boolean | undefined
81
90
  }
82
91
 
92
+ type Collected = Pick<MergeResult, 'token' | 'outcome'>
93
+
83
94
  interface DrivePorts {
84
95
  // Whether a child's process has confirmed-ended — `lane:await`'s re-confirmed check in production.
85
96
  is_finished: (issue: string) => boolean
86
- // `run:merge <N>`'s first stdout token.
87
- merge: (issue: string) => Promise<string>
97
+ // `run:merge <N>`'s first stdout token and the outcome it handled the child as.
98
+ merge: (issue: string) => Promise<Collected>
88
99
  // `backlog:offer`'s answer for the state, or `undefined` when it exited non-zero.
89
100
  offer: (state: DriveState) => Promise<OfferRead | undefined>
90
- launch: (issue: string) => Promise<boolean>
101
+ // Where `lane:launch`'s chain ended — `launched`, `unopened` (no lane taken) or `failed` (one taken).
102
+ launch: (issue: string) => Promise<LaunchOutcome['kind']>
103
+ // The lanes free right now — `backlog_ready.drive_free_lane_count` in production, the limit less the
104
+ // open lanes.
105
+ free_lanes: () => Promise<number>
91
106
  now: () => Date
92
107
  }
93
108
 
@@ -150,29 +165,40 @@ function merge_stopped(issue: string, state: DriveState): DriveState {
150
165
  }
151
166
  }
152
167
 
153
- // The child collected and excluded, and — unless its cut was resumed in place — out of flight.
168
+ // Only a merged child is excluded: `backlog:next` may still read it OPEN. Any other is classified by
169
+ // its labels, so excluding it would hide a child a person released for the rest of the process.
170
+ function excluded(issue: string, outcome: Collected['outcome'], state: DriveState): Array<string> {
171
+ if (outcome !== MERGED_OUTCOME || state.exclude.includes(issue)) return state.exclude
172
+
173
+ return [...state.exclude, issue]
174
+ }
175
+
176
+ // The child collected, and — unless its cut was resumed in place — out of flight.
154
177
  function collected_state(
155
178
  issue: string,
156
- token: string,
179
+ collected: Collected,
157
180
  state: DriveState,
158
181
  active: string,
159
182
  ): DriveState {
160
183
  const in_flight =
161
- token === RESUMED_TOKEN ? state.in_flight : state.in_flight.filter((item) => item !== issue)
162
- const exclude = state.exclude.includes(issue) ? state.exclude : [...state.exclude, issue]
184
+ collected.token === RESUMED_TOKEN
185
+ ? state.in_flight
186
+ : state.in_flight.filter((item) => item !== issue)
187
+ const exclude = excluded(issue, collected.outcome, state)
163
188
 
164
189
  return { ...state, in_flight, exclude, active }
165
190
  }
166
191
 
167
192
  // One finished child: handed back on an immediate token, otherwise collected.
168
193
  async function collect(issue: string, state: DriveState, ports: DrivePorts): Promise<PassResult> {
169
- const token = await ports.merge(issue)
194
+ const collected = await ports.merge(issue)
195
+ const { token } = collected
170
196
 
171
197
  // An empty first line is a `run:merge` that failed before printing one: never read as a collection.
172
198
  if (token === '') return ended('merge', state, undefined, issue)
173
199
  if (IMMEDIATE_TOKENS.has(token)) return ended('merge', state, token, issue)
174
200
 
175
- const next = collected_state(issue, token, state, ports.now().toISOString())
201
+ const next = collected_state(issue, collected, state, ports.now().toISOString())
176
202
 
177
203
  return { kind: 'continue', state: token === STOP_VERDICT ? merge_stopped(issue, next) : next }
178
204
  }
@@ -182,6 +208,7 @@ async function collect_finished(state: DriveState, ports: DrivePorts): Promise<P
182
208
  let current = state
183
209
 
184
210
  for (const issue of finished) {
211
+ // eslint-disable-next-line no-await-in-loop -- each collect starts from the state the previous one returned
185
212
  const result = await collect(issue, current, ports)
186
213
 
187
214
  if (result.kind === 'end') return { ...result, state: current }
@@ -192,22 +219,55 @@ async function collect_finished(state: DriveState, ports: DrivePorts): Promise<P
192
219
  return { kind: 'continue', state: current }
193
220
  }
194
221
 
222
+ // A `lane:open` refused with every lane taken is a wait, not a judgement (joshuafolkken/kit#3027): the
223
+ // next offer after a lane frees retries it. A step failing after the lane opened is always handed back —
224
+ // that lane holds the last seat itself, so the free count would read the failure as a full pool.
225
+ async function refused(
226
+ issue: string,
227
+ outcome: LaunchOutcome['kind'],
228
+ state: DriveState,
229
+ ports: DrivePorts,
230
+ ): Promise<PassResult> {
231
+ if (outcome !== UNOPENED) return ended('launch', state, undefined, issue)
232
+ if ((await ports.free_lanes()) <= NO_FREE_LANES) return { kind: 'continue', state }
233
+
234
+ return ended('launch', state, undefined, issue)
235
+ }
236
+
237
+ // **No more launches than free lanes** (joshuafolkken/kit#3027). `backlog:offer` lists every ready issue
238
+ // without reading the lanes, so launching all of them overran `JOSH_LANE_LIMIT` until `lane:open` ran
239
+ // out of seats. The free count is read once, before the first launch, because every launch here opens
240
+ // a lane the count would otherwise have to be told about.
241
+ //
242
+ // **Only a launch is activity** (joshuafolkken/kit#3027): a pass that started nothing — every lane taken
243
+ // — keeps `active` where it was, or the once-a-minute offer would restart the idle budget for good.
244
+ function launched(state: DriveState, in_flight: Array<string>, ports: DrivePorts): DriveState {
245
+ if (in_flight.length === state.in_flight.length) return { ...state, in_flight }
246
+
247
+ return { ...state, in_flight, active: ports.now().toISOString() }
248
+ }
249
+
195
250
  async function launch_all(
196
251
  issues: ReadonlyArray<string>,
197
252
  state: DriveState,
198
253
  ports: DrivePorts,
199
254
  ): Promise<PassResult> {
200
255
  const in_flight = [...state.in_flight]
256
+ const free = await ports.free_lanes()
201
257
 
202
- for (const issue of issues) {
203
- const is_launched = await ports.launch(issue)
258
+ for (const issue of issues.slice(0, free)) {
259
+ // eslint-disable-next-line no-await-in-loop -- lanes launch in order and the first refusal ends the pass
260
+ const outcome = await ports.launch(issue)
204
261
 
205
- if (!is_launched) return ended('launch', { ...state, in_flight }, undefined, issue)
262
+ if (outcome !== LAUNCHED) {
263
+ // eslint-disable-next-line no-await-in-loop -- lanes launch in order and the first refusal ends the pass
264
+ return await refused(issue, outcome, launched(state, in_flight, ports), ports)
265
+ }
206
266
 
207
267
  in_flight.push(issue)
208
268
  }
209
269
 
210
- return { kind: 'continue', state: { ...state, in_flight, active: ports.now().toISOString() } }
270
+ return { kind: 'continue', state: launched(state, in_flight, ports) }
211
271
  }
212
272
 
213
273
  // A watch with nothing in flight is the drain: `run:step` owes the retrospective there, so it is the
@@ -337,7 +397,9 @@ async function run_loop(
337
397
  let step = await loop_step(first, config, ports)
338
398
 
339
399
  while (step.kind === 'next') {
400
+ // eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
340
401
  await ports.sleep(config.poll_ms)
402
+ // eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
341
403
  step = await loop_step(step.run, config, ports)
342
404
  }
343
405
 
@@ -348,10 +410,8 @@ async function run_loop(
348
410
 
349
411
  const backlog_drive = {
350
412
  HANDOFF_TOKENS,
351
- collect,
352
413
  initial_state,
353
414
  merge_stopped,
354
- on_verdict,
355
415
  run_loop,
356
416
  run_pass,
357
417
  }
@@ -141,9 +141,6 @@ function epic_item_outcome(
141
141
  }
142
142
 
143
143
  const backlog_named = {
144
- BACKLOG_KIND,
145
- END_KIND,
146
- NAMED_KIND,
147
144
  NOTHING_TO_RUN,
148
145
  after_failure,
149
146
  epic_item_outcome,
@@ -9,8 +9,9 @@ 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_stale } from '#scripts/epic/epic-solo-stale'
12
13
  import { epic_triage, type TriageVerdict } from '#scripts/epic/epic-triage'
13
- import { git_gh_command } from '#scripts/git/git-gh-command'
14
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
14
15
  import { PROJECT_ROOT } from '#scripts/init/init-paths'
15
16
  import { issue_citation } from '#scripts/rules/issue-citation'
16
17
  import { backlog_defect_priority } from './backlog-defect-priority'
@@ -294,7 +295,13 @@ async function gate_solo(result: EpicNextResult, context: PoolContext): Promise<
294
295
  if (result.verdict !== 'run') return result
295
296
 
296
297
  const { repo } = context
297
- const read = await epic_busy.read_repository(repo)
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
+
298
305
  const gated = backlog_rank.gate(result, read, repo, standalone_keys(context))
299
306
 
300
307
  if (gated.notice !== undefined) console.error(gated.notice)
@@ -410,23 +417,10 @@ const backlog_next = {
410
417
  RETRY_MESSAGE,
411
418
  READ_FAILURES,
412
419
  VERDICT_TOKENS,
413
- tokens_of,
414
- warn_epic_gap,
415
- warn_gaps,
416
- report,
417
- combine,
418
- views_from,
419
420
  resolve,
420
421
  standalone_keys,
421
- is_transport_failure,
422
- report_retry,
423
- report_triage,
424
422
  context_of,
425
- answer_pool,
426
- report_none,
427
- answer,
428
423
  run,
429
- main,
430
424
  }
431
425
 
432
426
  if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))