@joshuafolkken/kit 1.1784.0 → 1.2114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/.claude/agents/reviewer-verification.md +19 -0
  2. package/.claude/agents/reviewer.md +19 -0
  3. package/.claude/settings.json +5 -6
  4. package/.claude/skills/dependency-update/SKILL.md +9 -10
  5. package/.claude/skills/workflow-commands/SKILL.md +6 -6
  6. package/.claude/skills/workflow-commands/background-commands.md +8 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
  13. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  14. package/.claude/skills/workflow-commands/chain-rule.md +41 -32
  15. package/.claude/skills/workflow-commands/delegation.md +6 -6
  16. package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
  17. package/.claude/skills/workflow-commands/followup.md +7 -5
  18. package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
  19. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  20. package/.claude/skills/workflow-commands/issue-comments.md +9 -4
  21. package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +2 -2
  23. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  24. package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
  25. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
  26. package/.claude/skills/workflow-commands/prerequisite.md +3 -2
  27. package/.claude/skills/workflow-commands/retrospective.md +2 -2
  28. package/.claude/skills/workflow-commands/split-assessment.md +7 -5
  29. package/.codex/hooks.json +5 -5
  30. package/.github/actions/setup-pnpm/action.yml +9 -16
  31. package/.github/dependabot.yml +16 -0
  32. package/.github/workflows/auto-tag.yml +45 -0
  33. package/.github/workflows/ci.yml +155 -15
  34. package/.github/workflows/dependabot-auto-merge.yml +63 -4
  35. package/.github/workflows/production.yml +8 -0
  36. package/.github/workflows/publish.yml +59 -45
  37. package/.vscode/settings.json +19 -0
  38. package/CLAUDE.md +5 -12
  39. package/SECURITY.md +2 -2
  40. package/dist/CLAUDE.md +5 -12
  41. package/dist/commands/document-read-cli.js +2 -1
  42. package/dist/commands/document-section-main.js +2 -1
  43. package/dist/commands/read-set-cli.js +194 -134
  44. package/dist/commands/run-prep-main.js +9694 -10173
  45. package/dist/commands/run-step-cli.js +22138 -18285
  46. package/dist/hooks/audit-provision.js +23 -5
  47. package/dist/hooks/chunk-3GKE653K.js +6204 -0
  48. package/dist/hooks/chunk-45CJJOO7.js +1243 -0
  49. package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
  50. package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
  51. package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
  52. package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
  53. package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
  54. package/dist/hooks/chunk-VVNYJWEI.js +30 -0
  55. package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
  56. package/dist/hooks/codex-hook-adapter.js +8 -8
  57. package/dist/hooks/format-edited.js +4 -5
  58. package/dist/hooks/inputs.json +415 -389
  59. package/dist/hooks/pretool-guard.js +7 -6
  60. package/dist/hooks/session-lang.js +101 -258
  61. package/dist/hooks/stop-guard.js +1744 -2576
  62. package/dist/josh.js +7133 -6368
  63. package/dist/version/index.js +161 -1
  64. package/eslint/rules/comment-why-only.js +108 -0
  65. package/package.json +15 -8
  66. package/prompts/coding-standards.md +1 -1
  67. package/prompts/collaboration-workflow/file-edits.md +14 -11
  68. package/prompts/collaboration-workflow/issue-citation.md +0 -4
  69. package/prompts/collaboration-workflow/issue-template.md +40 -21
  70. package/prompts/collaboration-workflow/lane-instructions.md +3 -1
  71. package/prompts/collaboration-workflow/measurement.md +8 -1
  72. package/prompts/collaboration-workflow/operating-rules.md +11 -2
  73. package/prompts/collaboration-workflow/output-bounds.md +6 -2
  74. package/prompts/collaboration-workflow/plan-comment.md +1 -1
  75. package/prompts/collaboration-workflow/principles.md +9 -0
  76. package/prompts/collaboration-workflow/residency.md +3 -2
  77. package/prompts/collaboration-workflow/rule-delivery.md +13 -1
  78. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  79. package/prompts/collaboration-workflow/wip-cap.md +3 -1
  80. package/prompts/review-rubric.md +8 -0
  81. package/prompts/review.md +31 -22
  82. package/prompts/testing-guide.md +1 -0
  83. package/scripts/agent/agent-argv.ts +14 -16
  84. package/scripts/agent/agent-provider.ts +138 -0
  85. package/scripts/agent/agent-role-profile.ts +108 -156
  86. package/scripts/agent/agent-safe-text.ts +20 -5
  87. package/scripts/agent/api-usage-limit.ts +65 -0
  88. package/scripts/agent/claude-agent-argv.ts +19 -9
  89. package/scripts/agent/codex-agent-argv.ts +118 -22
  90. package/scripts/agent/usage-limit-reset.ts +179 -0
  91. package/scripts/auth/claude-token-output.ts +2 -2
  92. package/scripts/auto-ok/auto-ok-author.ts +53 -0
  93. package/scripts/auto-ok/auto-ok-cli.ts +20 -9
  94. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  95. package/scripts/backlog/backlog-drive-cli.ts +6 -1
  96. package/scripts/backlog/backlog-drive-hold.ts +3 -3
  97. package/scripts/backlog/backlog-drive-idle.ts +10 -0
  98. package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
  99. package/scripts/backlog/backlog-drive-launch.ts +8 -2
  100. package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
  101. package/scripts/backlog/backlog-drive-named.ts +28 -7
  102. package/scripts/backlog/backlog-drive-paused.ts +5 -4
  103. package/scripts/backlog/backlog-drive-restore.ts +11 -3
  104. package/scripts/backlog/backlog-drive.ts +21 -60
  105. package/scripts/backlog/backlog-gate-scope.ts +61 -0
  106. package/scripts/backlog/backlog-measure-close.ts +112 -0
  107. package/scripts/backlog/backlog-next.ts +39 -49
  108. package/scripts/backlog/backlog-occupied.ts +2 -2
  109. package/scripts/backlog/backlog-overlap.ts +42 -8
  110. package/scripts/backlog/backlog-plan-cli.ts +4 -1
  111. package/scripts/backlog/backlog-plan-comment.ts +81 -0
  112. package/scripts/backlog/backlog-plan-read.ts +19 -6
  113. package/scripts/backlog/backlog-pool.ts +50 -12
  114. package/scripts/backlog/backlog-rank.ts +60 -13
  115. package/scripts/backlog/backlog-stalled-detect.ts +2 -1
  116. package/scripts/backlog/backlog-waves.ts +17 -2
  117. package/scripts/backlog/backlogrun-launch.ts +7 -4
  118. package/scripts/cost-runtime/context-cut-payback.ts +27 -25
  119. package/scripts/cost-runtime/cost-cli.ts +37 -16
  120. package/scripts/cost-runtime/cost-pricing.ts +9 -0
  121. package/scripts/cost-runtime/cost-verdict.ts +4 -1
  122. package/scripts/delegation/delegation-policy.ts +2 -2
  123. package/scripts/delegation/duplicate-read-guard.ts +1 -0
  124. package/scripts/delegation/investigation-guard.ts +14 -2
  125. package/scripts/delegation/investigation-reads.ts +45 -23
  126. package/scripts/dependabot/dependabot-cooldown.ts +3 -2
  127. package/scripts/discovery/repo-party.ts +80 -20
  128. package/scripts/discovery/session-owner-pin.ts +51 -0
  129. package/scripts/document/capped-print.ts +1 -1
  130. package/scripts/document/command-reference-line-budget.ts +42 -0
  131. package/scripts/document/document-byte-budget.ts +17 -7
  132. package/scripts/document/document-checks.ts +20 -0
  133. package/scripts/document/document-ratchet-check.ts +101 -0
  134. package/scripts/document/document-section.ts +1 -0
  135. package/scripts/document/entry-read-budget.ts +7 -3
  136. package/scripts/document/issue-citation-budget.ts +94 -0
  137. package/scripts/epic/epic-cli-argv.ts +2 -2
  138. package/scripts/epic/epic-fetch.ts +4 -2
  139. package/scripts/epic/epic-graph.ts +7 -2
  140. package/scripts/epic/epic-issue.ts +3 -1
  141. package/scripts/epic/epic-rank.ts +5 -0
  142. package/scripts/exports/unused-members-check.ts +62 -0
  143. package/scripts/exports/unused-members-cli.ts +6 -51
  144. package/scripts/followup/followup-ci-path.ts +67 -0
  145. package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
  146. package/scripts/followup/followup-coderabbit-wait.ts +13 -10
  147. package/scripts/followup/followup-failed-tests.ts +160 -0
  148. package/scripts/followup/followup-job-needs.ts +100 -0
  149. package/scripts/followup/followup-job-rerun.ts +303 -0
  150. package/scripts/followup/followup-main-move.ts +113 -0
  151. package/scripts/followup/git-followup-finish.ts +43 -11
  152. package/scripts/followup/git-followup-pending.ts +3 -3
  153. package/scripts/followup/git-followup-stages.ts +41 -11
  154. package/scripts/followup/git-pr-followup-checks.ts +55 -16
  155. package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
  156. package/scripts/followup/git-pr-followup.ts +62 -27
  157. package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
  158. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
  159. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
  160. package/scripts/gate/busy-average.ts +34 -2
  161. package/scripts/gate/check-related-pinned.ts +43 -0
  162. package/scripts/gate/check-related-reuse.ts +64 -0
  163. package/scripts/gate/check-related-section.ts +57 -0
  164. package/scripts/gate/check-related-unplanned.ts +34 -0
  165. package/scripts/gate/check-related.ts +265 -0
  166. package/scripts/gate/command-memory.ts +1 -1
  167. package/scripts/gate/core-admission.ts +24 -6
  168. package/scripts/gate/core-budget-timing.ts +20 -6
  169. package/scripts/gate/core-budget.ts +40 -11
  170. package/scripts/gate/core-wait-record.ts +3 -2
  171. package/scripts/gate/core-wait.ts +25 -2
  172. package/scripts/gate/core-weights.ts +15 -7
  173. package/scripts/gate/gate-labels.ts +46 -0
  174. package/scripts/gate/gate-ledger.ts +14 -6
  175. package/scripts/gate/gate-plan.ts +103 -58
  176. package/scripts/gate/gate-report.ts +9 -7
  177. package/scripts/gate/gate-run-steps.ts +26 -3
  178. package/scripts/gate/gate-skip.ts +45 -6
  179. package/scripts/gate/gate-static-group.ts +60 -0
  180. package/scripts/gate/gate-steps.ts +2 -1
  181. package/scripts/gate/gate-tree.ts +4 -2
  182. package/scripts/gate/hook-gate-reuse.ts +19 -2
  183. package/scripts/gate/metrics-totals.ts +59 -0
  184. package/scripts/gate/scoped-green.ts +68 -15
  185. package/scripts/gate/since-green.ts +14 -6
  186. package/scripts/gate/structural-checks.ts +96 -0
  187. package/scripts/gate/verification-gate.ts +51 -16
  188. package/scripts/gate/verification-inputs.ts +21 -6
  189. package/scripts/gate/verification-tail.ts +52 -0
  190. package/scripts/gh/gh-author-association.ts +23 -2
  191. package/scripts/gh/git-ai-review-scan.ts +1 -1
  192. package/scripts/gh/git-gh-api-path.ts +12 -3
  193. package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
  194. package/scripts/gh/git-gh-exec.ts +32 -2
  195. package/scripts/gh/git-gh-issue-read.ts +4 -2
  196. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  197. package/scripts/gh/git-gh-issue-write.ts +8 -2
  198. package/scripts/gh/git-gh-issue.ts +12 -5
  199. package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
  200. package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
  201. package/scripts/gh/git-gh-pr-read.ts +74 -13
  202. package/scripts/gh/git-gh-pr-rest.ts +3 -1
  203. package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
  204. package/scripts/gh/git-gh-pr-too-large.ts +20 -0
  205. package/scripts/gh/git-gh-pr.ts +21 -7
  206. package/scripts/gh/git-gh-retry.ts +141 -0
  207. package/scripts/gh/git-pr-ai-review.ts +15 -6
  208. package/scripts/gh/git-pr-checks-eval.ts +3 -0
  209. package/scripts/gh/git-pr-checks.ts +5 -5
  210. package/scripts/git/base-tree.ts +30 -0
  211. package/scripts/git/change-base.ts +4 -10
  212. package/scripts/git/changed-paths-sync.ts +63 -0
  213. package/scripts/git/changed-paths.ts +16 -1
  214. package/scripts/git/git-common-directory.ts +11 -1
  215. package/scripts/git/git-diff-reads.ts +66 -20
  216. package/scripts/git/git-error.ts +9 -2
  217. package/scripts/git/git-schemas.ts +13 -0
  218. package/scripts/git/git-worktree.ts +13 -0
  219. package/scripts/git/gone-branch.ts +4 -1
  220. package/scripts/git/main-merge-budget-conflict.ts +89 -0
  221. package/scripts/git/main-merge-guard.ts +55 -18
  222. package/scripts/git/main-merge-regenerate.ts +59 -25
  223. package/scripts/git/main-merge.ts +53 -5
  224. package/scripts/git/no-base-checkout.ts +127 -0
  225. package/scripts/git/stash/git-stash.ts +11 -2
  226. package/scripts/git/stash/stash-push-cli.ts +10 -0
  227. package/scripts/hooks/batch-guard.ts +40 -21
  228. package/scripts/hooks/edited-cspell.ts +37 -11
  229. package/scripts/hooks/format-edited-file.ts +22 -2
  230. package/scripts/hooks/lane-plan-guard.ts +50 -0
  231. package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
  232. package/scripts/hooks/pre-commit-lint.ts +14 -1
  233. package/scripts/hooks/pre-push-related-switch.ts +10 -0
  234. package/scripts/hooks/pre-push-unit.ts +76 -10
  235. package/scripts/hooks/pretool-guard.ts +2 -0
  236. package/scripts/hooks/reserved-run.ts +14 -1
  237. package/scripts/hooks/run-hook.sh +23 -8
  238. package/scripts/hooks/step-zero-notice.ts +6 -1
  239. package/scripts/hooks/stop-actions.ts +3 -15
  240. package/scripts/hooks/stop-guard.ts +7 -6
  241. package/scripts/init/distributed-paths.ts +1 -0
  242. package/scripts/init/hook-command-rewrite.ts +5 -1
  243. package/scripts/init/hook-launch.ts +4 -2
  244. package/scripts/issue/aggregation-split.ts +52 -0
  245. package/scripts/issue/defect-origin-line.ts +19 -1
  246. package/scripts/issue/defect-rate-cli.ts +5 -102
  247. package/scripts/issue/defect-report.ts +4 -4
  248. package/scripts/issue/effect-audit-cli.ts +178 -0
  249. package/scripts/issue/effect-audit.ts +185 -0
  250. package/scripts/issue/expected-effect-lint.ts +64 -9
  251. package/scripts/issue/git-next-issues.ts +5 -0
  252. package/scripts/issue/issue-body-or-comment.ts +38 -4
  253. package/scripts/issue/issue-cite-cli.ts +1 -3
  254. package/scripts/issue/issue-classification.ts +1 -0
  255. package/scripts/issue/issue-closing-pr.ts +8 -1
  256. package/scripts/issue/issue-evidence-exit.ts +4 -2
  257. package/scripts/issue/issue-file-cap.ts +58 -0
  258. package/scripts/issue/issue-file-cli.ts +111 -38
  259. package/scripts/issue/issue-file-deferral.ts +52 -0
  260. package/scripts/issue/issue-file-fold.ts +29 -10
  261. package/scripts/issue/issue-file-interrupt.ts +85 -0
  262. package/scripts/issue/issue-file-lane-hint.ts +30 -0
  263. package/scripts/issue/issue-file.ts +43 -6
  264. package/scripts/issue/issue-labels.ts +19 -2
  265. package/scripts/issue/issue-lint-cli.ts +27 -2
  266. package/scripts/issue/issue-measured-cli.ts +13 -35
  267. package/scripts/issue/issue-measured-close.ts +58 -0
  268. package/scripts/issue/issue-not-before-label.ts +2 -2
  269. package/scripts/issue/issue-not-before.ts +4 -1
  270. package/scripts/issue/issue-priority-cli.ts +60 -0
  271. package/scripts/issue/issue-priority-raise.ts +55 -0
  272. package/scripts/issue/issue-priority.ts +174 -43
  273. package/scripts/issue/issue-rank.ts +21 -4
  274. package/scripts/issue/issue-read-cli.ts +32 -6
  275. package/scripts/issue/issue-read.ts +37 -12
  276. package/scripts/issue/issue-sample-gate.ts +95 -74
  277. package/scripts/issue/issue-sample-prime.ts +127 -0
  278. package/scripts/issue/issue-sample-required.ts +57 -0
  279. package/scripts/issue/issue-sample-source.ts +12 -11
  280. package/scripts/issue/issue-search.ts +124 -0
  281. package/scripts/issue/issue-state.ts +13 -1
  282. package/scripts/issue/issue-template-section.ts +43 -0
  283. package/scripts/issue/issue-watch-diff.ts +31 -1
  284. package/scripts/issue/issue-watch-hook.ts +11 -2
  285. package/scripts/issue/issue-watch.ts +55 -12
  286. package/scripts/issue/markdown-section.ts +1 -0
  287. package/scripts/issue/measure-declaration.ts +121 -0
  288. package/scripts/issue/measure-rerun-cli.ts +7 -70
  289. package/scripts/issue/plan-consumers-section.ts +22 -1
  290. package/scripts/issue/reproduction-measure.ts +3 -1
  291. package/scripts/issue/size-estimate.ts +90 -0
  292. package/scripts/issue/trusted-measure.ts +84 -0
  293. package/scripts/josh/command-usage.ts +3 -2
  294. package/scripts/josh/durable-state-migrate.ts +105 -0
  295. package/scripts/josh/durable-state.ts +112 -0
  296. package/scripts/josh/josh-aliases.ts +95 -0
  297. package/scripts/josh/josh-command-map.ts +1 -82
  298. package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
  299. package/scripts/josh/josh-commands-ai.ts +5 -5
  300. package/scripts/josh/josh-commands-development.ts +59 -58
  301. package/scripts/josh/josh-commands-issue-write.ts +23 -4
  302. package/scripts/josh/josh-commands-issue.ts +32 -0
  303. package/scripts/josh/josh-commands-lane.ts +87 -4
  304. package/scripts/josh/josh-commands-lint.ts +16 -3
  305. package/scripts/josh/josh-commands-maintenance.ts +15 -0
  306. package/scripts/josh/josh-commands-review.ts +41 -6
  307. package/scripts/josh/josh-commands-ship.ts +56 -8
  308. package/scripts/josh/josh-commands-test-check.ts +80 -0
  309. package/scripts/josh/josh-commands-workflow.ts +1 -1
  310. package/scripts/josh/josh-environment-file.ts +43 -3
  311. package/scripts/josh/josh-in-process.ts +2 -2
  312. package/scripts/josh/josh-logic.ts +5 -3
  313. package/scripts/josh/josh-run.ts +19 -1
  314. package/scripts/josh/run-record-path.ts +15 -0
  315. package/scripts/josh/stamp-file.ts +11 -2
  316. package/scripts/lane/lane-adopt-cli.ts +98 -0
  317. package/scripts/lane/lane-adopt.ts +208 -0
  318. package/scripts/lane/lane-await.ts +5 -8
  319. package/scripts/lane/lane-baseline.ts +123 -0
  320. package/scripts/lane/lane-breakdown-cli.ts +64 -0
  321. package/scripts/lane/lane-breakdown-implement.ts +170 -0
  322. package/scripts/lane/lane-breakdown-laps.ts +107 -0
  323. package/scripts/lane/lane-breakdown-pinned.ts +76 -0
  324. package/scripts/lane/lane-breakdown-text.ts +145 -0
  325. package/scripts/lane/lane-breakdown.ts +239 -0
  326. package/scripts/lane/lane-check-stats.ts +29 -8
  327. package/scripts/lane/lane-child-invocation.ts +9 -2
  328. package/scripts/lane/lane-child-mark-keys.ts +12 -0
  329. package/scripts/lane/lane-child-marker.ts +20 -1
  330. package/scripts/lane/lane-ci-path-stats.ts +73 -0
  331. package/scripts/lane/lane-claim-stats.ts +84 -0
  332. package/scripts/lane/lane-close.ts +8 -3
  333. package/scripts/lane/lane-dispatch-log.ts +3 -3
  334. package/scripts/lane/lane-dispatch.ts +2 -2
  335. package/scripts/lane/lane-gap-stats.ts +7 -6
  336. package/scripts/lane/lane-guard-policy.ts +7 -1
  337. package/scripts/lane/lane-implement-stats.ts +207 -0
  338. package/scripts/lane/lane-implement-subagents.ts +90 -0
  339. package/scripts/lane/lane-investigation-switch.ts +16 -0
  340. package/scripts/lane/lane-ledger-kind.ts +31 -0
  341. package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
  342. package/scripts/lane/lane-ledger-schema.ts +119 -36
  343. package/scripts/lane/lane-ledger-write.ts +257 -0
  344. package/scripts/lane/lane-ledger.ts +31 -177
  345. package/scripts/lane/lane-load.ts +1 -1
  346. package/scripts/lane/lane-measure-cli.ts +176 -59
  347. package/scripts/lane/lane-net-read.ts +44 -0
  348. package/scripts/lane/lane-net-time.ts +212 -0
  349. package/scripts/lane/lane-open.ts +16 -7
  350. package/scripts/lane/lane-output.ts +2 -4
  351. package/scripts/lane/lane-overrun-prompt.ts +147 -0
  352. package/scripts/lane/lane-overrun.ts +274 -0
  353. package/scripts/lane/lane-paths.ts +24 -1
  354. package/scripts/lane/lane-pause-cli.ts +4 -1
  355. package/scripts/lane/lane-paused-spans.ts +97 -0
  356. package/scripts/lane/lane-plan-check.ts +207 -0
  357. package/scripts/lane/lane-plan-cli.ts +41 -0
  358. package/scripts/lane/lane-plan.ts +144 -0
  359. package/scripts/lane/lane-registry.ts +14 -1
  360. package/scripts/lane/lane-relaunch.ts +1 -1
  361. package/scripts/lane/lane-round-one-stats.ts +51 -0
  362. package/scripts/lane/lane-sampler.ts +23 -4
  363. package/scripts/lane/lane-session-action.ts +93 -18
  364. package/scripts/lane/lane-session-broker.ts +49 -21
  365. package/scripts/lane/lane-session-reread-main.ts +14 -0
  366. package/scripts/lane/lane-session-reread.ts +51 -0
  367. package/scripts/lane/lane-ship-lap-stats.ts +74 -0
  368. package/scripts/lane/lane-sleep.ts +58 -0
  369. package/scripts/lane/lane-spent.ts +1 -1
  370. package/scripts/lane/lane-split-stats.ts +28 -0
  371. package/scripts/lane/lane-stage-stats.ts +13 -1
  372. package/scripts/lane/lane-state.ts +41 -9
  373. package/scripts/lane/lane-stats-tables.ts +58 -0
  374. package/scripts/lane/lane-stats.ts +21 -5
  375. package/scripts/lane/lane-stop-stats.ts +51 -19
  376. package/scripts/lane/lane-tail-stats.ts +32 -0
  377. package/scripts/lane/lane-transcript-calls.ts +67 -10
  378. package/scripts/lane/lane-transcript-stats.ts +44 -12
  379. package/scripts/lane/lane-value-cell.ts +72 -0
  380. package/scripts/lane/lane-value.ts +118 -0
  381. package/scripts/lane/lane-withdraw-closed.ts +124 -0
  382. package/scripts/lane/lane-withdraw.ts +108 -31
  383. package/scripts/lane/lane-yield.ts +85 -0
  384. package/scripts/lane/openai-lane-ship.ts +12 -11
  385. package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
  386. package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
  387. package/scripts/lane/openai-lane-supervisor.ts +5 -4
  388. package/scripts/lib/poll.ts +38 -9
  389. package/scripts/lib/settled-together.ts +10 -0
  390. package/scripts/lib/shell-quote.ts +21 -0
  391. package/scripts/lib/token-shape.ts +111 -0
  392. package/scripts/lint/eslint-daemon-sources.ts +79 -0
  393. package/scripts/lint/eslint-daemon.ts +45 -6
  394. package/scripts/lint/eslint-warm-cli.ts +7 -0
  395. package/scripts/lint/eslint-warm.ts +66 -0
  396. package/scripts/lint/import-graph.ts +141 -11
  397. package/scripts/lint/lint-gate-scope.ts +2 -2
  398. package/scripts/lint/lint-parallel.ts +54 -6
  399. package/scripts/lint/lint-related.ts +154 -33
  400. package/scripts/lint/rename-cli.ts +179 -0
  401. package/scripts/lint/rename-identifier.ts +192 -0
  402. package/scripts/lint/type-only-imports.ts +58 -0
  403. package/scripts/notify/telegram-notify.ts +12 -3
  404. package/scripts/observations/observation-record.ts +42 -16
  405. package/scripts/observations/observations-flush.ts +32 -9
  406. package/scripts/overrides/overrides-check.ts +9 -1
  407. package/scripts/release/release-approval-prune.ts +7 -1
  408. package/scripts/repo/required-checks-logic.ts +4 -1
  409. package/scripts/repo/ruleset-check.ts +4 -3
  410. package/scripts/report/markdown-shape.ts +28 -0
  411. package/scripts/report/report-format-reference.ts +15 -1
  412. package/scripts/report/report-labels.ts +12 -1
  413. package/scripts/report/report-lint-child-summary.ts +94 -0
  414. package/scripts/report/report-lint-cli.ts +26 -7
  415. package/scripts/report/report-lint.ts +2 -20
  416. package/scripts/review/prose-stale-cli.ts +88 -0
  417. package/scripts/review/prose-stale-scan.ts +152 -0
  418. package/scripts/review/review-attest.ts +73 -23
  419. package/scripts/review/review-brief-cli.ts +68 -19
  420. package/scripts/review/review-brief-paths.ts +22 -0
  421. package/scripts/review/review-brief.ts +14 -7
  422. package/scripts/review/review-comment-carry.ts +47 -0
  423. package/scripts/review/review-comment-round.ts +85 -22
  424. package/scripts/review/review-finding-ledger.ts +109 -7
  425. package/scripts/review/review-finding-text.ts +43 -0
  426. package/scripts/review/review-interaction.ts +11 -0
  427. package/scripts/review/review-lane-handover.ts +68 -0
  428. package/scripts/review/review-lint-cli.ts +57 -0
  429. package/scripts/review/review-lint-findings.ts +92 -0
  430. package/scripts/review/review-lint-sections.ts +80 -0
  431. package/scripts/review/review-lint.ts +221 -0
  432. package/scripts/review/review-prior-findings.ts +102 -0
  433. package/scripts/review/review-readers-scan.ts +4 -2
  434. package/scripts/review/review-record-cli.ts +177 -54
  435. package/scripts/review/review-record-code-round.ts +55 -0
  436. package/scripts/review/review-record-comment.ts +60 -0
  437. package/scripts/review/review-record-interaction.ts +35 -0
  438. package/scripts/review/review-record-pinned.ts +108 -0
  439. package/scripts/review/review-record.ts +25 -1
  440. package/scripts/review/review-reviewed-tree.ts +307 -0
  441. package/scripts/review/review-round-one-closure.ts +62 -0
  442. package/scripts/review/review-round-reuse.ts +106 -0
  443. package/scripts/review/review-round2-cli.ts +91 -29
  444. package/scripts/review/review-round2-skip-comment.ts +55 -0
  445. package/scripts/review/review-round2.ts +24 -8
  446. package/scripts/review/review-stamps.ts +7 -3
  447. package/scripts/review/review-stop-log.ts +20 -6
  448. package/scripts/review/review-stops-cli.ts +2 -1
  449. package/scripts/review/review-submit-cli.ts +101 -0
  450. package/scripts/rules/agent-model.ts +1 -1
  451. package/scripts/rules/bash-triggers.ts +3 -3
  452. package/scripts/rules/concise-reply.ts +88 -0
  453. package/scripts/rules/credential-body.ts +77 -0
  454. package/scripts/rules/decision-oracle.ts +1 -1
  455. package/scripts/rules/delivered-rules.ts +36 -17
  456. package/scripts/rules/destructive-command.ts +3 -11
  457. package/scripts/rules/direct-issue-comment.ts +34 -12
  458. package/scripts/rules/direct-issue-read.ts +191 -0
  459. package/scripts/rules/direct-pr-create.ts +4 -24
  460. package/scripts/rules/early-heartbeat.ts +2 -0
  461. package/scripts/rules/edit-tool.ts +27 -0
  462. package/scripts/rules/file-edit-rows.ts +17 -0
  463. package/scripts/rules/file-rewrite.ts +146 -0
  464. package/scripts/rules/firing-point.ts +3 -2
  465. package/scripts/rules/gh-api.ts +128 -7
  466. package/scripts/rules/gh-repo-selector.ts +84 -0
  467. package/scripts/rules/git-force.ts +2 -2
  468. package/scripts/rules/guard-remedy.ts +14 -5
  469. package/scripts/rules/implementation-cut.ts +61 -28
  470. package/scripts/rules/investigator-foreground.ts +231 -0
  471. package/scripts/rules/issue-citation.ts +15 -60
  472. package/scripts/rules/issue-comments.ts +9 -4
  473. package/scripts/rules/josh-git-bare.ts +1 -1
  474. package/scripts/rules/lane-background.ts +41 -13
  475. package/scripts/rules/lane-direct-tool.ts +107 -9
  476. package/scripts/rules/lane-interactive-ask.ts +4 -1
  477. package/scripts/rules/lane-park.ts +4 -2
  478. package/scripts/rules/lane-spilled-read.ts +90 -0
  479. package/scripts/rules/lane-split-park.ts +1 -1
  480. package/scripts/rules/lane-switch-main.ts +4 -4
  481. package/scripts/rules/last-prompt.ts +20 -47
  482. package/scripts/rules/oracle-consulted.ts +1 -1
  483. package/scripts/rules/permission-guards.ts +5 -1
  484. package/scripts/rules/piped-verification-shapes.ts +110 -0
  485. package/scripts/rules/piped-verification.ts +67 -17
  486. package/scripts/rules/poll-loop.ts +72 -9
  487. package/scripts/rules/pr-merge.ts +5 -5
  488. package/scripts/rules/pre-gate-cut.ts +3 -3
  489. package/scripts/rules/protected-files.ts +88 -24
  490. package/scripts/rules/reply-language.ts +3 -17
  491. package/scripts/rules/reply-prose.ts +34 -0
  492. package/scripts/rules/repo-admin.ts +132 -0
  493. package/scripts/rules/reviewer-foreground.ts +88 -0
  494. package/scripts/rules/round-one-check-related.ts +120 -0
  495. package/scripts/rules/rule-body-guard.ts +5 -15
  496. package/scripts/rules/rule-value-cache.ts +53 -5
  497. package/scripts/rules/rule-value-candidates.ts +84 -0
  498. package/scripts/rules/rule-value-cli.ts +59 -11
  499. package/scripts/rules/rule-value.ts +17 -4
  500. package/scripts/rules/run-tail-rule.ts +1 -1
  501. package/scripts/rules/sed-in-place.ts +196 -0
  502. package/scripts/rules/shell-segments.ts +10 -2
  503. package/scripts/rules/stop-rule-rows.ts +1 -13
  504. package/scripts/rules/stop-rules.ts +33 -58
  505. package/scripts/rules/test-declared-commit.ts +1 -1
  506. package/scripts/rules/third-party-write.ts +110 -23
  507. package/scripts/rules/timed-rerun.ts +1 -1
  508. package/scripts/rules/turn-filings.ts +90 -0
  509. package/scripts/rules/worktree-guard.ts +22 -7
  510. package/scripts/run/board/run-board-ending-reason.ts +6 -2
  511. package/scripts/run/board/run-board-fit.ts +24 -1
  512. package/scripts/run/board/run-board-found.ts +134 -0
  513. package/scripts/run/board/run-board-header.ts +8 -0
  514. package/scripts/run/board/run-board-labels.ts +11 -1
  515. package/scripts/run/board/run-board-lane-time.ts +1 -0
  516. package/scripts/run/board/run-board-link.ts +15 -2
  517. package/scripts/run/board/run-board-notes.ts +7 -1
  518. package/scripts/run/board/run-board-operation.ts +4 -2
  519. package/scripts/run/board/run-board-phase.ts +2 -5
  520. package/scripts/run/board/run-board-plan.ts +1 -1
  521. package/scripts/run/board/run-board-read.ts +46 -1
  522. package/scripts/run/board/run-board-reason.ts +20 -8
  523. package/scripts/run/board/run-board-render-checks.ts +3 -2
  524. package/scripts/run/board/run-board-render-drawn.ts +11 -0
  525. package/scripts/run/board/run-board-render-found.ts +157 -0
  526. package/scripts/run/board/run-board-render-lead.ts +9 -0
  527. package/scripts/run/board/run-board-render-legend.ts +19 -0
  528. package/scripts/run/board/run-board-render-tail.ts +1 -1
  529. package/scripts/run/board/run-board-render.ts +82 -18
  530. package/scripts/run/board/run-board-spin.ts +1 -3
  531. package/scripts/run/board/run-board-status.ts +2 -0
  532. package/scripts/run/board/run-board-tick.ts +39 -10
  533. package/scripts/run/carry/run-carry-ended.ts +3 -2
  534. package/scripts/run/carry/run-carry.ts +3 -2
  535. package/scripts/run/cut/run-cut-cli.ts +33 -133
  536. package/scripts/run/cut/run-cut-digest.ts +152 -0
  537. package/scripts/run/cut/run-cut-handoff.ts +52 -8
  538. package/scripts/run/cut/run-cut-report.ts +24 -3
  539. package/scripts/run/cut/run-cut-resume.ts +129 -0
  540. package/scripts/run/cut/run-cut-review.ts +105 -0
  541. package/scripts/run/cut/run-cut.ts +18 -7
  542. package/scripts/run/detached-launch.ts +1 -1
  543. package/scripts/run/entry/run-entry-cli.ts +88 -19
  544. package/scripts/run/entry/run-entry-spill.ts +83 -0
  545. package/scripts/run/entry/run-entry-stop.ts +5 -1
  546. package/scripts/run/entry/run-entry-trust.ts +91 -0
  547. package/scripts/run/event/run-event-baseline.ts +56 -0
  548. package/scripts/run/event/run-event-filed.ts +9 -3
  549. package/scripts/run/event/run-event-overrun.ts +197 -0
  550. package/scripts/run/event/run-event-render.ts +4 -0
  551. package/scripts/run/event/run-event-stream.ts +25 -3
  552. package/scripts/run/hold/run-stop-notify.ts +3 -8
  553. package/scripts/run/merge/run-merge-cap.ts +57 -0
  554. package/scripts/run/merge/run-merge-cli.ts +66 -23
  555. package/scripts/run/merge/run-merge-outcome.ts +2 -0
  556. package/scripts/run/merge/run-merge-ship.ts +13 -3
  557. package/scripts/run/merge/run-merge-signals.ts +40 -23
  558. package/scripts/run/merge/run-merge-steps.ts +56 -36
  559. package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
  560. package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
  561. package/scripts/run/merge/run-merge.ts +41 -14
  562. package/scripts/run/review/run-review-cli.ts +306 -20
  563. package/scripts/run/review/run-review-early-push.ts +121 -0
  564. package/scripts/run/review/run-review-join-read.ts +94 -0
  565. package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
  566. package/scripts/run/review/run-review-pinned-join.ts +216 -0
  567. package/scripts/run/review/run-review-pinned-steps.ts +383 -0
  568. package/scripts/run/review/run-review-pinned-tree.ts +37 -0
  569. package/scripts/run/review/run-review-pinned.ts +53 -0
  570. package/scripts/run/review/run-review-steps.ts +70 -34
  571. package/scripts/run/review/run-review.ts +26 -6
  572. package/scripts/run/run-parent-cut-hook.ts +3 -4
  573. package/scripts/run/run-prep.ts +1 -0
  574. package/scripts/run/run-stage-read.ts +2 -6
  575. package/scripts/run/run-step-cli.ts +17 -5
  576. package/scripts/run/run-step-lane-plan.ts +27 -0
  577. package/scripts/run/run-step-section.ts +26 -7
  578. package/scripts/run/run-step.ts +5 -2
  579. package/scripts/run/ship/run-ship-args.ts +19 -0
  580. package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
  581. package/scripts/run/ship/run-ship-cli.ts +131 -39
  582. package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
  583. package/scripts/run/ship/run-ship-detach.ts +10 -4
  584. package/scripts/run/ship/run-ship-handover.ts +106 -0
  585. package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
  586. package/scripts/run/ship/run-ship-lap.ts +63 -0
  587. package/scripts/run/ship/run-ship-next.ts +27 -26
  588. package/scripts/run/ship/run-ship-overlap.ts +8 -3
  589. package/scripts/run/ship/run-ship-place.ts +9 -2
  590. package/scripts/run/ship/run-ship-planned.ts +92 -0
  591. package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
  592. package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
  593. package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
  594. package/scripts/run/ship/run-ship-preflight.ts +16 -4
  595. package/scripts/run/ship/run-ship-probe.ts +23 -9
  596. package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
  597. package/scripts/run/ship/run-ship-relaunch.ts +51 -0
  598. package/scripts/run/ship/run-ship-return.ts +103 -29
  599. package/scripts/run/ship/run-ship-review-reason.ts +24 -0
  600. package/scripts/run/ship/run-ship-review-steps.ts +230 -59
  601. package/scripts/run/ship/run-ship-review.ts +171 -51
  602. package/scripts/run/ship/run-ship-reviewer.ts +138 -42
  603. package/scripts/run/ship/run-ship-scoped.ts +39 -10
  604. package/scripts/run/ship/run-ship-stage.ts +49 -13
  605. package/scripts/run/ship/run-ship-steps.ts +24 -4
  606. package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
  607. package/scripts/run/ship/run-ship-stop-context.ts +50 -13
  608. package/scripts/run/ship/run-ship-sync.ts +171 -30
  609. package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
  610. package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
  611. package/scripts/run/ship/run-ship-waiter.ts +114 -0
  612. package/scripts/run/ship/run-ship.ts +16 -2
  613. package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
  614. package/scripts/run/wake/run-wake-cli.ts +6 -113
  615. package/scripts/run/wake/run-wake-code.ts +53 -11
  616. package/scripts/run/wake/run-wake-driver.ts +5 -3
  617. package/scripts/run/wake/run-wake-finish.ts +137 -0
  618. package/scripts/run/wake/run-wake-loop.ts +5 -3
  619. package/scripts/run/wake/run-wake-ports.ts +11 -3
  620. package/scripts/run/wake/run-wake-session.ts +2 -0
  621. package/scripts/run/wake/run-wake.ts +3 -2
  622. package/scripts/safe-chain/ci-installer-pin.ts +49 -8
  623. package/scripts/safe-chain/project-config.ts +3 -2
  624. package/scripts/security/secretlint-scan.ts +42 -9
  625. package/scripts/security/security-audit-provision-main.ts +9 -1
  626. package/scripts/security/security-audit-provision.ts +1 -1
  627. package/scripts/security/security-audit.ts +21 -1
  628. package/scripts/security/sonar-file.ts +10 -5
  629. package/scripts/split/split-assess.ts +12 -3
  630. package/scripts/split/split-early-notice.ts +116 -0
  631. package/scripts/split/split-measure.ts +20 -2
  632. package/scripts/split/split-resolution.ts +107 -0
  633. package/scripts/test/direct-importers.ts +185 -0
  634. package/scripts/test/pure-files-entries.ts +83 -0
  635. package/scripts/test/related-heavy-tests.ts +81 -0
  636. package/scripts/test/test-no-base.ts +88 -0
  637. package/scripts/test/test-pinned-cache-logic.ts +363 -0
  638. package/scripts/test/test-pinned-cache.ts +337 -0
  639. package/scripts/test/test-pinned-emit.ts +49 -0
  640. package/scripts/test/test-pinned-functions.ts +116 -0
  641. package/scripts/test/test-pinned-ledger.ts +80 -0
  642. package/scripts/test/test-pinned-logic.ts +208 -20
  643. package/scripts/test/test-pinned-main.ts +84 -18
  644. package/scripts/test/test-pinned-pool.ts +73 -0
  645. package/scripts/test/test-pinned-probe.ts +254 -0
  646. package/scripts/test/test-pinned-prune-logic.ts +118 -0
  647. package/scripts/test/test-pinned-reach.ts +194 -0
  648. package/scripts/test/test-pinned-record.ts +196 -0
  649. package/scripts/test/test-pinned-rewire.ts +148 -0
  650. package/scripts/test/test-pinned-spawn.ts +81 -0
  651. package/scripts/test/test-pinned-stage.ts +70 -0
  652. package/scripts/test/test-pinned.ts +258 -47
  653. package/scripts/test/test-reach-cli.ts +61 -0
  654. package/scripts/test/test-read-map.ts +35 -11
  655. package/scripts/test/test-read-recording.ts +17 -5
  656. package/scripts/test/test-read-trace.ts +26 -8
  657. package/scripts/test/test-red-logic.ts +46 -7
  658. package/scripts/test/test-red.ts +34 -14
  659. package/scripts/test/test-related-claim.ts +37 -0
  660. package/scripts/test/test-related-main.ts +4 -1
  661. package/scripts/test/test-related-moved.ts +91 -0
  662. package/scripts/test/test-related-record.ts +43 -23
  663. package/scripts/test/test-related-scope.ts +99 -34
  664. package/scripts/test/test-related-synced.ts +135 -0
  665. package/scripts/test/test-related-touched.ts +84 -0
  666. package/scripts/test/test-related.ts +167 -83
  667. package/scripts/test/test-unit-guard.ts +28 -5
  668. package/scripts/test/unit-durations.ts +41 -0
  669. package/scripts/test/unit-worker-share.ts +5 -4
  670. package/scripts/test/vitest-cache-directory.ts +19 -0
  671. package/scripts/test/vitest-cli-excludes.ts +14 -0
  672. package/scripts/test/vitest-failure-summary.ts +111 -0
  673. package/scripts/test/whole-suite-files.ts +12 -1
  674. package/scripts/time-runtime/time-batch-guard.ts +42 -4
  675. package/scripts/time-runtime/time-bundle-call.ts +1 -1
  676. package/scripts/time-runtime/time-bundles.ts +36 -5
  677. package/scripts/time-runtime/time-markers.ts +2 -2
  678. package/scripts/time-runtime/time-search-call.ts +38 -0
  679. package/scripts/time-runtime/time-shell.ts +1 -1
  680. package/scripts/time-runtime/time-transcript-line.ts +17 -5
  681. package/scripts/version/development-engines-verdict.ts +124 -0
  682. package/scripts/version/latest-corepack.ts +2 -0
  683. package/templates/gitignore +2 -0
  684. package/templates/workflows/ci.yml +41 -0
  685. package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
  686. package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
  687. package/dist/hooks/chunk-TUIFS5E7.js +0 -72
  688. package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
  689. package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
  690. package/scripts/rules/filing-cap.ts +0 -139
@@ -0,0 +1,94 @@
1
+ // The issue-number citation ceiling of every agent-read document — an exact ratchet.
2
+ //
3
+ // Issue-number citations and the "why / measured / rejected" prose around them are what swell the
4
+ // documents an agent reads at run time; a reduction epic cuts them only for the next PRs to add them
5
+ // back, because nothing held the reduced count. This list is that hold:
6
+ // `issue-citation-budget.test.ts` fails `pnpm josh gate` when an agent-read document cites more issue
7
+ // numbers than its entry records, and when it cites fewer — a reduction must lower its entry in the
8
+ // same PR, so the ceiling follows the count down and never sits loose above it. A document absent from
9
+ // the list has a ceiling of zero. History belongs in `docs/maintainers/<topic>-rationale.md`, which
10
+ // this budget does not hold (`docs/maintainers/residency-rationale.md` → "The issue-citation ratchet").
11
+ //
12
+ // The ratchet is exact rather than block-quantized like `document-byte-budget.ts`: a single added
13
+ // citation is the growth this exists to refuse, so there is no headroom to quantize.
14
+
15
+ interface CitationBudget {
16
+ path: string
17
+ citations: number
18
+ }
19
+
20
+ // An issue number as a document cites it — `#1234` or `joshuafolkken/kit#1234`. Three digits or more,
21
+ // so a placeholder (`#N`, `#<N>`) and a list ordinal are not counted.
22
+ const CITATION_PATTERN = /#\d{3,}\b/gu
23
+
24
+ const ISSUE_CITATION_BUDGET: ReadonlyArray<CitationBudget> = [
25
+ { path: 'docs/josh-commands-backlog.md', citations: 13 },
26
+ { path: 'docs/josh-commands-run.md', citations: 4 },
27
+ { path: 'docs/josh-commands.md', citations: 2 },
28
+ { path: 'prompts/collaboration-workflow/file-edits.md', citations: 1 },
29
+ { path: 'prompts/collaboration-workflow/glossary.md', citations: 1 },
30
+ { path: 'prompts/collaboration-workflow/issue-citation.md', citations: 1 },
31
+ { path: 'prompts/collaboration-workflow/issue-template.md', citations: 5 },
32
+ { path: 'prompts/collaboration-workflow/operating-rules.md', citations: 3 },
33
+ { path: 'prompts/collaboration-workflow/output-bounds.md', citations: 3 },
34
+ { path: 'prompts/collaboration-workflow/overview.md', citations: 1 },
35
+ { path: 'prompts/collaboration-workflow/plan-comment.md', citations: 1 },
36
+ { path: 'prompts/collaboration-workflow/principles.md', citations: 1 },
37
+ { path: 'prompts/collaboration-workflow/report-format.md', citations: 7 },
38
+ { path: 'prompts/collaboration-workflow/residency.md', citations: 3 },
39
+ { path: 'prompts/collaboration-workflow/rule-delivery.md', citations: 5 },
40
+ { path: 'prompts/collaboration-workflow/shell-body.md', citations: 2 },
41
+ { path: 'prompts/collaboration-workflow/turn-batching.md', citations: 2 },
42
+ { path: 'prompts/collaboration-workflow/upstream-interrupt.md', citations: 1 },
43
+ { path: 'prompts/collaboration-workflow/wip-cap.md', citations: 2 },
44
+ { path: 'prompts/refactoring.md', citations: 1 },
45
+ { path: 'prompts/review-rubric.md', citations: 1 },
46
+ { path: 'prompts/sonar-hotspot-handling.md', citations: 2 },
47
+ { path: 'prompts/testing-guide.md', citations: 3 },
48
+ ]
49
+
50
+ const BUDGET_FILE = 'scripts/document/issue-citation-budget.ts'
51
+
52
+ function count_citations(text: string): number {
53
+ return text.match(CITATION_PATTERN)?.length ?? 0
54
+ }
55
+
56
+ // The recorded ceiling for one path — zero when the list carries no entry for it.
57
+ function recorded_citations_for(relative_path: string): number {
58
+ return ISSUE_CITATION_BUDGET.find((entry) => entry.path === relative_path)?.citations ?? 0
59
+ }
60
+
61
+ function over_budget_message(relative_path: string, current: number, recorded: number): string {
62
+ const hint = `move the history to docs/maintainers/<topic>-rationale.md, or record ${current.toString()} in ${BUDGET_FILE} so the reason shows in the PR diff`
63
+
64
+ return `${relative_path} cites ${current.toString()} issue numbers, over its ceiling of ${recorded.toString()}. Cut the citations — ${hint}.`
65
+ }
66
+
67
+ function stale_budget_message(relative_path: string, current: number, recorded: number): string {
68
+ const hint = `lower its entry to ${current.toString()} in ${BUDGET_FILE} (remove the entry at 0) so the ratchet holds the reduction`
69
+
70
+ return `${relative_path} cites ${current.toString()} issue numbers but records ${recorded.toString()}. ${hint}.`
71
+ }
72
+
73
+ // The failure for one document, or undefined when its count equals its record. Growth and a
74
+ // reduction left unrecorded both fail, which is what makes the ceiling a ratchet rather than a cap.
75
+ function citation_violation(
76
+ relative_path: string,
77
+ current: number,
78
+ recorded: number,
79
+ ): string | undefined {
80
+ if (current > recorded) return over_budget_message(relative_path, current, recorded)
81
+ if (current < recorded) return stale_budget_message(relative_path, current, recorded)
82
+
83
+ return undefined
84
+ }
85
+
86
+ const issue_citation_budget = {
87
+ ISSUE_CITATION_BUDGET,
88
+ citation_violation,
89
+ count_citations,
90
+ recorded_citations_for,
91
+ }
92
+
93
+ export type { CitationBudget }
94
+ export { issue_citation_budget }
@@ -33,9 +33,9 @@ interface FormFlags {
33
33
  value_flags?: ReadonlyArray<string>
34
34
  }
35
35
 
36
- type OptionEntry = [string, ParseArgsOptionsConfig[string]]
37
-
38
36
  function options_of(form: FormFlags): ParseArgsOptionsConfig {
37
+ type OptionEntry = [string, ParseArgsOptionsConfig[string]]
38
+
39
39
  const entries: Array<OptionEntry> = [
40
40
  ...form.switches.map((flag): OptionEntry => [cli_flags.option_name(flag), SWITCH_OPTION]),
41
41
  ...(form.value_flags ?? []).map((flag): OptionEntry => [
@@ -1,6 +1,6 @@
1
1
  import { git_gh_command } from '#scripts/gh/git-gh-command'
2
2
  import type { IssueReadFailure } from '#scripts/gh/git-gh-issue-read'
3
- import { issue_sample_gate } from '#scripts/issue/issue-sample-gate'
3
+ import { issue_sample_prime } from '#scripts/issue/issue-sample-prime'
4
4
  import { epic_cross_repo } from './epic-cross-repo'
5
5
  import type { EpicChild, IssueReference } from './epic-graph'
6
6
  import { epic_issue, type EpicIssue } from './epic-issue'
@@ -23,6 +23,8 @@ function to_child(parsed: EpicIssue, repo: string): EpicChild {
23
23
  labels: epic_issue.label_names(parsed),
24
24
  blocked_by: epic_issue.blocker_references_of(parsed, repo),
25
25
  body: parsed.body,
26
+ author_association: parsed.author_association,
27
+ user: parsed.user,
26
28
  }
27
29
  }
28
30
 
@@ -134,7 +136,7 @@ async function fetch_child_read(
134
136
 
135
137
  const parsed = epic_issue.parse_epic_issue(read.json)
136
138
 
137
- await issue_sample_gate.prime([parsed?.body])
139
+ await issue_sample_prime.prime([parsed?.body])
138
140
 
139
141
  return { child: parsed === undefined ? undefined : to_child(parsed, repo), is_unreachable: false }
140
142
  }
@@ -1,3 +1,4 @@
1
+ import type { AuthorFields } from '#scripts/gh/gh-author-association'
1
2
  import type { DependencyLink } from './epic-parse'
2
3
  import {
3
4
  format_dependency_link,
@@ -23,13 +24,17 @@ import {
23
24
  // exist, `blocker_categories` dropped it, and the child ran as though nothing blocked it. It also
24
25
  // made `epic_cross_repo.resolve_cross_repo`'s cross-repository branch unreachable: every blocker
25
26
  // arrived carrying the blocked child's repository, so `blocker.repo === blocked.repo` always held.
26
- interface EpicChild {
27
+ //
28
+ // The author fields (`AuthorFields`) are read only by `backlog:next`, which offers a child only when
29
+ // its author has write access (joshuafolkken/kit#4633); absent on rows built without a read.
30
+ interface EpicChild extends AuthorFields {
27
31
  number: number
28
32
  repo: string
29
33
  state: 'OPEN' | 'CLOSED'
30
34
  labels: ReadonlyArray<string>
31
35
  blocked_by: ReadonlyArray<IssueReference>
32
- // Read only to hold a child until its declared start time; absent on rows built without a read.
36
+ // Read to hold a child until its declared start time and to rank it by its causing-lane minutes;
37
+ // absent on rows built without a read.
33
38
  body?: string | undefined
34
39
  }
35
40
 
@@ -1,5 +1,5 @@
1
1
  import { git_gh_issue_rest } from '#scripts/gh/git-gh-issue-rest'
2
- import { blocked_by_schema } from '#scripts/git/git-schemas'
2
+ import { author_fields_schema, blocked_by_schema } from '#scripts/git/git-schemas'
3
3
  import { parse_json_object_or_undefined } from '#scripts/git/parse-json-array'
4
4
  import { z } from 'zod'
5
5
  import { REPOSITORY_SEGMENT_SOURCE } from './epic-parse'
@@ -36,6 +36,8 @@ const epic_issue_schema = z.object({
36
36
  url: z.string().default(''),
37
37
  labels: z.array(label_schema).default([]),
38
38
  blockedBy: blocked_by_schema,
39
+ // Who filed the issue, for `backlog:next`'s author-trust check (joshuafolkken/kit#4633).
40
+ ...author_fields_schema,
39
41
  })
40
42
 
41
43
  type EpicIssue = z.infer<typeof epic_issue_schema>
@@ -1,3 +1,5 @@
1
+ import { expected_effect_lint } from '#scripts/issue/expected-effect-lint'
2
+ import { issue_priority } from '#scripts/issue/issue-priority'
1
3
  import { issue_rank } from '#scripts/issue/issue-rank'
2
4
  import { epic_graph, type EpicChild } from './epic-graph'
3
5
 
@@ -27,7 +29,10 @@ function rank(
27
29
  const dependents = dependents_of(pool)
28
30
 
29
31
  return issue_rank.rank(children, (child) => ({
32
+ is_last: issue_priority.is_last(child.body),
30
33
  labels: child.labels,
34
+ lost_minutes: issue_priority.lost_minutes(child.body),
35
+ gain_minutes: expected_effect_lint.weekly_minutes(child.body),
31
36
  dependents: dependents.get(epic_graph.key_of(child)) ?? 0,
32
37
  }))
33
38
  }
@@ -0,0 +1,62 @@
1
+ import path from 'node:path'
2
+ import { doctor_consumer } from '#scripts/doctor/doctor-consumer'
3
+ import { NOTHING_TO_CHECK_MARKER } from '#scripts/test/skip-marker'
4
+ import type { NamespaceMember } from '#tools/exports/namespace-members'
5
+
6
+ const CHECK = 'exports'
7
+ const FAIL_EXIT_CODE = 1
8
+ // A consumer's namespaces are read from `.svelte` files and routes a TypeScript program over its
9
+ // `tsconfig.json` does not see, so every member they use would read as unused. The check is kit's own.
10
+ //
11
+ // **Not a skip notice**. `SKIP_MARKER` means "a check passed without running",
12
+ // and the gate withholds its green record on it — so a consumer, where this notice prints on every
13
+ // run, never got a green record. Out of scope by design is not unverified, so the marker stays out.
14
+ const CONSUMER_NOTICE = `josh ${CHECK}: checks only the kit repository itself — ${NOTHING_TO_CHECK_MARKER} in a consumer project.`
15
+
16
+ function format_member(root: string, member: NamespaceMember): string {
17
+ return `${path.relative(root, member.file)}:${String(member.line)} ${member.namespace}.${member.member}`
18
+ }
19
+
20
+ function report(root: string, members: ReadonlyArray<NamespaceMember>): string {
21
+ if (members.length === 0) return `josh ${CHECK}: no unused namespace member`
22
+
23
+ return [
24
+ `josh ${CHECK}: ${String(members.length)} namespace member(s) nothing reads — remove each from its namespace object (and the declaration, if nothing in its file uses it either):`,
25
+ ...members.map((member) => ` ${format_member(root, member)}`),
26
+ ].join('\n')
27
+ }
28
+
29
+ // Loaded only past the consumer skip: the scanner pulls in `typescript`, an optional peer a
30
+ // basic-profile consumer may not have, and this check runs in every consumer's gate. For the same
31
+ // reason the scanner is left out of the published package (`package.json` `files`); only this check
32
+ // ships. `files` narrows the scan to the namespaces they declare or import directly; undefined scans
33
+ // the whole program.
34
+ async function find_members(
35
+ root: string,
36
+ files: ReadonlyArray<string> | undefined,
37
+ ): Promise<ReadonlyArray<NamespaceMember>> {
38
+ const { unused_members } = await import('#tools/exports/unused-members')
39
+ if (files === undefined) return unused_members.find_unused_members(root)
40
+
41
+ return await unused_members.find_unused_members_in(root, files)
42
+ }
43
+
44
+ async function run_unused_members(root: string, files?: ReadonlyArray<string>): Promise<number> {
45
+ if (doctor_consumer.is_kit_consumer(root)) {
46
+ console.info(CONSUMER_NOTICE)
47
+
48
+ return 0
49
+ }
50
+
51
+ const members = await find_members(root, files)
52
+ const output = report(root, members)
53
+
54
+ if (members.length === 0) console.info(output)
55
+ else console.error(output)
56
+
57
+ return members.length === 0 ? 0 : FAIL_EXIT_CODE
58
+ }
59
+
60
+ const unused_members_check = { run_unused_members, CONSUMER_NOTICE }
61
+
62
+ export { unused_members_check }
@@ -1,58 +1,13 @@
1
1
  #!/usr/bin/env tsx
2
- import path from 'node:path'
3
2
  import { fileURLToPath } from 'node:url'
4
- import { doctor_consumer } from '#scripts/doctor/doctor-consumer'
5
3
  import { project_checks } from '#scripts/gate/project-checks'
6
- import { NOTHING_TO_CHECK_MARKER } from '#scripts/test/skip-marker'
7
- import type { NamespaceMember } from '#tools/exports/namespace-members'
4
+ import { unused_members_check } from './unused-members-check'
8
5
 
9
- const CHECK = 'exports'
10
- const FAIL_EXIT_CODE = 1
11
- // A consumer's namespaces are read from `.svelte` files and routes a TypeScript program over its
12
- // `tsconfig.json` does not see, so every member they use would read as unused. The check is kit's own.
13
- //
14
- // **Not a skip notice**. `SKIP_MARKER` means "a check passed without running",
15
- // and the gate withholds its green record on it — so a consumer, where this notice prints on every
16
- // run, never got a green record. Out of scope by design is not unverified, so the marker stays out.
17
- const CONSUMER_NOTICE = `josh ${CHECK}: checks only the kit repository itself — ${NOTHING_TO_CHECK_MARKER} in a consumer project.`
18
-
19
- function format_member(root: string, member: NamespaceMember): string {
20
- return `${path.relative(root, member.file)}:${String(member.line)} ${member.namespace}.${member.member}`
21
- }
22
-
23
- function report(root: string, members: ReadonlyArray<NamespaceMember>): string {
24
- if (members.length === 0) return `josh ${CHECK}: no unused namespace member`
25
-
26
- return [
27
- `josh ${CHECK}: ${String(members.length)} namespace member(s) nothing reads — remove each from its namespace object (and the declaration, if nothing in its file uses it either):`,
28
- ...members.map((member) => ` ${format_member(root, member)}`),
29
- ].join('\n')
30
- }
31
-
32
- async function run_unused_members(root: string): Promise<number> {
33
- if (doctor_consumer.is_kit_consumer(root)) {
34
- console.info(CONSUMER_NOTICE)
35
-
36
- return 0
37
- }
38
-
39
- // Loaded only past the skip: the scanner pulls in `typescript`, an optional peer a basic-profile
40
- // consumer may not have, and this check runs in every consumer's gate. For the same reason the
41
- // scanner is left out of the published package (`package.json` `files`); only this entry ships.
42
- const { unused_members } = await import('#tools/exports/unused-members')
43
- const members = unused_members.find_unused_members(root)
44
- const output = report(root, members)
45
-
46
- if (members.length === 0) console.info(output)
47
- else console.error(output)
48
-
49
- return members.length === 0 ? 0 : FAIL_EXIT_CODE
50
- }
6
+ // The `josh exports:unused` entry point: the whole-program scan the gate runs. `josh lint:related`
7
+ // imports the check itself, narrowed to the changed files.
51
8
 
52
9
  if (process.argv[1] === fileURLToPath(import.meta.url)) {
53
- process.exitCode = await run_unused_members(project_checks.project_root(process.cwd()))
10
+ process.exitCode = await unused_members_check.run_unused_members(
11
+ project_checks.project_root(process.cwd()),
12
+ )
54
13
  }
55
-
56
- const unused_members_cli = { run_unused_members, CONSUMER_NOTICE }
57
-
58
- export { unused_members_cli }
@@ -0,0 +1,67 @@
1
+ import type { SnapshotObserver } from '#scripts/gh/git-pr-checks'
2
+ import { is_coderabbit_check } from '#scripts/gh/git-pr-checks-eval'
3
+ import { CHECK_STATUS_PENDING, type PrStateSnapshot } from '#scripts/gh/git-pr-checks-parse'
4
+
5
+ // Which check `followup`'s CI wait waited on last (joshuafolkken/kit#5295). The `ci-wait` row alone
6
+ // says how long the wait was, not which job held it, so the next job to shorten could not be chosen.
7
+ //
8
+ // **A check is timed when it is seen pending and then settled**: its time is the interval from the
9
+ // wait's first poll to the first poll that saw it settled. A check already settled at the first poll
10
+ // held nothing up and is not recorded. CodeRabbit is left out — its wait is `coderabbit-wait`'s
11
+ // (`followup-coderabbit-wait.ts`), so it is never counted twice. The clock is `performance.now()`'s
12
+ // for the reason `git-followup-stages.ts` states.
13
+
14
+ // Mutable for the reason `StageLog` is: the observer adds to it as the wait polls.
15
+ interface CiPath {
16
+ start_ms?: number
17
+ pending: Set<string>
18
+ check_ms: Record<string, number>
19
+ }
20
+
21
+ // The `ci-wait` row's critical-path fields, both absent where no check was seen to settle.
22
+ interface CiPathRecord {
23
+ last_check?: string
24
+ check_ms?: Record<string, number>
25
+ }
26
+
27
+ function new_path(): CiPath {
28
+ return { pending: new Set(), check_ms: {} }
29
+ }
30
+
31
+ function settle(path: CiPath, name: string, elapsed_ms: number): void {
32
+ if (path.pending.delete(name)) path.check_ms[name] = elapsed_ms
33
+ }
34
+
35
+ function observe(path: CiPath, snapshot: PrStateSnapshot, now_ms: number): void {
36
+ path.start_ms ??= now_ms
37
+ const elapsed_ms = now_ms - path.start_ms
38
+ const checks = snapshot.rollup.filter((check) => !is_coderabbit_check(check.name))
39
+
40
+ for (const check of checks) {
41
+ if (check.status === CHECK_STATUS_PENDING) path.pending.add(check.name)
42
+ else settle(path, check.name, elapsed_ms)
43
+ }
44
+ }
45
+
46
+ // **Only the first check wait is timed**: the `ci-wait` row is that wait's lap, and the wait the merge
47
+ // goes back to when the head moved (#4036) times checks the row does not cover.
48
+ function observer_of(path: CiPath): SnapshotObserver {
49
+ const is_first_wait = path.start_ms === undefined
50
+
51
+ return async function time_checks(snapshot: PrStateSnapshot): Promise<void> {
52
+ if (is_first_wait) observe(path, snapshot, performance.now())
53
+ }
54
+ }
55
+
56
+ function record_of(path: CiPath): CiPathRecord {
57
+ const [last] = Object.entries(path.check_ms).toSorted((left, right) => right[1] - left[1])
58
+
59
+ if (last === undefined) return {}
60
+
61
+ return { last_check: last[0], check_ms: { ...path.check_ms } }
62
+ }
63
+
64
+ const followup_ci_path = { new_path, observe, observer_of, record_of }
65
+
66
+ export type { CiPath, CiPathRecord }
67
+ export { followup_ci_path }
@@ -0,0 +1,261 @@
1
+ // cspell:words coderabbit coderabbitai
2
+ import { is_coderabbit_author, is_coderabbit_rate_limit } from '#scripts/gh/git-ai-review-scan'
3
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
4
+ import type { SnapshotObserver } from '#scripts/gh/git-pr-checks'
5
+ import { parse_pr_state_snapshot } from '#scripts/gh/git-pr-checks-parse'
6
+ import { ai_review_pull_comment_schema } from '#scripts/git/git-schemas'
7
+ import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
8
+ import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
9
+ import { poll } from '#scripts/lib/poll'
10
+ import type { CodeRabbitState } from '#scripts/run/event/run-event-checks'
11
+ import { followup_checks_event } from './followup-checks-event'
12
+ import { followup_coderabbit_wait, type CodeRabbitWait } from './followup-coderabbit-wait'
13
+
14
+ // A rate-limited CodeRabbit, retriggered once by `followup` itself (joshuafolkken/kit#5289): read the
15
+ // window off CodeRabbit's own notice, wait it out, post `@coderabbitai review`, and wait for the check
16
+ // again. **Once per pull request** — a retrigger comment older than the latest notice means the one
17
+ // attempt was spent, and a second rate limit is the person's call (bypassing it is Tier C); one newer
18
+ // than it, whoever posted it, is a review still to come and is waited for, not posted again. The pull
19
+ // request, not the ledger, answers "already retriggered", so a re-run `followup` reads the same answer.
20
+
21
+ const RETRIGGER_BODY = '@coderabbitai review'
22
+ // `Please wait **12 minutes and 34 seconds** before requesting another review.` — the clause between
23
+ // `wait` and `before`, read as `<number> <unit>` token pairs.
24
+ const WAIT_OPENER = /please wait/iu
25
+ const WAIT_CLOSER = /before/iu
26
+ const TOKEN = /\d+|[a-z]+/giu
27
+ const DIGITS = /^\d+$/u
28
+ const SECOND_MS = 1000
29
+ const SECONDS_PER_MINUTE = 60
30
+ const MINUTES_PER_HOUR = 60
31
+ const MINUTE_MS = SECONDS_PER_MINUTE * SECOND_MS
32
+ const UNIT_MS: Readonly<Record<string, number>> = {
33
+ h: MINUTES_PER_HOUR * MINUTE_MS,
34
+ m: MINUTE_MS,
35
+ s: SECOND_MS,
36
+ }
37
+ // Past the window's end, so the retrigger does not land on its last second.
38
+ const MARGIN_SECONDS = 15
39
+ const MARGIN_MS = MARGIN_SECONDS * SECOND_MS
40
+ // The longest window `followup` waits out; a longer one is left to the person (`followup.md`).
41
+ const WINDOW_CAP_MINUTES = 30
42
+ const WINDOW_CAP_MS = WINDOW_CAP_MINUTES * MINUTE_MS
43
+ // The wait for the retriggered review: twenty minutes, polled every thirty seconds.
44
+ const REVIEW_WAIT_ATTEMPTS = 40
45
+ const REVIEW_WAIT_INTERVAL_SECONDS = 30
46
+ const REVIEW_WAIT_INTERVAL_MS = REVIEW_WAIT_INTERVAL_SECONDS * SECOND_MS
47
+ const NO_TIME = 0
48
+ const FIRST = 0
49
+ const NEXT = 1
50
+ const RATE_LIMITED: CodeRabbitState = 'rate-limited'
51
+ const TIER_C = 'bypassing it is Tier C — explicit user instruction only'
52
+ const BY_HAND = `wait it out, post \`${RETRIGGER_BODY}\` and rerun followup; ${TIER_C}`
53
+ const AGAIN_NOTE = `CodeRabbit rate-limited again after the one \`${RETRIGGER_BODY}\` (kit#5289): ${TIER_C}`
54
+ const UNREADABLE_NOTE = `CodeRabbit rate-limited with no window readable from its comments (kit#5289): no retrigger posted — ${BY_HAND}`
55
+ const UNFINISHED_NOTE = `CodeRabbit still reviewing after \`${RETRIGGER_BODY}\` (kit#5289): the merge goes ahead without it`
56
+
57
+ interface PullComment {
58
+ body?: string | undefined
59
+ author?: { login?: string | undefined } | undefined
60
+ updatedAt?: string | undefined
61
+ }
62
+
63
+ type Decision =
64
+ { kind: 'retrigger'; wait_ms: number } | { kind: 'await' } | { kind: 'stop'; note: string }
65
+
66
+ interface Retrigger {
67
+ is_retriggered: boolean
68
+ notes: Array<string>
69
+ }
70
+
71
+ interface Settled<T> {
72
+ snapshot: T
73
+ is_retriggered: boolean
74
+ notes: Array<string>
75
+ }
76
+
77
+ function wait_clause_of(body: string): string {
78
+ const after_opener = body.split(WAIT_OPENER)[NEXT] ?? ''
79
+
80
+ return after_opener.split(WAIT_CLOSER)[FIRST] ?? ''
81
+ }
82
+
83
+ // The time one token stands for: a number times the unit the next token starts with.
84
+ function token_ms(tokens: ReadonlyArray<string>, index: number): number {
85
+ const token = tokens[index] ?? ''
86
+ if (!DIGITS.test(token)) return NO_TIME
87
+ const unit = (tokens[index + NEXT] ?? '').charAt(FIRST).toLowerCase()
88
+
89
+ return Number(token) * (UNIT_MS[unit] ?? NO_TIME)
90
+ }
91
+
92
+ // The wait CodeRabbit's notice states, or `undefined` for a comment that states none.
93
+ function parse_wait_ms(body: string): number | undefined {
94
+ if (!is_coderabbit_rate_limit(body)) return undefined
95
+ const tokens = wait_clause_of(body).match(TOKEN) ?? []
96
+ const wait_ms = tokens.reduce((sum, _token, index) => sum + token_ms(tokens, index), NO_TIME)
97
+
98
+ return wait_ms === NO_TIME ? undefined : wait_ms
99
+ }
100
+
101
+ function is_notice(comment: PullComment): boolean {
102
+ return (
103
+ is_coderabbit_author(comment.author?.login ?? '') &&
104
+ is_coderabbit_rate_limit(comment.body ?? '')
105
+ )
106
+ }
107
+
108
+ function is_retrigger(comment: PullComment): boolean {
109
+ return comment.body?.trim() === RETRIGGER_BODY
110
+ }
111
+
112
+ function written_ms(comment: PullComment): number {
113
+ return Date.parse(comment.updatedAt ?? '')
114
+ }
115
+
116
+ // The latest time among the comments `is_match` picks; one with no readable time counts as none.
117
+ function latest_ms(
118
+ comments: ReadonlyArray<PullComment>,
119
+ is_match: (comment: PullComment) => boolean,
120
+ ): number | undefined {
121
+ const times = comments
122
+ .filter((comment) => is_match(comment))
123
+ .map((comment) => written_ms(comment))
124
+ .filter((ms) => !Number.isNaN(ms))
125
+
126
+ return times.length === 0 ? undefined : Math.max(...times)
127
+ }
128
+
129
+ // The latest end any CodeRabbit notice states — the window counts from when the notice was written.
130
+ function window_end_ms(comments: ReadonlyArray<PullComment>): number | undefined {
131
+ const ends = comments
132
+ .filter((comment) => is_notice(comment))
133
+ .map((comment) => written_ms(comment) + (parse_wait_ms(comment.body ?? '') ?? NaN))
134
+ .filter((ms) => !Number.isNaN(ms))
135
+
136
+ return ends.length === 0 ? undefined : Math.max(...ends)
137
+ }
138
+
139
+ // A retrigger written after the latest notice is still to be answered; one before it was answered
140
+ // with another rate limit. Either time unreadable reads as the second.
141
+ function decide_after_retrigger(comments: ReadonlyArray<PullComment>): Decision {
142
+ const retrigger_ms = latest_ms(comments, is_retrigger) ?? NO_TIME
143
+ const notice_ms = latest_ms(comments, is_notice) ?? retrigger_ms
144
+
145
+ return retrigger_ms > notice_ms ? { kind: 'await' } : { kind: 'stop', note: AGAIN_NOTE }
146
+ }
147
+
148
+ function too_long_note(wait_ms: number): string {
149
+ const minutes = String(Math.ceil(wait_ms / MINUTE_MS))
150
+
151
+ return `CodeRabbit rate-limited for ${minutes} more minutes, over followup's ${String(WINDOW_CAP_MINUTES)} minute wait (kit#5289): no retrigger posted — ${BY_HAND}`
152
+ }
153
+
154
+ function decide(comments: ReadonlyArray<PullComment>, now_ms: number): Decision {
155
+ if (comments.some((comment) => is_retrigger(comment))) return decide_after_retrigger(comments)
156
+ const end_ms = window_end_ms(comments)
157
+ if (end_ms === undefined) return { kind: 'stop', note: UNREADABLE_NOTE }
158
+ const wait_ms = Math.max(NO_TIME, end_ms - now_ms) + MARGIN_MS
159
+
160
+ return wait_ms > WINDOW_CAP_MS
161
+ ? { kind: 'stop', note: too_long_note(wait_ms) }
162
+ : { kind: 'retrigger', wait_ms }
163
+ }
164
+
165
+ async function read_comments(branch_name: string): Promise<Array<PullComment>> {
166
+ const raw = await git_gh_command.pr_get_comments(branch_name)
167
+
168
+ return parse_json_array_or_undefined(raw ?? '', ai_review_pull_comment_schema) ?? []
169
+ }
170
+
171
+ async function read_state(
172
+ branch_name: string,
173
+ observe: SnapshotObserver,
174
+ ): Promise<CodeRabbitState> {
175
+ const checks = await git_gh_command.pr_get_checks_snapshot(branch_name)
176
+ const snapshot = parse_pr_state_snapshot(checks.snapshot_json)
177
+
178
+ await observe(snapshot)
179
+
180
+ return followup_checks_event.coderabbit_of(snapshot)
181
+ }
182
+
183
+ // **A rate limit read before the review started is the old one**, so only one read after another
184
+ // state is the second rate limit; a check that never moves off it is read as one at the budget's end.
185
+ // One observer for the whole wait, so its polls are timed against one another.
186
+ async function wait_for_review(branch_name: string, wait: CodeRabbitWait): Promise<Array<string>> {
187
+ const observe = followup_coderabbit_wait.observer_of(wait)
188
+ let is_started = false
189
+ let state: CodeRabbitState = RATE_LIMITED
190
+ const is_settled = await poll.poll_until(
191
+ async () => {
192
+ state = await read_state(branch_name, observe)
193
+ is_started ||= state !== RATE_LIMITED
194
+
195
+ return is_started && state !== 'running'
196
+ },
197
+ { attempts: REVIEW_WAIT_ATTEMPTS, interval_ms: REVIEW_WAIT_INTERVAL_MS, sleeper: poll.sleep },
198
+ )
199
+ if (state === RATE_LIMITED) return [AGAIN_NOTE]
200
+
201
+ return is_settled ? [] : [UNFINISHED_NOTE]
202
+ }
203
+
204
+ async function retrigger(
205
+ branch_name: string,
206
+ wait: CodeRabbitWait,
207
+ wait_ms: number,
208
+ ): Promise<Retrigger> {
209
+ const seconds = String(Math.ceil(wait_ms / SECOND_MS))
210
+
211
+ console.info(`⏳ CodeRabbit rate-limited — waiting ${seconds}s, then \`${RETRIGGER_BODY}\``)
212
+ await poll.sleep(wait_ms)
213
+ await git_gh_command.pr_comment(branch_name, RETRIGGER_BODY)
214
+ await lane_ledger_write.record_stage({
215
+ stage: lane_ledger_write.CODERABBIT_RETRIGGER_STAGE,
216
+ elapsed_ms: wait_ms,
217
+ })
218
+
219
+ return { is_retriggered: true, notes: await wait_for_review(branch_name, wait) }
220
+ }
221
+
222
+ // Runs only when the check wait last saw CodeRabbit rate-limited; anything else is untouched.
223
+ async function run(branch_name: string, wait: CodeRabbitWait): Promise<Retrigger> {
224
+ if (wait.last?.state !== RATE_LIMITED) return { is_retriggered: false, notes: [] }
225
+ const decision = decide(await read_comments(branch_name), Date.now())
226
+ if (decision.kind === 'stop') return { is_retriggered: false, notes: [decision.note] }
227
+
228
+ if (decision.kind === 'await') {
229
+ return { is_retriggered: true, notes: await wait_for_review(branch_name, wait) }
230
+ }
231
+
232
+ return await retrigger(branch_name, wait, decision.wait_ms)
233
+ }
234
+
235
+ // The checks after a retriggered review are read again, so the merge and the skip notes read the
236
+ // snapshot that review left, not the rate-limited one.
237
+ async function settle<T>(
238
+ snapshot: T,
239
+ wait_for_green: () => Promise<T>,
240
+ branch_name: string,
241
+ wait: CodeRabbitWait,
242
+ ): Promise<Settled<T>> {
243
+ const { is_retriggered, notes } = await run(branch_name, wait)
244
+ if (!is_retriggered) return { snapshot, is_retriggered, notes }
245
+
246
+ return { snapshot: await wait_for_green(), is_retriggered, notes }
247
+ }
248
+
249
+ const followup_coderabbit_retrigger = {
250
+ AGAIN_NOTE,
251
+ RETRIGGER_BODY,
252
+ UNFINISHED_NOTE,
253
+ UNREADABLE_NOTE,
254
+ decide,
255
+ parse_wait_ms,
256
+ run,
257
+ settle,
258
+ }
259
+
260
+ export type { PullComment, Retrigger, Settled }
261
+ export { followup_coderabbit_retrigger }