@joshuafolkken/kit 1.1455.0 → 1.1514.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 (374) hide show
  1. package/.claude/settings.json +6 -6
  2. package/.claude/skills/dependency-update/SKILL.md +26 -55
  3. package/.claude/skills/epic-commands/SKILL.md +83 -156
  4. package/.claude/skills/epic-commands/epic-bundle.md +51 -79
  5. package/.claude/skills/epic-commands/execution-waves.md +43 -81
  6. package/.claude/skills/verify-ui/SKILL.md +24 -50
  7. package/.claude/skills/workflow-commands/SKILL.md +7 -9
  8. package/.claude/skills/workflow-commands/background-commands.md +31 -46
  9. package/.claude/skills/workflow-commands/backlogrun-child.md +65 -85
  10. package/.claude/skills/workflow-commands/backlogrun-lanes.md +86 -125
  11. package/.claude/skills/workflow-commands/backlogrun-park.md +68 -118
  12. package/.claude/skills/workflow-commands/backlogrun-progress.md +17 -28
  13. package/.claude/skills/workflow-commands/backlogrun-recovery.md +17 -26
  14. package/.claude/skills/workflow-commands/backlogrun-steps.md +35 -52
  15. package/.claude/skills/workflow-commands/backlogrun.md +31 -50
  16. package/.claude/skills/workflow-commands/chain-rule.md +31 -40
  17. package/.claude/skills/workflow-commands/delegation.md +12 -8
  18. package/.claude/skills/workflow-commands/entry-sequence.md +1 -3
  19. package/.claude/skills/workflow-commands/followup.md +55 -77
  20. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -5
  21. package/.claude/skills/workflow-commands/fullrun.md +4 -6
  22. package/.claude/skills/workflow-commands/halfrun.md +3 -7
  23. package/.claude/skills/workflow-commands/into-target.md +1 -2
  24. package/.claude/skills/workflow-commands/issue-comments.md +43 -60
  25. package/.claude/skills/workflow-commands/issue-scout.md +3 -2
  26. package/.claude/skills/workflow-commands/kickoff.md +65 -74
  27. package/.claude/skills/workflow-commands/latest-gate.md +2 -6
  28. package/.claude/skills/workflow-commands/needs-human-review.md +2 -5
  29. package/.claude/skills/workflow-commands/observation-filing.md +59 -118
  30. package/.claude/skills/workflow-commands/observation-ledger.md +52 -112
  31. package/.claude/skills/workflow-commands/pre-gate-cut.md +88 -142
  32. package/.claude/skills/workflow-commands/prerequisite.md +11 -17
  33. package/.claude/skills/workflow-commands/progress-watcher.md +23 -33
  34. package/.claude/skills/workflow-commands/prrun.md +3 -5
  35. package/.claude/skills/workflow-commands/retrospective.md +35 -54
  36. package/.claude/skills/workflow-commands/split-assessment.md +32 -58
  37. package/.claude/skills/workflow-commands/target-repository.md +1 -2
  38. package/.claude/skills/workflow-commands/working-tree-hold.md +54 -73
  39. package/.codex/hooks.json +6 -6
  40. package/.github/actions/setup-node/action.yml +1 -1
  41. package/.github/workflows/ci.yml +1 -1
  42. package/.github/workflows/publish.yml +2 -2
  43. package/CLAUDE.md +7 -6
  44. package/cspell/index.yaml +7 -0
  45. package/dist/CLAUDE.md +7 -6
  46. package/dist/commands/document-section-cli.js +67 -34
  47. package/dist/hooks/batch-guard.js +6 -6
  48. package/dist/hooks/chunk-4KKD2UTK.js +66 -0
  49. package/dist/hooks/{chunk-GPLGZ6IC.js → chunk-6TS2JD6D.js} +14 -3
  50. package/dist/hooks/chunk-7DSBJVFH.js +50 -0
  51. package/dist/hooks/{chunk-5H4SSDSD.js → chunk-A65Y4YOR.js} +3 -3
  52. package/dist/hooks/{chunk-3C4XRYG6.js → chunk-APUO6U4J.js} +102 -19
  53. package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
  54. package/dist/hooks/chunk-GP4IEC4O.js +20 -0
  55. package/dist/hooks/chunk-JP45SOCJ.js +56 -0
  56. package/dist/hooks/{chunk-OLMO72ON.js → chunk-LAOV7NLM.js} +1 -1
  57. package/dist/hooks/chunk-M4HGZV7U.js +83 -0
  58. package/dist/hooks/{chunk-TBJFTSUY.js → chunk-O6GDRNJB.js} +9 -7
  59. package/dist/hooks/{chunk-HFGWYGUV.js → chunk-PF2NZL2A.js} +5 -5
  60. package/dist/hooks/{chunk-WNLVGT7E.js → chunk-PTICX4OL.js} +4 -5
  61. package/dist/hooks/{chunk-HHI6FO3R.js → chunk-RT27QRJW.js} +763 -1515
  62. package/dist/hooks/{chunk-7WQBPLUN.js → chunk-SYOTDYCI.js} +1149 -1095
  63. package/dist/hooks/{chunk-LIAFCXL7.js → chunk-TNUMY6NL.js} +1519 -1332
  64. package/dist/hooks/chunk-UPUZF3AN.js +279 -0
  65. package/dist/hooks/{chunk-Z4XI7CZS.js → chunk-WK6SMBIC.js} +1932 -784
  66. package/dist/hooks/codex-hook-adapter.js +17 -14
  67. package/dist/hooks/duplicate-read-guard.js +7 -7
  68. package/dist/hooks/format-edited.js +5 -4
  69. package/dist/hooks/inputs.json +302 -247
  70. package/dist/hooks/investigation-guard.js +8 -11
  71. package/dist/hooks/pretool-guard.js +16 -13
  72. package/dist/hooks/session-lang.js +372 -26
  73. package/dist/hooks/stop-guard.js +2593 -189
  74. package/dist/josh.js +2128 -282
  75. package/dist/managed-marker/index.js +1 -2
  76. package/dist/version/index.js +15 -12
  77. package/eslint/base.js +2 -1
  78. package/eslint/rules/unicorn.js +16 -0
  79. package/lefthook/base.yml +5 -3
  80. package/package.json +14 -12
  81. package/prompts/coding-standards.md +12 -22
  82. package/prompts/collaboration-workflow/file-edits.md +25 -45
  83. package/prompts/collaboration-workflow/gh-rest.md +19 -36
  84. package/prompts/collaboration-workflow/glossary.md +5 -7
  85. package/prompts/collaboration-workflow/issue-citation.md +6 -14
  86. package/prompts/collaboration-workflow/issue-template.md +40 -32
  87. package/prompts/collaboration-workflow/lane-instructions.md +35 -0
  88. package/prompts/collaboration-workflow/operating-rules.md +33 -46
  89. package/prompts/collaboration-workflow/output-bounds.md +15 -15
  90. package/prompts/collaboration-workflow/overview.md +14 -19
  91. package/prompts/collaboration-workflow/plan-comment.md +13 -13
  92. package/prompts/collaboration-workflow/principles.md +57 -54
  93. package/prompts/collaboration-workflow/proposal-request.md +1 -9
  94. package/prompts/collaboration-workflow/report-format.md +41 -49
  95. package/prompts/collaboration-workflow/residency.md +15 -11
  96. package/prompts/collaboration-workflow/rule-delivery.md +7 -15
  97. package/prompts/collaboration-workflow/shell-body.md +10 -20
  98. package/prompts/collaboration-workflow/turn-batching.md +8 -14
  99. package/prompts/collaboration-workflow/upstream-interrupt.md +26 -34
  100. package/prompts/collaboration-workflow/wip-cap.md +26 -32
  101. package/prompts/collaboration-workflow.md +25 -25
  102. package/prompts/refactoring.md +33 -43
  103. package/prompts/review-rubric.md +84 -129
  104. package/prompts/review.md +110 -141
  105. package/prompts/sonar-hotspot-handling.md +22 -50
  106. package/prompts/testing-guide.md +48 -99
  107. package/scripts/adopt/adopt-toolkits.ts +2 -4
  108. package/scripts/agent/agent-argv.ts +20 -4
  109. package/scripts/agent/agent-exit-record.ts +23 -1
  110. package/scripts/agent/agent-role-profile.ts +101 -45
  111. package/scripts/agent/agent-safe-text.ts +60 -0
  112. package/scripts/agent/claude-agent-argv.ts +14 -0
  113. package/scripts/agent/codex-agent-argv.ts +9 -2
  114. package/scripts/agent/lane-instructions.ts +144 -0
  115. package/scripts/backlog/backlog-budget-cli.ts +1 -2
  116. package/scripts/backlog/backlog-budget.ts +4 -4
  117. package/scripts/backlog/backlog-drive-carried.ts +26 -0
  118. package/scripts/backlog/backlog-drive-cli.ts +10 -5
  119. package/scripts/backlog/backlog-drive-named.ts +3 -5
  120. package/scripts/backlog/backlog-drive-restore.ts +28 -2
  121. package/scripts/backlog/backlog-drive-seats.ts +27 -0
  122. package/scripts/backlog/backlog-drive.ts +11 -6
  123. package/scripts/backlog/backlog-occupied.ts +56 -0
  124. package/scripts/backlog/backlog-ready.ts +59 -15
  125. package/scripts/behavior/behavior-rules.ts +3 -2
  126. package/scripts/cost-runtime/codex-usage.ts +4 -2
  127. package/scripts/cost-runtime/cost-cli.ts +1 -2
  128. package/scripts/delegation/duplicate-read-guard.ts +2 -2
  129. package/scripts/delegation/investigation-guard.ts +15 -65
  130. package/scripts/delegation/investigation-reads.ts +1 -2
  131. package/scripts/discovery/repo-discovery.ts +4 -2
  132. package/scripts/discovery/repo-origin.ts +2 -4
  133. package/scripts/doctor/doctor-logic.ts +6 -3
  134. package/scripts/document/document-byte-budget.ts +28 -27
  135. package/scripts/document/document-byte-check.ts +1 -2
  136. package/scripts/document/document-section-cli.ts +17 -5
  137. package/scripts/document/entry-read-budget.ts +3 -3
  138. package/scripts/document/read-set-cli.ts +4 -1
  139. package/scripts/epic/epic-audit-orphans.ts +5 -8
  140. package/scripts/epic/epic-audit-rationale.ts +8 -2
  141. package/scripts/epic/epic-audit.ts +52 -1
  142. package/scripts/epic/epic-bundle-cli.ts +1 -2
  143. package/scripts/epic/epic-bundle-evidence.ts +49 -26
  144. package/scripts/epic/epic-bundle-referenced.ts +14 -3
  145. package/scripts/epic/epic-bundle.ts +17 -9
  146. package/scripts/epic/epic-chains.ts +6 -2
  147. package/scripts/epic/epic-close-comment.ts +1 -2
  148. package/scripts/followup/git-followup-workflow.ts +19 -1
  149. package/scripts/gate/check-inputs.ts +129 -0
  150. package/scripts/gate/check-reuse.ts +162 -0
  151. package/scripts/gate/core-budget.ts +93 -44
  152. package/scripts/gate/core-wait.ts +130 -0
  153. package/scripts/gate/free-cores.ts +49 -0
  154. package/scripts/gate/gate-ledger.ts +17 -3
  155. package/scripts/gate/gate-plan.ts +23 -1
  156. package/scripts/gate/gate-report.ts +15 -2
  157. package/scripts/gate/gate-run-steps.ts +150 -0
  158. package/scripts/gate/held-mark.ts +55 -0
  159. package/scripts/gate/machine-capacity.ts +70 -11
  160. package/scripts/gate/pre-commit-type-check.ts +14 -1
  161. package/scripts/gate/verification-gate.ts +65 -127
  162. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  163. package/scripts/gh/git-gh-issue-write.ts +24 -2
  164. package/scripts/gh/git-pr.ts +19 -3
  165. package/scripts/git/git-common-directory.ts +3 -3
  166. package/scripts/git/git-diff-reads.ts +16 -9
  167. package/scripts/git/git-execa-error.ts +1 -2
  168. package/scripts/git/main-merge-regenerate.ts +78 -0
  169. package/scripts/git/main-merge.ts +14 -5
  170. package/scripts/hooks/batch-guard.ts +5 -8
  171. package/scripts/hooks/format-edited-file.ts +10 -4
  172. package/scripts/hooks/pre-commit-lint.ts +91 -0
  173. package/scripts/hooks/pre-push-unit.ts +15 -4
  174. package/scripts/hooks/pretool-guard.ts +48 -79
  175. package/scripts/hooks/reserved-run.ts +1 -0
  176. package/scripts/hooks/run-hook.sh +10 -12
  177. package/scripts/hooks/step-zero-notice.ts +3 -2
  178. package/scripts/hooks/stop-actions.ts +204 -0
  179. package/scripts/hooks/stop-guard.ts +62 -21
  180. package/scripts/init/hook-command-rewrite.ts +6 -2
  181. package/scripts/init/hook-launch.ts +12 -7
  182. package/scripts/init/init-logic-deploy-vps.ts +4 -2
  183. package/scripts/issue/behavior-change-lint.ts +12 -6
  184. package/scripts/issue/expected-effect-lint.ts +59 -0
  185. package/scripts/issue/issue-cite-cli.ts +35 -15
  186. package/scripts/issue/issue-classification.ts +1 -1
  187. package/scripts/issue/issue-lint-cli.ts +5 -3
  188. package/scripts/issue/issue-scout-cli.ts +22 -16
  189. package/scripts/issue/markdown-section.ts +19 -5
  190. package/scripts/josh/command-usage.ts +110 -0
  191. package/scripts/josh/file-map-stamp.ts +1 -3
  192. package/scripts/josh/hook-decision.ts +17 -2
  193. package/scripts/josh/josh-command-map.ts +2 -0
  194. package/scripts/josh/josh-command-types.ts +55 -9
  195. package/scripts/josh/josh-commands-ai.ts +13 -0
  196. package/scripts/josh/josh-commands-development.ts +1 -1
  197. package/scripts/josh/josh-commands-hooks.ts +6 -0
  198. package/scripts/josh/josh-commands-lane.ts +10 -1
  199. package/scripts/josh/josh-commands-workflow.ts +5 -0
  200. package/scripts/josh/josh-logic.ts +6 -15
  201. package/scripts/josh/josh-usage-automation.ts +121 -0
  202. package/scripts/josh/josh-usage-backlog.ts +180 -0
  203. package/scripts/josh/josh-usage-developer.ts +230 -0
  204. package/scripts/josh/josh-usage-hooks.ts +38 -0
  205. package/scripts/josh/josh-usage-issue.ts +205 -0
  206. package/scripts/josh/josh-usage-review.ts +101 -0
  207. package/scripts/josh/josh-usage-run.ts +252 -0
  208. package/scripts/josh/josh-usage-session.ts +238 -0
  209. package/scripts/josh/josh-usage-workflow.ts +212 -0
  210. package/scripts/josh/josh.ts +14 -5
  211. package/scripts/josh/package-directory.ts +20 -0
  212. package/scripts/lane/lane-await.ts +3 -2
  213. package/scripts/lane/lane-capacity.ts +8 -2
  214. package/scripts/lane/lane-check-stats.ts +56 -0
  215. package/scripts/lane/lane-child-invocation.ts +49 -5
  216. package/scripts/lane/lane-dispatch.ts +3 -3
  217. package/scripts/lane/lane-guard-policy.ts +5 -5
  218. package/scripts/lane/lane-handoff.ts +8 -1
  219. package/scripts/lane/lane-install.ts +12 -4
  220. package/scripts/lane/lane-ledger.ts +52 -1
  221. package/scripts/lane/lane-limit-cli.ts +8 -9
  222. package/scripts/lane/lane-load.ts +9 -2
  223. package/scripts/lane/lane-measure-cli.ts +4 -0
  224. package/scripts/lane/lane-parked.ts +18 -0
  225. package/scripts/lane/lane-preserve.ts +29 -1
  226. package/scripts/lane/lane-process-load.ts +125 -0
  227. package/scripts/lane/lane-process-stats.ts +93 -0
  228. package/scripts/lane/lane-registry.ts +17 -10
  229. package/scripts/lane/lane-restart.ts +5 -26
  230. package/scripts/lane/lane-session-action.ts +146 -0
  231. package/scripts/lane/lane-session-broker.ts +68 -125
  232. package/scripts/lane/lane-spent.ts +14 -3
  233. package/scripts/lane/lane-stats.ts +1 -0
  234. package/scripts/lane/lane-withdraw-cli.ts +71 -0
  235. package/scripts/lane/lane-withdraw.ts +182 -0
  236. package/scripts/lint/format.ts +4 -4
  237. package/scripts/lint/lint-parallel.ts +10 -3
  238. package/scripts/lint/lint-related-scope.ts +4 -1
  239. package/scripts/managed-marker/managed-marker-logic.ts +1 -2
  240. package/scripts/notify/telegram-test.ts +10 -5
  241. package/scripts/overrides/lockfile-overrides.ts +1 -3
  242. package/scripts/propagate/propagate.ts +1 -2
  243. package/scripts/release/release-progress.ts +3 -2
  244. package/scripts/report/report-format-reference.ts +2 -0
  245. package/scripts/report/report-lint.ts +7 -4
  246. package/scripts/review/acceptance-map-check.ts +61 -0
  247. package/scripts/review/acceptance-map.ts +205 -0
  248. package/scripts/review/review-brief-cli.ts +1 -1
  249. package/scripts/review/review-record-cli.ts +6 -2
  250. package/scripts/rules/agent-model.ts +59 -0
  251. package/scripts/rules/delivered-rules.ts +11 -2
  252. package/scripts/rules/destructive-command.ts +6 -9
  253. package/scripts/rules/direct-filing.ts +3 -7
  254. package/scripts/rules/early-heartbeat.ts +7 -18
  255. package/scripts/rules/file-body.ts +6 -13
  256. package/scripts/rules/filing-cap.ts +4 -6
  257. package/scripts/rules/firing-point.ts +6 -5
  258. package/scripts/rules/git-force.ts +5 -9
  259. package/scripts/rules/guard-remedy.ts +117 -0
  260. package/scripts/rules/implementation-cut.ts +37 -35
  261. package/scripts/rules/index-guard.ts +4 -7
  262. package/scripts/rules/issue-comments.ts +67 -16
  263. package/scripts/rules/josh-git-bare.ts +3 -6
  264. package/scripts/rules/lane-background.ts +10 -21
  265. package/scripts/rules/lane-carry-conflict.ts +7 -17
  266. package/scripts/rules/lane-interactive-ask.ts +6 -11
  267. package/scripts/rules/lane-park.ts +5 -13
  268. package/scripts/rules/lane-split-park.ts +2 -7
  269. package/scripts/rules/lane-switch-main.ts +9 -12
  270. package/scripts/rules/last-prompt.ts +50 -5
  271. package/scripts/rules/oracle-consulted.ts +4 -5
  272. package/scripts/rules/oracle-list-cli.ts +7 -0
  273. package/scripts/rules/piped-verification.ts +84 -32
  274. package/scripts/rules/poll-loop.ts +7 -17
  275. package/scripts/rules/pre-gate-cut.ts +7 -21
  276. package/scripts/rules/prior-comment-read.ts +7 -5
  277. package/scripts/rules/protected-files.ts +5 -9
  278. package/scripts/rules/raw-field-body.ts +3 -7
  279. package/scripts/rules/rule-body-guard.ts +4 -11
  280. package/scripts/rules/rule-list.ts +4 -1
  281. package/scripts/rules/rule-registry-rows.ts +10 -0
  282. package/scripts/rules/rule-registry.ts +7 -4
  283. package/scripts/rules/rule-rewrite.ts +114 -0
  284. package/scripts/rules/rule-value.ts +7 -1
  285. package/scripts/rules/run-tail-rule.ts +6 -22
  286. package/scripts/rules/shell-body-trigger.ts +4 -6
  287. package/scripts/rules/shell-segments.ts +22 -0
  288. package/scripts/rules/stop-decision.ts +77 -0
  289. package/scripts/rules/stop-rules.ts +164 -77
  290. package/scripts/rules/tail-commands.ts +15 -1
  291. package/scripts/rules/test-declared-commit.ts +4 -10
  292. package/scripts/rules/third-party-write.ts +5 -10
  293. package/scripts/rules/worktree-guard.ts +5 -12
  294. package/scripts/run/board/run-board-cli.ts +2 -0
  295. package/scripts/run/board/run-board-closed.ts +8 -2
  296. package/scripts/run/board/run-board-github.ts +19 -3
  297. package/scripts/run/board/run-board-header.ts +4 -1
  298. package/scripts/run/board/run-board-idle.ts +31 -0
  299. package/scripts/run/board/run-board-labels.ts +2 -0
  300. package/scripts/run/board/run-board-layout.ts +1 -1
  301. package/scripts/run/board/run-board-read.ts +25 -12
  302. package/scripts/run/board/run-board-reason.ts +17 -4
  303. package/scripts/run/board/run-board-render-legend.ts +1 -0
  304. package/scripts/run/board/run-board-render-slot.ts +1 -1
  305. package/scripts/run/board/run-board-render-waits.ts +41 -0
  306. package/scripts/run/board/run-board-render.ts +6 -1
  307. package/scripts/run/board/run-board-row-yield.ts +1 -0
  308. package/scripts/run/board/run-board-state.ts +9 -0
  309. package/scripts/run/board/run-board-status.ts +89 -23
  310. package/scripts/run/board/run-board-thanks.ts +7 -2
  311. package/scripts/run/board/run-board-tick.ts +37 -8
  312. package/scripts/run/carry/run-carry-args.ts +1 -3
  313. package/scripts/run/carry/run-carry-conversation.ts +7 -2
  314. package/scripts/run/cut/run-cut-cli.ts +18 -22
  315. package/scripts/run/cut/run-cut-decline.ts +72 -0
  316. package/scripts/run/cut/run-cut-ready.ts +74 -0
  317. package/scripts/run/cut/run-cut-report.ts +29 -0
  318. package/scripts/run/cut/run-cut.ts +10 -1
  319. package/scripts/run/detached-launch.ts +23 -4
  320. package/scripts/run/entry/run-entry-cli.ts +8 -1
  321. package/scripts/run/event/run-event-render.ts +1 -0
  322. package/scripts/run/event/run-event-scope.ts +19 -1
  323. package/scripts/run/event/run-event-stream-emit.ts +13 -6
  324. package/scripts/run/event/run-event-stream.ts +4 -0
  325. package/scripts/run/hold/run-hold-cli.ts +14 -3
  326. package/scripts/run/hold/run-hold-session.ts +46 -0
  327. package/scripts/run/hold/run-hold.ts +13 -4
  328. package/scripts/run/launch-priority.ts +32 -0
  329. package/scripts/run/merge/run-merge-cli.ts +16 -42
  330. package/scripts/run/merge/run-merge-ship.ts +59 -1
  331. package/scripts/run/merge/run-merge-signals.ts +99 -0
  332. package/scripts/run/merge/run-merge-steps.ts +11 -1
  333. package/scripts/run/merge/run-merge.ts +22 -9
  334. package/scripts/run/run-ending.ts +31 -3
  335. package/scripts/run/run-liveness.ts +1 -1
  336. package/scripts/run/run-parent-cut-hook.ts +5 -10
  337. package/scripts/run/run-poll-hook.ts +157 -0
  338. package/scripts/run/run-preflight.ts +1 -2
  339. package/scripts/run/run-step-cli.ts +22 -15
  340. package/scripts/run/run-step-section.ts +85 -0
  341. package/scripts/run/run-stop-notify.ts +1 -2
  342. package/scripts/run/run-watcher-guard.ts +6 -3
  343. package/scripts/run/ship/run-ship-cli.ts +5 -12
  344. package/scripts/run/ship/run-ship-detach.ts +9 -0
  345. package/scripts/run/ship/run-ship-next.ts +10 -2
  346. package/scripts/run/ship/run-ship-overlap.ts +195 -0
  347. package/scripts/run/ship/run-ship-pre-detach.ts +33 -15
  348. package/scripts/run/ship/run-ship-preflight.ts +28 -2
  349. package/scripts/run/ship/run-ship-return.ts +31 -7
  350. package/scripts/run/ship/run-ship-review-steps.ts +25 -14
  351. package/scripts/run/ship/run-ship-review.ts +15 -2
  352. package/scripts/run/ship/run-ship-steps.ts +1 -1
  353. package/scripts/run/ship/run-ship-stop-context.ts +206 -0
  354. package/scripts/run/ship/run-ship-sync.ts +14 -6
  355. package/scripts/run/tidy/run-tidy-cli.ts +3 -3
  356. package/scripts/run/tidy/run-tidy-lanes.ts +3 -2
  357. package/scripts/safe-chain/project-config.ts +7 -2
  358. package/scripts/sync/sync-hook-safety.ts +8 -2
  359. package/scripts/test/test-e2e-guard.ts +30 -5
  360. package/scripts/test/test-unit-guard.ts +16 -1
  361. package/scripts/test/unit-worker-share.ts +26 -3
  362. package/scripts/time-runtime/time-batch-guard.ts +3 -3
  363. package/scripts/time-runtime/time-hook-transcript.ts +3 -2
  364. package/scripts/time-runtime/time-shell.ts +39 -4
  365. package/scripts/time-runtime/time-spans.ts +1 -0
  366. package/scripts/time-runtime/time-tool-call.ts +7 -0
  367. package/scripts/version/effective-upstream.ts +3 -4
  368. package/scripts/version/kit-version-config.ts +8 -2
  369. package/scripts/version/latest-corepack.ts +1 -2
  370. package/scripts/version/release-hold.ts +11 -4
  371. package/scripts/version/version-commands.ts +12 -4
  372. package/templates/gitignore +6 -2
  373. package/dist/hooks/chunk-D5QBTOCZ.js +0 -68
  374. package/dist/hooks/chunk-UT7YC3N2.js +0 -56
@@ -2,6 +2,7 @@
2
2
  import { setTimeout as sleep } from 'node:timers/promises'
3
3
  import { fileURLToPath } from 'node:url'
4
4
  import { styleText } from 'node:util'
5
+ import { core_budget } from '#scripts/gate/core-budget'
5
6
  import { machine_capacity } from '#scripts/gate/machine-capacity'
6
7
  import { telegram_notify } from '#scripts/notify/telegram-notify'
7
8
  import { run_progress } from '#scripts/run/progress/run-progress'
@@ -51,6 +52,7 @@ const LIVE_PORTS: BoardPorts = {
51
52
  read_plan: async (scope) => await run_board_plan.read_plan(scope),
52
53
  read_local: run_board_read.read_local,
53
54
  read_machine: machine_capacity.read_sample,
55
+ read_waits: () => core_budget.budget_waits(),
54
56
  read_usage: run_board_usage_read.usage_reader(),
55
57
  read_closed: async (issues) => await run_board_closed.read_all(issues),
56
58
  read_thanks: async () => await run_board_thanks_read.read_thanks(),
@@ -18,10 +18,13 @@ interface ClosedIssue {
18
18
  closed_ms: number | undefined
19
19
  // Closed by a merged pull request, rather than closed by hand.
20
20
  is_merged: boolean
21
+ // Closed as not planned — withdrawn rather than finished (joshuafolkken/kit#3958).
22
+ is_not_planned: boolean
21
23
  }
22
24
 
23
- const CLOSED_FIELDS = 'title,state,closedAt,labels'
25
+ const CLOSED_FIELDS = 'title,state,stateReason,closedAt,labels'
24
26
  const CLOSED_STATE = 'CLOSED'
27
+ const NOT_PLANNED_REASON = 'not_planned'
25
28
  const STILL_OPEN = 'open' as const
26
29
 
27
30
  // What one issue answered: closed, still open, or `undefined` when it could not be read.
@@ -41,6 +44,7 @@ const READ_CONCURRENCY = 4
41
44
  const closed_schema = z.looseObject({
42
45
  title: z.string(),
43
46
  state: z.string(),
47
+ stateReason: z.string().nullish(),
44
48
  closedAt: z.string().nullish(),
45
49
  labels: z.array(issue_label_schema).default([]),
46
50
  })
@@ -69,7 +73,9 @@ async function read_closed(issue: number): Promise<ClosedRead> {
69
73
 
70
74
  const labels = view.labels.map((label) => label.name)
71
75
 
72
- return { title: view.title, labels, closed_ms: closed_ms_of(view), is_merged }
76
+ const is_not_planned = view.stateReason === NOT_PLANNED_REASON
77
+
78
+ return { title: view.title, labels, closed_ms: closed_ms_of(view), is_merged, is_not_planned }
73
79
  }
74
80
 
75
81
  async function read_all(
@@ -2,6 +2,7 @@ import { run_progress } from '#scripts/run/progress/run-progress'
2
2
  import { run_progress_cli } from '#scripts/run/progress/run-progress-cli'
3
3
  import type { ClosedAnswer } from './run-board-closed'
4
4
  import { run_board_fetch, type Fetch } from './run-board-fetch'
5
+ import { run_board_idle } from './run-board-idle'
5
6
  import { run_board_layout, type BoardPlan } from './run-board-layout'
6
7
  import type { LocalRead } from './run-board-read'
7
8
  import { run_board_state, type BoardPorts, type BoardState, type Pace } from './run-board-state'
@@ -72,8 +73,23 @@ function fold_plan(state: BoardState): BoardState {
72
73
  }
73
74
  }
74
75
 
75
- function statuses_for(plan: BoardPlan, local: LocalRead): ReadonlyMap<number, ItemStatus> {
76
- return run_board_status.statuses_of(local.events, local.lanes, run_board_layout.numbers_of(plan))
76
+ // joshuafolkken/kit#3969: the planned issues whose open lane may be this run's though the stream never
77
+ // saw it launched. An earlier run's lane is not, so its issue waits until this run launches it.
78
+ function lanes_in_run(plan: BoardPlan, local: LocalRead): ReadonlySet<number> {
79
+ const earlier = new Set(local.earlier_lanes?.map(Number))
80
+
81
+ return new Set([...run_board_layout.numbers_of(plan)].filter((issue) => !earlier.has(issue)))
82
+ }
83
+
84
+ function statuses_for(
85
+ state: BoardState,
86
+ plan: BoardPlan,
87
+ local: LocalRead,
88
+ ): ReadonlyMap<number, ItemStatus> {
89
+ const in_run = lanes_in_run(plan, local)
90
+ const stopped = run_board_idle.stopped_of(state.idle_since, state.read_ms)
91
+
92
+ return run_board_status.statuses_of(local.events, local.lanes, in_run, stopped)
77
93
  }
78
94
 
79
95
  // The children the run touched that the open listing no longer holds and the board has not read yet.
@@ -83,7 +99,7 @@ function unread_closed(state: BoardState, local: LocalRead): Array<number> {
83
99
 
84
100
  if (open === undefined || state.plan === undefined) return []
85
101
 
86
- const touched = [...statuses_for(state.plan, local).keys()]
102
+ const touched = [...statuses_for(state, state.plan, local).keys()]
87
103
 
88
104
  return touched.filter((issue) => !open.has(issue) && !state.closed.has(issue))
89
105
  }
@@ -1,5 +1,6 @@
1
1
  import { styleText } from 'node:util'
2
2
  import { backlog_budget } from '#scripts/backlog/backlog-budget'
3
+ import type { BudgetWait } from '#scripts/gate/core-wait'
3
4
  import { run_board_fit } from './run-board-fit'
4
5
  import { run_board_fresh } from './run-board-fresh'
5
6
  import { run_board_labels } from './run-board-labels'
@@ -65,6 +66,8 @@ interface BoardHeader {
65
66
  machine: MachineGauges | undefined
66
67
  // Each running lane's CPU and memory, `undefined` where none could be read.
67
68
  usages?: LaneUsages | undefined
69
+ // The claims waiting in the machine-wide core budget (`run-board-render-waits.ts`).
70
+ budget_waits?: ReadonlyArray<BudgetWait> | undefined
68
71
  // How long each phase usually takes; a running row's phase time is colored against it.
69
72
  usual?: UsualTimes | undefined
70
73
  // The spinner's frame a running run and a running row turn, `undefined` where the output is not a
@@ -107,7 +110,7 @@ function count_state(rows: ReadonlyArray<BoardRow>, state: BoardRow['state']): n
107
110
  function counts_of(layout: BoardLayout): BoardCounts {
108
111
  const total = run_board_layout.rows_of(layout).length
109
112
  const parked = count_state(layout.active, 'parked')
110
- const settled = count_state(layout.active, 'merged') + parked + count_state(layout.active, 'done')
113
+ const settled = layout.active.filter((row) => run_board_layout.is_settled(row.state)).length
111
114
  const running = count_state(layout.active, 'running')
112
115
 
113
116
  return { total, settled, parked, running, remaining: total - settled - running }
@@ -0,0 +1,31 @@
1
+ import { lane_await } from '#scripts/lane/lane-await'
2
+
3
+ // A lane is drawn stopped only once it has read idle for `lane_await.RECONFIRM_MS`: a boundary cut drops
4
+ // the child's process for a moment before its resume takes over, and one bare probe cannot tell that
5
+ // handoff from a child that died — `lane:await` re-confirms a disappearance over the same window. A live
6
+ // board carries when each lane was first read idle across its reads; a single frame has no earlier read,
7
+ // so it reads the lanes again after the window (`run-board-tick.ts`).
8
+
9
+ /** When each lane the last read found idle was first read so. */
10
+ type IdleSince = ReadonlyMap<string, number>
11
+
12
+ const NO_IDLE: IdleSince = new Map()
13
+
14
+ // The lanes read idle now, each keeping the moment it was first read so; a lane read working drops out.
15
+ function since_of(before: IdleSince, idle: ReadonlyArray<string>, now_ms: number): IdleSince {
16
+ return new Map(idle.map((lane) => [lane, before.get(lane) ?? now_ms]))
17
+ }
18
+
19
+ // The lanes idle for the whole window as of the read at `read_ms`; none before any read.
20
+ function stopped_of(since: IdleSince, read_ms: number | undefined): Array<string> {
21
+ if (read_ms === undefined) return []
22
+
23
+ const lasted = [...since].filter(([, at]) => read_ms - at >= lane_await.RECONFIRM_MS)
24
+
25
+ return lasted.map(([lane]) => lane)
26
+ }
27
+
28
+ const run_board_idle = { NO_IDLE, since_of, stopped_of }
29
+
30
+ export type { IdleSince }
31
+ export { run_board_idle }
@@ -43,6 +43,7 @@ const WORDS = {
43
43
  merged: 'merged',
44
44
  parked: 'parked',
45
45
  done: 'done',
46
+ withdrawn: 'withdrawn',
46
47
  in_progress: 'running',
47
48
  stopped: 'stopped',
48
49
  waiting: 'waiting',
@@ -104,6 +105,7 @@ const STATE_ICONS: Readonly<Record<ItemState, string>> = {
104
105
  waiting: '⏳',
105
106
  human: '🙋',
106
107
  stopped: '🛑',
108
+ withdrawn: '🚫',
107
109
  }
108
110
 
109
111
  // The blocking edge, chosen on the same rule: ⛓ is a text symbol.
@@ -82,7 +82,7 @@ interface Drawn extends Overlay {
82
82
  reason_of: ReasonOf | undefined
83
83
  }
84
84
 
85
- const SETTLED_STATES: ReadonlySet<ItemState> = new Set(['merged', 'parked', 'done'])
85
+ const SETTLED_STATES: ReadonlySet<ItemState> = new Set(['merged', 'parked', 'done', 'withdrawn'])
86
86
 
87
87
  function is_settled(state: ItemState): boolean {
88
88
  return SETTLED_STATES.has(state)
@@ -1,7 +1,8 @@
1
1
  import type { NamedPlan } from '#scripts/backlog/backlog-plan'
2
+ import { backlog_ready } from '#scripts/backlog/backlog-ready'
2
3
  import { backlogrun_launch } from '#scripts/backlog/backlogrun-launch'
3
- import { lane_capacity } from '#scripts/lane/lane-capacity'
4
4
  import { lane_registry, type LaneInfo } from '#scripts/lane/lane-registry'
5
+ import { lane_spent } from '#scripts/lane/lane-spent'
5
6
  import { run_carry } from '#scripts/run/carry/run-carry'
6
7
  import { run_carry_ended } from '#scripts/run/carry/run-carry-ended'
7
8
  import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
@@ -33,21 +34,22 @@ interface LocalRead {
33
34
  is_stranded: boolean
34
35
  // The newest operation of each session of the run, read off the end of its log.
35
36
  operations?: SessionOperations | undefined
36
- // How many more lanes the limit allows beside the live ones; `undefined` where the limit could not
37
- // be read, so nothing is said about it.
37
+ // The open lanes no child is working in at this read (`lane-spent.ts`); one is drawn stopped once it
38
+ // has stayed so (`run-board-idle.ts`).
39
+ idle_lanes?: ReadonlyArray<string> | undefined
40
+ // The open lanes that last took a child before the run started — an earlier run's, until this run
41
+ // dispatches into one again.
42
+ earlier_lanes?: ReadonlyArray<string> | undefined
43
+ // How many more lanes the limit allows beside the live ones, as `lane:limit` counts them;
44
+ // `undefined` where the limit could not be read, so nothing is said about it.
38
45
  free_lanes?: number | undefined
46
+ // The issues whose lanes hold a place under the limit (`backlog-occupied.ts`), named beside a row
47
+ // that waits for a free lane.
48
+ occupied_lanes?: ReadonlyArray<string> | undefined
39
49
  // How long each phase usually takes, from the repository's lane ledger.
40
50
  usual?: UsualTimes | undefined
41
51
  }
42
52
 
43
- async function free_lanes_of(lanes: ReadonlyArray<LaneInfo>): Promise<number | undefined> {
44
- const choice = await lane_capacity.lane_limit()
45
-
46
- if (choice.kind !== 'limit') return undefined
47
-
48
- return lane_capacity.free_lanes(choice.limit, lane_registry.live_count(lanes))
49
- }
50
-
51
53
  const NO_SPAN: SpanHere = { span: undefined, resume: undefined, transcript: undefined }
52
54
 
53
55
  interface SpanHere {
@@ -85,6 +87,13 @@ function sources_of(
85
87
  }
86
88
  }
87
89
 
90
+ // joshuafolkken/kit#3969: a lane whose last dispatch is unknown is not called earlier.
91
+ function earlier_lanes(lanes: ReadonlyArray<LaneInfo>, started_ms: number): Array<string> {
92
+ return lanes
93
+ .filter((lane) => lane.dispatched_ms !== undefined && lane.dispatched_ms < started_ms)
94
+ .map((lane) => lane.issue)
95
+ }
96
+
88
97
  // `undefined` when no run has started here.
89
98
  async function read_local(): Promise<LocalRead | undefined> {
90
99
  const repository = await run_carry.repository_directory()
@@ -94,6 +103,7 @@ async function read_local(): Promise<LocalRead | undefined> {
94
103
 
95
104
  const events = run_event_stream.read_events(run_event_stream.target_of(repository))
96
105
  const lanes = await lane_registry.list_lanes()
106
+ const occupied = await backlog_ready.occupied_lanes(lanes)
97
107
 
98
108
  return {
99
109
  started_ms: span.started_ms,
@@ -104,7 +114,10 @@ async function read_local(): Promise<LocalRead | undefined> {
104
114
  resume,
105
115
  is_stranded: run_stranded_detect.is_stranded_at(repository),
106
116
  operations: run_board_operation_read.read(sources_of(repository, lanes, transcript)),
107
- free_lanes: await free_lanes_of(lanes),
117
+ idle_lanes: lane_spent.idle_lanes(lanes),
118
+ earlier_lanes: earlier_lanes(lanes, span.started_ms),
119
+ free_lanes: await backlog_ready.free_lanes_beside(lanes),
120
+ occupied_lanes: occupied.map((lane) => lane.issue),
108
121
  usual: run_board_usual.read(repository),
109
122
  }
110
123
  }
@@ -5,6 +5,7 @@ import { run_event_setup } from '#scripts/run/event/run-event-setup'
5
5
  import type { RunEvent } from '#scripts/run/event/run-event-stream'
6
6
  import { run_board_layout, type Overlay, type ReasonOf, type RowReason } from './run-board-layout'
7
7
  import { run_board_setup } from './run-board-setup'
8
+ import type { ItemStatus } from './run-board-status'
8
9
 
9
10
  // Why each waiting row waits — pure, so the words are tested apart from the screen. A row whose lane is
10
11
  // being opened says the stage that lane is at; any other says what `backlog:next` would answer for it,
@@ -35,6 +36,9 @@ interface ReasonRead extends Overlay {
35
36
  events: ReadonlyArray<RunEvent>
36
37
  // How many more lanes the limit allows; `undefined` where it could not be read.
37
38
  free_lanes: number | undefined
39
+ // The issues whose lanes fill the limit, named after `no free lane` so a lane an earlier run left
40
+ // behind shows on the row it holds back.
41
+ occupied_lanes?: ReadonlyArray<string> | undefined
38
42
  // A stopped run opens no lane, so a stage its stream ends on is not one any lane is at.
39
43
  is_stopped: boolean
40
44
  }
@@ -63,10 +67,13 @@ function is_untriaged_candidate(child: EpicChild, read: ReasonRead): boolean {
63
67
  return !epic_triage.is_triaged(child) && !run_board_layout.is_blocked(child, read)
64
68
  }
65
69
 
70
+ // A child whose lane stands with no one in it still holds the paths it declared, as its open lane does.
71
+ function holds_paths(status: ItemStatus): boolean {
72
+ return status.state === 'running' || status.lane !== undefined
73
+ }
74
+
66
75
  function running_of(read: ReasonRead): Array<number> {
67
- return [...read.statuses]
68
- .filter(([, status]) => status.state === 'running')
69
- .map(([issue]) => issue)
76
+ return [...read.statuses].filter(([, status]) => holds_paths(status)).map(([issue]) => issue)
70
77
  }
71
78
 
72
79
  function pool_of(read: ReasonRead): Pool {
@@ -88,6 +95,12 @@ function overlap_of(child: EpicChild, read: ReasonRead, pool: Pool): string | un
88
95
  : `${WORDS.overlaps} ${String(clash.holder)} (${clash.path})`
89
96
  }
90
97
 
98
+ function no_lane_of(occupied: ReadonlyArray<string> = []): string {
99
+ if (occupied.length === 0) return WORDS.no_lane
100
+
101
+ return `${WORDS.no_lane} — ${occupied.join(' ')}`
102
+ }
103
+
91
104
  // A candidate nothing blocks, in the order `backlog:next` answers: the pool held for a judgment, then
92
105
  // a path a running issue declares, then a pool with no lane left.
93
106
  function offer_wait(child: EpicChild, read: ReasonRead, pool: Pool): string | undefined {
@@ -97,7 +110,7 @@ function offer_wait(child: EpicChild, read: ReasonRead, pool: Pool): string | un
97
110
 
98
111
  if (overlap !== undefined) return overlap
99
112
 
100
- return read.free_lanes === NO_FREE_LANES ? WORDS.no_lane : undefined
113
+ return read.free_lanes === NO_FREE_LANES ? no_lane_of(read.occupied_lanes) : undefined
101
114
  }
102
115
 
103
116
  function wait_of(child: EpicChild, read: ReasonRead, pool: Pool): RowReason | undefined {
@@ -32,6 +32,7 @@ const STATE_LEGEND: ReadonlyArray<readonly [ItemState, keyof Words]> = [
32
32
  ['merged', 'merged'],
33
33
  ['parked', 'parked'],
34
34
  ['done', 'done'],
35
+ ['withdrawn', 'withdrawn'],
35
36
  ['running', 'in_progress'],
36
37
  ['stopped', 'stopped'],
37
38
  ['waiting', 'waiting'],
@@ -9,7 +9,7 @@ import { run_board_usage_text } from './run-board-usage-text'
9
9
  // blanks on any other row.
10
10
 
11
11
  const { finish_of, text_of } = run_board_usage_text
12
- const FINISHED: ReadonlySet<ItemState> = new Set(['merged', 'done'])
12
+ const FINISHED: ReadonlySet<ItemState> = new Set(['merged', 'done', 'withdrawn'])
13
13
  const BLANK = ' '
14
14
 
15
15
  // What one frame's column is drawn from: each lane's usage, and the running rows the frame draws —
@@ -0,0 +1,41 @@
1
+ import { core_wait, type BudgetWait } from '#scripts/gate/core-wait'
2
+
3
+ // The claims waiting in the machine-wide core budget, under the header (joshuafolkken/kit#3943): a lane
4
+ // held back by memory pressure otherwise reads the same as one that is stuck. Each line names the
5
+ // command, the lane it runs in — the main checkout where none, and another repository's checkout by
6
+ // name, unlinked — how long it has waited, and why.
7
+
8
+ const MS_PER_SECOND = 1000
9
+ // 🚧 rather than ⏳, which a waiting row already draws, or 🚦, the gate stage's; an emoji a terminal
10
+ // draws two columns wide.
11
+ const BUDGET_ICON = '🚧'
12
+ const GAP = ' '
13
+ const MAIN_CHECKOUT = 'main'
14
+
15
+ function place_text(wait: BudgetWait, link: (reference: string) => string): string {
16
+ if (wait.elsewhere !== undefined) return wait.elsewhere
17
+
18
+ return wait.lane === undefined ? MAIN_CHECKOUT : link(wait.lane)
19
+ }
20
+
21
+ function wait_text(wait: BudgetWait, link: (reference: string) => string): string {
22
+ const lane = place_text(wait, link)
23
+ const seconds = `${String(Math.round(wait.waited_ms / MS_PER_SECOND))}s`
24
+ const parts = [wait.command ?? 'a step', lane, seconds, core_wait.REASON_TEXT[wait.reason]]
25
+
26
+ return `${BUDGET_ICON} ${parts.join(GAP)}`
27
+ }
28
+
29
+ // Nothing is drawn while no claim waits.
30
+ function wait_lines(
31
+ waits: ReadonlyArray<BudgetWait> | undefined,
32
+ link: (reference: string) => string,
33
+ ): Array<string> {
34
+ if (waits === undefined || waits.length === 0) return []
35
+
36
+ return ['', ...waits.map((wait) => wait_text(wait, link))]
37
+ }
38
+
39
+ const run_board_render_waits = { BUDGET_ICON, wait_lines }
40
+
41
+ export { run_board_render_waits }
@@ -24,6 +24,7 @@ import { run_board_render_slot, type UsageSlot } from './run-board-render-slot'
24
24
  import { run_board_render_tail } from './run-board-render-tail'
25
25
  import { run_board_render_thanks } from './run-board-render-thanks'
26
26
  import { run_board_render_title } from './run-board-render-title'
27
+ import { run_board_render_waits } from './run-board-render-waits'
27
28
  import { run_board_row_yield } from './run-board-row-yield'
28
29
  import type { Thanks } from './run-board-thanks-csv'
29
30
 
@@ -388,7 +389,11 @@ function bounded(parts: FrameParts, bounds: FrameBounds): Array<string> {
388
389
 
389
390
  function render(view: BoardView): Array<string> {
390
391
  const { header, notes } = view
391
- const above = [...run_board_header.header_lines(header), ...resume_lines(view.resume)]
392
+ const above = [
393
+ ...run_board_header.header_lines(header),
394
+ ...run_board_render_waits.wait_lines(header.budget_waits, header.link),
395
+ ...resume_lines(view.resume),
396
+ ]
392
397
  const parts = {
393
398
  above,
394
399
  plan: plan_sections(header, view.size),
@@ -9,6 +9,7 @@ import type { ItemState } from './run-board-status'
9
9
  const YIELD_STAGES: Readonly<Record<ItemState, YieldStage>> = {
10
10
  merged: 'finished',
11
11
  done: 'finished',
12
+ withdrawn: 'finished',
12
13
  waiting: 'pending',
13
14
  human: 'pending',
14
15
  parked: 'parked',
@@ -1,9 +1,11 @@
1
1
  import type { NamedPlan } from '#scripts/backlog/backlog-plan'
2
+ import type { BudgetWait } from '#scripts/gate/core-wait'
2
3
  import type { MachineSample } from '#scripts/gate/machine-capacity'
3
4
  import type { ClosedAnswer, ClosedIssue } from './run-board-closed'
4
5
  import type { Fetch } from './run-board-fetch'
5
6
  import type { TerminalSize } from './run-board-fit'
6
7
  import type { BoardForm } from './run-board-header'
8
+ import { run_board_idle, type IdleSince } from './run-board-idle'
7
9
  import type { BoardPlan } from './run-board-layout'
8
10
  import type { Link } from './run-board-link'
9
11
  import type { MachineGauges, MachineMark } from './run-board-machine'
@@ -20,6 +22,8 @@ interface BoardPorts {
20
22
  // `undefined` when no run has started here.
21
23
  read_local: () => Promise<LocalRead | undefined>
22
24
  read_machine: () => Promise<MachineSample>
25
+ // The claims waiting in the core budget, read on the machine's second; none where it is not given.
26
+ read_waits?: (() => Array<BudgetWait>) | undefined
23
27
  // Every process and its lane, the last reading's lanes reused (`run-board-usage-read.ts`).
24
28
  read_usage: (before: UsageMark | undefined) => Promise<UsageMark | undefined>
25
29
  // The issues that answered as closed, and whether every issue answered (`run-board-closed.ts`).
@@ -56,6 +60,8 @@ interface BoardState {
56
60
  // The last local read, drawn until the next is due; `undefined` when no run had started.
57
61
  local: LocalRead | undefined
58
62
  read_ms: number | undefined
63
+ // When each lane the reads found idle was first read so (`run-board-idle.ts`).
64
+ idle_since: IdleSince
59
65
  // The start of the run the plan state was built for, so a run that begins draws nothing of the last.
60
66
  run_started_ms: number | undefined
61
67
  plan: BoardPlan | undefined
@@ -82,6 +88,7 @@ interface BoardState {
82
88
  // them.
83
89
  usage: UsageMark | undefined
84
90
  usages: LaneUsages | undefined
91
+ budget_waits: ReadonlyArray<BudgetWait>
85
92
  // The reviewers drawn above the legend, the read of them in flight, and when it was last launched.
86
93
  thanks: Thanks
87
94
  thanks_fetch: Fetch<Thanks> | undefined
@@ -91,6 +98,7 @@ interface BoardState {
91
98
  const FRESH_STATE: BoardState = {
92
99
  local: undefined,
93
100
  read_ms: undefined,
101
+ idle_since: run_board_idle.NO_IDLE,
94
102
  run_started_ms: undefined,
95
103
  plan: undefined,
96
104
  plan_fetch: undefined,
@@ -108,6 +116,7 @@ const FRESH_STATE: BoardState = {
108
116
  spots: [],
109
117
  usage: undefined,
110
118
  usages: undefined,
119
+ budget_waits: [],
111
120
  thanks: [],
112
121
  thanks_fetch: undefined,
113
122
  thanks_attempted_ms: undefined,