@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
@@ -1,6 +1,3 @@
1
- import { read_pull } from '#scripts/gh/git-gh-pr-read'
2
- import { git_closes_keyword } from '#scripts/git/git-closes-keyword'
3
- import { lane_ledger } from '#scripts/lane/lane-ledger'
4
1
  import { issue_cite } from './issue-cite'
5
2
  import { markdown_section } from './markdown-section'
6
3
 
@@ -14,23 +11,40 @@ import { markdown_section } from './markdown-section'
14
11
  // the wait needs — when the pull request merged — is read ahead by `prime`, once per pull request per
15
12
  // process, wherever bodies first arrive. A body nobody primed reads as waiting: offering it early is
16
13
  // the defect this guards, and the next primed read releases it.
14
+ //
15
+ // joshuafolkken/kit#5220: the reads `prime` makes — the pull request and the lane ledger — live in
16
+ // `issue-sample-prime.ts`, which hands this module the ledger's merges. The readers that only judge a
17
+ // body import this module alone, so a change to the ledger's reading never reaches their suites.
18
+ //
19
+ // joshuafolkken/kit#5051: a lane files its measurement follow-up before its own pull request exists —
20
+ // `josh ship` wants the deferral row first. So the condition may cite the issue instead (`CLOSING_FORMAT`),
21
+ // and `prime` resolves the merged pull request that closes it; until one exists the body waits as
22
+ // unmerged.
17
23
 
18
24
  const BACKGROUND_HEADING = '## 背景'
19
25
  const PREFIX = '- 実施可能条件:'
20
- const CONDITION_PATTERN = /^#(?<pr>[1-9]\d*) のマージ後 (?<count>[1-9]\d*) マージ$/u
26
+ const CONDITION_PATTERN =
27
+ /^#(?<cited>[1-9]\d*) (?<closing>を閉じる PR )?のマージ後 (?<count>[1-9]\d*) マージ$/u
21
28
  const VALUE_FORMAT = '#<PR> のマージ後 <K> マージ'
29
+ const CLOSING_FORMAT = '#<issue> を閉じる PR のマージ後 <K> マージ'
22
30
  const DECLARATION = `${PREFIX} ${VALUE_FORMAT}`
23
- const MALFORMED = `\`${PREFIX}\` must read \`${VALUE_FORMAT}\` with K at least 1`
31
+ // The refusal ends in a line ready to paste (joshuafolkken/kit#5225).
32
+ const EXAMPLE = `${PREFIX} #5198 のマージ後 30 マージ`
33
+ const MALFORMED = `\`${PREFIX}\` must read \`${VALUE_FORMAT}\` or \`${CLOSING_FORMAT}\` with K at least 1\n${EXAMPLE}`
34
+
35
+ // `pull` cites the pull request itself, `issue` the issue whose closing pull request it waits on.
36
+ type Via = 'pull' | 'issue'
24
37
 
25
38
  interface Condition {
26
- pr: number
39
+ cited: number
40
+ via: Via
27
41
  count: number
28
42
  }
29
43
 
30
44
  type SampleWait =
31
- | { kind: 'unread'; pr: number }
32
- | { kind: 'unmerged'; pr: number }
33
- | { kind: 'short'; pr: number; counted: number; needed: number }
45
+ | { kind: 'unread'; cited: number; via: Via }
46
+ | { kind: 'unmerged'; cited: number; via: Via }
47
+ | { kind: 'short'; cited: number; via: Via; counted: number; needed: number }
34
48
 
35
49
  // One pull request as read: `merged_at` absent is one found unmerged, read again as it may merge since.
36
50
  // `closes` is the issue its body closes, whose ledger merge is the pull request's own.
@@ -39,19 +53,26 @@ interface PullRead {
39
53
  closes: number | undefined
40
54
  }
41
55
 
42
- interface PrimePorts {
43
- pull: (pr: number) => Promise<PullRead>
44
- ledger_path: () => Promise<string | undefined>
56
+ // One merge the lane ledger recorded, keyed by the issue its pull request closed.
57
+ interface MergeSample {
58
+ at: string
59
+ issue: number
45
60
  }
46
61
 
62
+ // The ledger's merges as they read now; none until a prime has handed them over.
47
63
  interface LedgerState {
48
- path: string | undefined
64
+ merges: () => ReadonlyArray<MergeSample>
49
65
  }
50
66
 
51
- // A pull request with no entry was never read.
67
+ // A cited number with no entry was never read. Keyed by `key_of`, as one number may be cited both ways.
68
+
69
+ const pull_by_key = new Map<string, PullRead>()
70
+
71
+ function no_merges(): ReadonlyArray<MergeSample> {
72
+ return []
73
+ }
52
74
 
53
- const pull_by_number = new Map<number, PullRead>()
54
- const ledger: LedgerState = { path: undefined }
75
+ const ledger: LedgerState = { merges: no_merges }
55
76
 
56
77
  function declared_value(body: string): string | undefined {
57
78
  const background = markdown_section.section_lines(body, BACKGROUND_HEADING).join('\n')
@@ -63,6 +84,10 @@ function declared_value(body: string): string | undefined {
63
84
  return line?.slice(PREFIX.length).trim()
64
85
  }
65
86
 
87
+ function via_of(groups: Partial<Record<string, string>>): Via {
88
+ return groups['closing'] === undefined ? 'pull' : 'issue'
89
+ }
90
+
66
91
  // The declared condition, or `undefined` when there is none or it does not parse — a malformed one is
67
92
  // refused by `issue:lint`, and holding a hand-edited one forever would lose the issue silently.
68
93
  function condition_of(body = ''): Condition | undefined {
@@ -70,12 +95,23 @@ function condition_of(body = ''): Condition | undefined {
70
95
 
71
96
  if (groups === undefined) return undefined
72
97
 
73
- return { pr: Number(groups['pr']), count: Number(groups['count']) }
98
+ return { cited: Number(groups['cited']), via: via_of(groups), count: Number(groups['count']) }
74
99
  }
75
100
 
76
101
  // The number the declared condition cites, or `undefined` when there is none or it does not parse.
77
102
  function cited_number(body: string): number | undefined {
78
- return condition_of(body)?.pr
103
+ return condition_of(body)?.cited
104
+ }
105
+
106
+ // The number cited by the given form only — the issue form cites an issue by design.
107
+ function cited_via(body: string, via: Via): number | undefined {
108
+ const condition = condition_of(body)
109
+
110
+ return condition?.via === via ? condition.cited : undefined
111
+ }
112
+
113
+ function key_of({ cited, via }: Pick<Condition, 'cited' | 'via'>): string {
114
+ return `${via}:${String(cited)}`
79
115
  }
80
116
 
81
117
  function problems(body: string): ReadonlyArray<string> {
@@ -84,63 +120,38 @@ function problems(body: string): ReadonlyArray<string> {
84
120
  return value === undefined || CONDITION_PATTERN.test(value) ? [] : [MALFORMED]
85
121
  }
86
122
 
87
- async function read_source(pr: number): Promise<PullRead> {
88
- const pull = await read_pull(pr)
89
- const closes = git_closes_keyword.parse_closes_issue_number(pull.body ?? undefined)
123
+ // Keeps what a prime read for the condition; `undefined` — a failed read — leaves it unread.
124
+ function record(condition: Condition, pull: PullRead | undefined): void {
125
+ const key = key_of(condition)
90
126
 
91
- return {
92
- merged_at: pull.merged_at ?? undefined,
93
- closes: closes === undefined ? undefined : Number(closes),
94
- }
127
+ if (pull === undefined) pull_by_key.delete(key)
128
+ else pull_by_key.set(key, pull)
95
129
  }
96
130
 
97
- const DEFAULT_PORTS: PrimePorts = { pull: read_source, ledger_path: lane_ledger.target }
98
-
99
- // A failed read stays unread, so the next prime asks again.
100
- async function remember(pr: number, ports: PrimePorts): Promise<void> {
101
- try {
102
- pull_by_number.set(pr, await ports.pull(pr))
103
- } catch {
104
- pull_by_number.delete(pr)
105
- }
131
+ function use_merges(merges: () => ReadonlyArray<MergeSample>): void {
132
+ ledger.merges = merges
106
133
  }
107
134
 
108
- function is_merge_known(pr: number): boolean {
109
- return pull_by_number.get(pr)?.merged_at !== undefined
135
+ function is_merge_known(condition: Condition): boolean {
136
+ return pull_by_key.get(key_of(condition))?.merged_at !== undefined
110
137
  }
111
138
 
112
- // Reads ahead what the bodies' conditions need. Nothing is read for bodies that declare none.
113
- async function prime(
114
- bodies: ReadonlyArray<string | undefined>,
115
- ports: PrimePorts = DEFAULT_PORTS,
116
- ): Promise<void> {
139
+ // The conditions the bodies declare whose pull request has not been read merged, one per key.
140
+ function unread_of(bodies: ReadonlyArray<string | undefined>): Array<Condition> {
117
141
  const unread = bodies
118
- .map((body) => condition_of(body)?.pr)
119
- .filter((pr): pr is number => pr !== undefined && !is_merge_known(pr))
120
-
121
- if (unread.length === 0) return
142
+ .map((body) => condition_of(body))
143
+ .filter(
144
+ (condition): condition is Condition => condition !== undefined && !is_merge_known(condition),
145
+ )
122
146
 
123
- ledger.path = await ports.ledger_path()
124
- await Promise.all(
125
- [...new Set(unread)].map(async (pr) => {
126
- await remember(pr, ports)
127
- }),
128
- )
147
+ return [...new Map(unread.map((condition) => [key_of(condition), condition])).values()]
129
148
  }
130
149
 
131
150
  function merges_after({ merged_at, closes }: PullRead & { merged_at: string }): number {
132
- if (ledger.path === undefined) return 0
133
-
134
151
  const since = Date.parse(merged_at)
135
152
 
136
- return lane_ledger
137
- .read_entries(ledger.path)
138
- .filter(
139
- (entry) =>
140
- entry.kind === lane_ledger.KIND.MERGE &&
141
- entry.issue !== closes &&
142
- Date.parse(entry.at) > since,
143
- ).length
153
+ return ledger.merges().filter((merge) => merge.issue !== closes && Date.parse(merge.at) > since)
154
+ .length
144
155
  }
145
156
 
146
157
  // What the body still waits on, or `undefined` when it declares nothing or has its samples.
@@ -149,44 +160,54 @@ function wait_of(body?: string): SampleWait | undefined {
149
160
 
150
161
  if (condition === undefined) return undefined
151
162
 
152
- const { pr, count } = condition
153
- const pull = pull_by_number.get(pr)
163
+ const { cited, via, count } = condition
164
+ const pull = pull_by_key.get(key_of(condition))
154
165
 
155
- if (pull === undefined) return { kind: 'unread', pr }
166
+ if (pull === undefined) return { kind: 'unread', cited, via }
156
167
 
157
168
  const { merged_at, closes } = pull
158
169
 
159
- if (merged_at === undefined) return { kind: 'unmerged', pr }
170
+ if (merged_at === undefined) return { kind: 'unmerged', cited, via }
160
171
 
161
172
  const counted = merges_after({ merged_at, closes })
162
173
 
163
- return counted >= count ? undefined : { kind: 'short', pr, counted, needed: count }
174
+ return counted >= count ? undefined : { kind: 'short', cited, via, counted, needed: count }
175
+ }
176
+
177
+ function subject_of({ cited, via }: SampleWait): string {
178
+ const plain = issue_cite.plain(cited)
179
+
180
+ return via === 'pull' ? plain : `the pull request closing ${plain}`
164
181
  }
165
182
 
166
183
  // Why the body waits, for a reader that cites the issue itself.
167
184
  function reason_of(wait: SampleWait): string {
168
- const pr = issue_cite.plain(wait.pr)
185
+ const subject = subject_of(wait)
169
186
 
170
- if (wait.kind === 'unread') return `until the merge of ${pr} is read`
171
- if (wait.kind === 'unmerged') return `until ${pr} merges`
187
+ if (wait.kind === 'unread') return `until the merge of ${subject} is read`
188
+ if (wait.kind === 'unmerged') return `until ${subject} merges`
172
189
 
173
- return `${String(wait.counted)}/${String(wait.needed)} merges after ${pr}`
190
+ return `${String(wait.counted)}/${String(wait.needed)} merges after ${subject}`
174
191
  }
175
192
 
176
193
  function reset(): void {
177
- pull_by_number.clear()
178
- ledger.path = undefined
194
+ pull_by_key.clear()
195
+ ledger.merges = no_merges
179
196
  }
180
197
 
181
198
  const issue_sample_gate = {
182
199
  DECLARATION,
200
+ CLOSING_FORMAT,
183
201
  cited_number,
184
- prime,
202
+ cited_via,
185
203
  problems,
186
204
  reason_of,
205
+ record,
187
206
  reset,
207
+ unread_of,
208
+ use_merges,
188
209
  wait_of,
189
210
  }
190
211
 
191
- export type { PrimePorts, SampleWait }
212
+ export type { Condition, MergeSample, PullRead, SampleWait }
192
213
  export { issue_sample_gate }
@@ -0,0 +1,127 @@
1
+ import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
2
+ import { git_gh_exec } from '#scripts/gh/git-gh-exec'
3
+ import { git_gh_issue_rest } from '#scripts/gh/git-gh-issue-rest'
4
+ import { read_pull } from '#scripts/gh/git-gh-pr-read'
5
+ import { git_gh_pr_too_large } from '#scripts/gh/git-gh-pr-too-large'
6
+ import { git_closes_keyword } from '#scripts/git/git-closes-keyword'
7
+ import { lane_ledger, type MergeEntry } from '#scripts/lane/lane-ledger'
8
+ import { issue_closing_pr } from './issue-closing-pr'
9
+ import {
10
+ issue_sample_gate,
11
+ type Condition,
12
+ type MergeSample,
13
+ type PullRead,
14
+ } from './issue-sample-gate'
15
+
16
+ // The reads `issue_sample_gate` waits on, made ahead where bodies first arrive (joshuafolkken/kit#4414):
17
+ // when the cited pull request merged, and the merges the lane ledger recorded. Kept apart from the
18
+ // gate so that only the primers reach the ledger's reader (joshuafolkken/kit#5220).
19
+
20
+ // The two fields `read_source` takes from a pull request, whichever endpoint served them.
21
+ interface PullFields {
22
+ body?: string | null | undefined
23
+ merged_at?: string | null | undefined
24
+ }
25
+
26
+ // `closing` reads the pull request that closes an issue; one not merged yet reads as unmerged.
27
+ interface PrimePorts {
28
+ pull: (pr: number) => Promise<PullRead>
29
+ closing: (issue: number) => Promise<PullRead>
30
+ ledger_path: () => Promise<string | undefined>
31
+ }
32
+
33
+ // joshuafolkken/kit#5241: GitHub refuses the single pull request read of a large one with 422 `too many
34
+ // files changed`, and a closing number carries no branch to read the listing row by. The issue
35
+ // endpoint serves the same pull request's body and merge time.
36
+ async function read_as_issue(pr: number): Promise<PullFields> {
37
+ const json = await git_gh_exec.exec_gh_api({ path: git_gh_api_path.issue_api_path(String(pr)) })
38
+ const issue = git_gh_issue_rest.parse_rest_issue(json)
39
+
40
+ return { body: issue.body, merged_at: issue.pull_request?.merged_at }
41
+ }
42
+
43
+ async function read_fields(pr: number): Promise<PullFields> {
44
+ try {
45
+ return await read_pull(pr)
46
+ } catch (error) {
47
+ if (!git_gh_pr_too_large.is_too_many_files(error)) throw error
48
+
49
+ return await read_as_issue(pr)
50
+ }
51
+ }
52
+
53
+ async function read_source(pr: number): Promise<PullRead> {
54
+ const pull = await read_fields(pr)
55
+ const closes = git_closes_keyword.parse_closes_issue_number(pull.body ?? undefined)
56
+
57
+ return {
58
+ merged_at: pull.merged_at ?? undefined,
59
+ closes: closes === undefined ? undefined : Number(closes),
60
+ }
61
+ }
62
+
63
+ // The merged pull request closing the issue; `closes` is the issue itself, whichever pull request it is.
64
+ async function read_closing_source(issue: number): Promise<PullRead> {
65
+ const pr = await issue_closing_pr.read_closing_number(issue)
66
+
67
+ if (pr === undefined) return { merged_at: undefined, closes: issue }
68
+
69
+ const { merged_at } = await read_source(pr)
70
+
71
+ return { merged_at, closes: issue }
72
+ }
73
+
74
+ const DEFAULT_PORTS: PrimePorts = {
75
+ pull: read_source,
76
+ closing: read_closing_source,
77
+ ledger_path: lane_ledger.target,
78
+ }
79
+
80
+ // The ledger's merges, read on each judgement so a merge recorded since the prime counts; an absent
81
+ // ledger records none.
82
+ function merges_of(path: string | undefined): () => ReadonlyArray<MergeSample> {
83
+ return function merges(): ReadonlyArray<MergeSample> {
84
+ if (path === undefined) return []
85
+
86
+ return lane_ledger
87
+ .read_entries(path)
88
+ .filter((entry): entry is MergeEntry => entry.kind === lane_ledger.KIND.MERGE)
89
+ }
90
+ }
91
+
92
+ // A failed read stays unread, so the next prime asks again.
93
+ async function remember(condition: Condition, ports: PrimePorts): Promise<void> {
94
+ const read = condition.via === 'pull' ? ports.pull : ports.closing
95
+
96
+ try {
97
+ issue_sample_gate.record(condition, await read(condition.cited))
98
+ } catch {
99
+ issue_sample_gate.record(condition, undefined)
100
+ }
101
+ }
102
+
103
+ // Reads ahead what the bodies' conditions need. Nothing is read for bodies that declare none.
104
+ async function prime(
105
+ bodies: ReadonlyArray<string | undefined>,
106
+ ports: PrimePorts = DEFAULT_PORTS,
107
+ ): Promise<void> {
108
+ const unread = issue_sample_gate.unread_of(bodies)
109
+
110
+ if (unread.length === 0) return
111
+
112
+ issue_sample_gate.use_merges(merges_of(await ports.ledger_path()))
113
+ await Promise.all(
114
+ unread.map(async (condition) => {
115
+ await remember(condition, ports)
116
+ }),
117
+ )
118
+ }
119
+
120
+ const issue_sample_prime = {
121
+ prime,
122
+ read_closing: read_closing_source,
123
+ read_source,
124
+ }
125
+
126
+ export type { PrimePorts }
127
+ export { issue_sample_prime }
@@ -0,0 +1,57 @@
1
+ import { acceptance_map } from '#scripts/review/acceptance-map'
2
+ import { issue_sample_gate } from './issue-sample-gate'
3
+ import { measure_declaration } from './measure-declaration'
4
+ import { size_estimate } from './size-estimate'
5
+
6
+ // joshuafolkken/kit#4901: a follow-up that measures a pull request's effect reads records that only
7
+ // the merges after it write. Filed without its start condition, the backlog offers it at once and the
8
+ // lane finds no sample (#4897). It is refused until it declares the condition (`measurement.md`).
9
+ //
10
+ // A measurement follow-up is a body carrying a measure declaration, or one changing no file whose criterion
11
+ // reads the period after a cited change: its wording says "after #N" whichever record it reads
12
+ // (#5103), or it names a reader of the post-merge records. A record read that cites nothing (a CI
13
+ // investigation) waits on no merge, so it files as before. A bare "after" character ends many words
14
+ // that are no period ("last", "around", "from now on"), so it counts only right after the citation,
15
+ // a possessive particle or "merge"; one opening a word ("see below", "cleanup", "successor") is no
16
+ // period either.
17
+
18
+ const AFTER_CITED_CHANGE: ReadonlyArray<RegExp> = [
19
+ /#[1-9]\d*(?:\s*|[^#\n]{0,16}?(?:の|マージ(?:した|直)?))後(?![述始片続])/u,
20
+ /#[1-9]\d*[^#\n]{0,16}?(?:以降|以後)/u,
21
+ ]
22
+ const RECORD_READERS: ReadonlyArray<string> = ['lane:stats', 'actions/runs', 'ship --log']
23
+ const CITATION_PATTERN = /#[1-9]\d*/u
24
+ const MISSING = `measurement follow-up without its start condition: declare \`${issue_sample_gate.DECLARATION}\`, or \`${issue_sample_gate.CLOSING_FORMAT}\` before that pull request exists, under ## 背景 — prompts/collaboration-workflow/measurement.md → "効果を測る後続 Issue"`
25
+ // joshuafolkken/kit#5144: one waiting K merges reads one value against a bound, which `backlog:drive`
26
+ // does without a lane once it is declared; the declaration is the default, a stated reason the exception.
27
+ const UNDECLARED = `measurement follow-up without a measure declaration: declare \`${measure_declaration.DECLARATION}\` (any \`lane:stats\` cell: \`pnpm josh lane:value cell\`), or \`${measure_declaration.ABSENT}\` when no one command prints the value, under ## 背景 — prompts/collaboration-workflow/measurement.md → "効果を測る後続 Issue"\n${measure_declaration.EXAMPLE}`
28
+
29
+ function measures_a_change(criterion: string): boolean {
30
+ if (AFTER_CITED_CHANGE.some((pattern) => pattern.test(criterion))) return true
31
+
32
+ return (
33
+ CITATION_PATTERN.test(criterion) && RECORD_READERS.some((reader) => criterion.includes(reader))
34
+ )
35
+ }
36
+
37
+ function is_measurement(body: string): boolean {
38
+ if (measure_declaration.parse(body) !== undefined) return true
39
+ if (size_estimate.declared_files(body) !== 0) return false
40
+
41
+ return acceptance_map.criteria_of(body).some((criterion) => measures_a_change(criterion.text))
42
+ }
43
+
44
+ // The refusal for a measurement follow-up whose start condition does not parse — missing, or
45
+ // malformed beside `issue_sample_gate.problems`' format refusal — or whose condition parses with no
46
+ // measure declaration nor a reason for none; `undefined` for any other filing.
47
+ function problem(body: string): string | undefined {
48
+ if (issue_sample_gate.cited_number(body) !== undefined) {
49
+ return measure_declaration.is_stated(body) ? undefined : UNDECLARED
50
+ }
51
+
52
+ return is_measurement(body) ? MISSING : undefined
53
+ }
54
+
55
+ const issue_sample_required = { problem }
56
+
57
+ export { issue_sample_required }
@@ -15,7 +15,7 @@ import { session_cite } from './session-cite'
15
15
 
16
16
  interface SourcePorts {
17
17
  is_pull: (number: number, repo: string | undefined) => Promise<boolean | undefined>
18
- closing_pr: (issue: number) => Promise<string | undefined>
18
+ closing_pr: (issue: number) => Promise<number | undefined>
19
19
  }
20
20
 
21
21
  async function read_is_pull(
@@ -33,15 +33,14 @@ async function read_is_pull(
33
33
  }
34
34
  }
35
35
 
36
- // The closing pull request's number, read off the URL `read_closing_pr` answers.
37
- async function read_closing_pr(issue: number): Promise<string | undefined> {
38
- const url = await issue_closing_pr.read_closing_pr(String(issue))
39
-
40
- return url?.slice(url.lastIndexOf('/') + 1)
36
+ async function read_closing_number(issue: number): Promise<number | undefined> {
37
+ return await issue_closing_pr.read_closing_number(issue)
41
38
  }
42
39
 
43
- const DEFAULT_PORTS: SourcePorts = { is_pull: read_is_pull, closing_pr: read_closing_pr }
40
+ const DEFAULT_PORTS: SourcePorts = { is_pull: read_is_pull, closing_pr: read_closing_number }
44
41
 
42
+ // No merged closing pull request yet — the lane filing ahead of its own (joshuafolkken/kit#5051) —
43
+ // points at the form that waits on the pull request to come.
45
44
  async function hint_of(
46
45
  issue: number,
47
46
  repo: string | undefined,
@@ -49,19 +48,21 @@ async function hint_of(
49
48
  ): Promise<string> {
50
49
  const closing = repo === undefined ? await ports.closing_pr(issue) : undefined
51
50
 
52
- if (closing === undefined) return 'cite the pull request whose merge it waits on'
51
+ if (closing === undefined) {
52
+ return `cite the pull request whose merge it waits on, or write \`${issue_sample_gate.CLOSING_FORMAT}\` to wait on the one that closes it`
53
+ }
53
54
 
54
55
  return `cite the pull request that closed it: ${session_cite.issue(closing)}`
55
56
  }
56
57
 
57
- // The refusal for a condition citing an issue, or `undefined` when it cites a pull request, cites
58
- // nothing, or cannot be read.
58
+ // The refusal for a pull request condition citing an issue, or `undefined` when it cites a pull
59
+ // request, cites nothing, uses the issue form, or cannot be read.
59
60
  async function problem(
60
61
  body: string,
61
62
  repo?: string,
62
63
  ports: SourcePorts = DEFAULT_PORTS,
63
64
  ): Promise<string | undefined> {
64
- const number = issue_sample_gate.cited_number(body)
65
+ const number = issue_sample_gate.cited_via(body, 'pull')
65
66
 
66
67
  if (number === undefined || (await ports.is_pull(number, repo)) !== false) return undefined
67
68
  const cited = session_cite.issue(number, undefined, repo)
@@ -0,0 +1,124 @@
1
+ import { git_gh_exec } from '#scripts/gh/git-gh-exec'
2
+ import { issue_label_schema } from '#scripts/git/git-schemas'
3
+ import { parse_json } from '#scripts/git/parse-json-array'
4
+ import { bounded_pool } from '#scripts/lib/bounded-pool'
5
+ import { z } from 'zod'
6
+
7
+ // Every issue one `search/issues` query matches, read page by page. `defect:rate`'s windows and
8
+ // `effect:audit`'s listing ask it.
9
+
10
+ const PER_PAGE = '100'
11
+ const FIRST_PAGE = 1
12
+ const SECOND_PAGE = 2
13
+ // The search API serves at most this many results, so a window past it holds no further page.
14
+ const SEARCH_RESULT_CEILING = 1000
15
+ // The search API allows 30 requests a minute, so this bounds only how many are in flight at once.
16
+ const PAGE_CONCURRENCY = 4
17
+
18
+ const search_item_schema = z.object({
19
+ number: z.number().optional(),
20
+ title: z.string().optional(),
21
+ state: z.string().optional(),
22
+ body: z.string().nullish(),
23
+ labels: z.array(issue_label_schema),
24
+ })
25
+
26
+ // One page of `search/issues`.
27
+ const search_page_schema = z.object({
28
+ total_count: z.number(),
29
+ incomplete_results: z.boolean(),
30
+ items: z.array(search_item_schema),
31
+ })
32
+
33
+ type SearchPage = z.infer<typeof search_page_schema>
34
+
35
+ interface SearchIssue {
36
+ number: number
37
+ title: string
38
+ state: string
39
+ body: string
40
+ labels: ReadonlyArray<string>
41
+ }
42
+
43
+ interface SearchResult {
44
+ issues: ReadonlyArray<SearchIssue>
45
+ is_capped: boolean
46
+ }
47
+
48
+ function search_path(query: string, page: number = FIRST_PAGE): string {
49
+ const parameters = new URLSearchParams({ q: query, per_page: PER_PAGE })
50
+
51
+ if (page !== FIRST_PAGE) parameters.set('page', String(page))
52
+
53
+ return `search/issues?${parameters.toString()}`
54
+ }
55
+
56
+ // How many pages the search holds, read off the first page's own count. Bounded by the API's
57
+ // ceiling, past which GitHub serves no page at all — so this asks for exactly the pages `gh api
58
+ // --paginate` would walk to.
59
+ function page_count(first: SearchPage): number {
60
+ return Math.ceil(Math.min(first.total_count, SEARCH_RESULT_CEILING) / Number(PER_PAGE))
61
+ }
62
+
63
+ async function read_page(query: string, page: number): Promise<SearchPage | undefined> {
64
+ const raw = await git_gh_exec.exec_gh_api({ path: search_path(query, page) })
65
+
66
+ return parse_json.parse_json_object_or_undefined(raw, search_page_schema)
67
+ }
68
+
69
+ // Every page of one search. Not `gh api --paginate`: it follows each page's `next` link one page
70
+ // after another, at about 1.5 s a page. The first page names the total, so the rest are fetched
71
+ // together; any page that does not read fails the whole search.
72
+ async function read_pages(query: string): Promise<ReadonlyArray<SearchPage> | undefined> {
73
+ const first = await read_page(query, FIRST_PAGE)
74
+
75
+ if (first === undefined) return undefined
76
+
77
+ const rest_numbers = Array.from(
78
+ { length: Math.max(page_count(first) - FIRST_PAGE, 0) },
79
+ (_value, index) => index + SECOND_PAGE,
80
+ )
81
+ const rest = await bounded_pool.bounded_map(
82
+ rest_numbers,
83
+ PAGE_CONCURRENCY,
84
+ async (page) => await read_page(query, page),
85
+ )
86
+
87
+ return rest.every((page) => page !== undefined) ? [first, ...rest] : undefined
88
+ }
89
+
90
+ function to_issue(item: z.infer<typeof search_item_schema>): SearchIssue {
91
+ return {
92
+ number: item.number ?? 0,
93
+ title: item.title ?? '',
94
+ state: item.state ?? '',
95
+ body: item.body ?? '',
96
+ labels: item.labels.map((label) => label.name),
97
+ }
98
+ }
99
+
100
+ function to_result(pages: ReadonlyArray<SearchPage>): SearchResult {
101
+ const issues = pages.flatMap((page) => page.items.map((item) => to_issue(item)))
102
+ // The search API serves at most 1000 results, and says so only by a total larger than what it
103
+ // handed back — a window past that ceiling is measured on part of itself.
104
+ const total = pages[0]?.total_count ?? 0
105
+ const is_incomplete = pages.some((page) => page.incomplete_results)
106
+
107
+ return { issues, is_capped: is_incomplete || total > issues.length }
108
+ }
109
+
110
+ // The search's issues, or `undefined` when any page could not be read.
111
+ async function search(query: string): Promise<SearchResult | undefined> {
112
+ try {
113
+ const pages = await read_pages(query)
114
+
115
+ return pages === undefined ? undefined : to_result(pages)
116
+ } catch {
117
+ return undefined
118
+ }
119
+ }
120
+
121
+ const issue_search = { search, search_path }
122
+
123
+ export type { SearchIssue, SearchResult }
124
+ export { issue_search }
@@ -1,6 +1,12 @@
1
1
  import { parse_json_object_or_undefined } from '#scripts/git/parse-json-array'
2
2
  import { z } from 'zod'
3
- import { has_any_label, NEEDS_HUMAN_REVIEW_LABEL } from './issue-labels'
3
+ import {
4
+ has_any_label,
5
+ has_label_name,
6
+ NEEDS_HUMAN_REVIEW_LABEL,
7
+ PLANNED_LABEL,
8
+ } from './issue-labels'
9
+ import type { StateRead } from './issue-state-cli'
4
10
 
5
11
  // The answer `josh issue:state` prints, kept apart from the reading and the printing so the shape of
6
12
  // the report is decided by one pure function.
@@ -93,7 +99,13 @@ function format_attributed_issue_state(issue_number: string, state: IssueState):
93
99
  return `${ISSUE_LABEL}${issue_number}\n${format_issue_state(state)}`
94
100
  }
95
101
 
102
+ // Whether a read issue carries `run:planned` — asked by `run:stage` and by `run:entry`'s trust gate.
103
+ function is_planned(read: StateRead): boolean {
104
+ return read.kind === 'state' && has_label_name(read.state.labels, PLANNED_LABEL)
105
+ }
106
+
96
107
  const issue_state = {
108
+ is_planned,
97
109
  NO_LABELS,
98
110
  parse_issue_state,
99
111
  format_issue_state,