@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,383 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import { setTimeout as sleep } from 'node:timers/promises'
3
+ import { core_budget } from '#scripts/gate/core-budget'
4
+ import { PROJECT_ROOT } from '#scripts/init/init-paths'
5
+ import { process_identity } from '#scripts/josh/process-identity'
6
+ import { stamp_file } from '#scripts/josh/stamp-file'
7
+ import type { LaunchResult } from '#scripts/run/detached-launch'
8
+ import { test_pinned_ledger, type PinnedOrigin } from '#scripts/test/test-pinned-ledger'
9
+ import { test_pinned_record, type PinnedSummary } from '#scripts/test/test-pinned-record'
10
+ import { run_review_steps } from './run-review-steps'
11
+
12
+ // The background `josh test:pinned` of `josh run:review` (joshuafolkken/kit#4770): launched beside the
13
+ // gate and the brief, and waited on at `--join`. Run as its own `josh` command, it reserves its own
14
+ // cores (`DIRECT_UNIT_CLAIM`), so it shares the budget with the gate's units rather than overrunning it.
15
+ //
16
+ // **Two records, both keyed to the checkout.** The check writes its answer under the launch's token
17
+ // (`test-pinned-record.ts`); the start writes the launched pid and that token here, with the
18
+ // `--unpinned-ok` reason the join judges the answer by (joshuafolkken/kit#4793). No pid means no check
19
+ // was launched, and a pid that is gone without an answer is a check that died, which fails open the way
20
+ // a check that could not run does.
21
+
22
+ const PINNED_COMMAND = 'test:pinned'
23
+ const LAUNCH_PREFIX = 'josh-run-review-pinned-launch-'
24
+ const LOG_PREFIX = 'josh-run-review-pinned-log-'
25
+ const MS_PER_SECOND = 1000
26
+ const LOST_NOTE = 'its process ended without an answer'
27
+ const STOP_SIGNAL = 'SIGTERM'
28
+
29
+ // The join waits here only once the gate has settled and the review has returned, so the grace is
30
+ // counted from the end of the pair (joshuafolkken/kit#4794). A check still queued for its cores gets no
31
+ // grace at all (joshuafolkken/kit#4808): it could not finish within it. **Where a read follows, neither
32
+ // is stopped** (joshuafolkken/kit#4888): the check keeps running on the tree the join read, and the note
33
+ // names the one command that waits for its answer, so no lane runs `test:pinned` again by hand. Where
34
+ // none follows, the late check is stopped rather than left holding cores.
35
+ const GRACE_MS = 120_000
36
+ const READ_COMMAND = 'pnpm josh run:review --pinned'
37
+ const STILL_RUNNING_NOTE = `it is still running on this tree; wait for its answer with \`${READ_COMMAND}\` (a lane child: the ship's pre-detach reads it)`
38
+ const TIMED_OUT_NOTE = `no answer within ${String(GRACE_MS / MS_PER_SECOND)}s of the gate and the review`
39
+ const QUEUED_NOTE = 'it was still waiting for cores when the gate and the review ended'
40
+ // A round 1 with fixes to make moves the tree, and the round's `josh review:record` starts the check
41
+ // again on the fixed one — a lane child runs no `josh check:related` after the fixes
42
+ // (joshuafolkken/kit#4983) — so the join waits no grace on this tree's (joshuafolkken/kit#4895).
43
+ const FIXING_NOTE =
44
+ "round 1 has fixes to make — it is left running, and the round's `josh review:record` starts it again on the fixed tree for the ship's pre-detach to read"
45
+ // `josh run:review --pinned` reads a check `josh review:record` started seconds before
46
+ // (joshuafolkken/kit#4834), so it waits through the core queue and the whole run; the bound only
47
+ // stops a check that hung.
48
+ const ALONE_GRACE_MS = 900_000
49
+ const ALONE_TIMED_OUT_NOTE = `no answer within ${String(ALONE_GRACE_MS / MS_PER_SECOND)}s of the read`
50
+
51
+ type Clock = () => number
52
+
53
+ // `start` is the launched pid's start time, so a pid the OS has since reissued is told apart from the
54
+ // check (joshuafolkken/kit#4828). `tree` is the digest of the tree it checks (`run-review-pinned-tree.ts`),
55
+ // so `josh review:record` tells a round-1 fix that moved the tree (joshuafolkken/kit#4834).
56
+ // `launched_at` is the launch's epoch milliseconds, the start of a stopped check's wall time; `origin`
57
+ // its launcher, the one a stopped check's ledger row names (joshuafolkken/kit#5327).
58
+ interface Launch {
59
+ pid: number
60
+ token: string
61
+ reason?: string | undefined
62
+ start?: string | undefined
63
+ tree?: string | undefined
64
+ launched_at?: number | undefined
65
+ origin?: PinnedOrigin | undefined
66
+ }
67
+
68
+ function launch_path(root: string = PROJECT_ROOT): string {
69
+ return stamp_file.stamp_path(LAUNCH_PREFIX, root)
70
+ }
71
+
72
+ function log_path(root: string = PROJECT_ROOT): string {
73
+ return stamp_file.stamp_path(LOG_PREFIX, root, '.log')
74
+ }
75
+
76
+ interface LaunchOptions {
77
+ root: string
78
+ token: string
79
+ argv: Array<string>
80
+ origin: PinnedOrigin
81
+ reason?: string | undefined
82
+ tree?: string | undefined
83
+ }
84
+
85
+ function launch_with({ root, token, argv, origin, reason, tree }: LaunchOptions): LaunchResult {
86
+ const launched = run_review_steps.launch_josh(argv, log_path(root), root)
87
+
88
+ if (launched.kind === 'launched') {
89
+ const { pid } = launched
90
+
91
+ stamp_file.write_stamp(launch_path(root), {
92
+ pid,
93
+ token,
94
+ reason,
95
+ start: process_identity.read_start(pid),
96
+ tree,
97
+ launched_at: Date.now(),
98
+ origin,
99
+ })
100
+ }
101
+
102
+ return launched
103
+ }
104
+
105
+ // The token ties the answer to this launch (`test-pinned-record.ts`).
106
+ function launch(root: string = PROJECT_ROOT, reason?: string, tree?: string): LaunchResult {
107
+ const token = randomUUID()
108
+ const origin = test_pinned_ledger.BACKGROUND_ORIGIN
109
+ const argv = test_pinned_record.launch_argv(token, reason, origin)
110
+
111
+ return launch_with({ root, token, argv, origin, reason, tree })
112
+ }
113
+
114
+ // **A pre-detach that stopped at its first unpinned hunk goes on probing the rest here**
115
+ // (joshuafolkken/kit#5327): every hunk, with no reason, on the stamp `launch` writes, so the next
116
+ // pre-detach joins it (`run-review-pinned-join.ts`) and `clear` discards it as it does that one.
117
+ function launch_remaining(root: string = PROJECT_ROOT, tree?: string): LaunchResult {
118
+ const token = randomUUID()
119
+ const origin: PinnedOrigin = 'pre-detach'
120
+ const argv = test_pinned_record.launch_argv(token, undefined, origin, true)
121
+
122
+ return launch_with({ root, token, argv, origin, tree })
123
+ }
124
+
125
+ function optional_text(value: unknown): string | undefined {
126
+ return typeof value === 'string' ? value : undefined
127
+ }
128
+
129
+ // A stamp written before `origin` was kept names none; its stop is written as `run:review`'s.
130
+ function origin_of(value: unknown): PinnedOrigin | undefined {
131
+ return typeof value === 'string' ? test_pinned_ledger.origin_of(value) : undefined
132
+ }
133
+
134
+ function to_launch(value: unknown): Launch | undefined {
135
+ const { pid, token, reason, start, tree, launched_at, origin } = value as Record<string, unknown>
136
+ if (typeof pid !== 'number' || typeof token !== 'string') return undefined
137
+
138
+ return {
139
+ pid,
140
+ token,
141
+ reason: optional_text(reason),
142
+ start: optional_text(start),
143
+ tree: optional_text(tree),
144
+ launched_at: typeof launched_at === 'number' ? launched_at : undefined,
145
+ origin: origin_of(origin),
146
+ }
147
+ }
148
+
149
+ function read_launch(root: string): Launch | undefined {
150
+ const raw = stamp_file.read_stamp_text(launch_path(root))
151
+
152
+ if (raw === undefined) return undefined
153
+
154
+ try {
155
+ return to_launch(JSON.parse(raw))
156
+ } catch {
157
+ return undefined
158
+ }
159
+ }
160
+
161
+ // One read: the answer, `undefined` while the check is still running, or a not-run answer once its
162
+ // process is gone without one. The record is read again after the pid is found gone, because the
163
+ // check writes its answer before it exits.
164
+ function read_answer(root: string, { pid, token, start }: Launch): PinnedSummary | undefined {
165
+ const summary = test_pinned_record.read(token, root)
166
+
167
+ if (summary !== undefined) return summary
168
+ if (process_identity.is_same_process(pid, start) !== false) return undefined
169
+
170
+ return (
171
+ test_pinned_record.read(token, root) ?? test_pinned_record.not_run_summary(new Error(LOST_NOTE))
172
+ )
173
+ }
174
+
175
+ // The launch is detached, so its pid leads a process group holding the `josh` process and its Vitest
176
+ // workers; signalling the group stops them all, and `base-tree.ts` removes the trees they held.
177
+ function signal_group(pid: number): void {
178
+ try {
179
+ process.kill(-pid, STOP_SIGNAL)
180
+ } catch {
181
+ // It ended between the check and the signal; there is nothing left to stop.
182
+ }
183
+ }
184
+
185
+ // **A check stopped while it runs is a discarded probe** (joshuafolkken/kit#5262): it writes no ledger
186
+ // row of its own, so the stop writes one with the wall time it ran. A launch stamped before
187
+ // `launched_at` has no wall time, and writes none.
188
+ function discard(launched: Launch): void {
189
+ signal_group(launched.pid)
190
+ if (launched.launched_at === undefined) return
191
+
192
+ void test_pinned_ledger.record_discarded(Date.now() - launched.launched_at, launched.origin)
193
+ }
194
+
195
+ // A stale stamp can outlive its check by days, so the start signals only a pid whose start time
196
+ // matches the launch's: a reissued pid, or one whose start could not be read, may lead another
197
+ // lane's group.
198
+ function stop_last(launched: Launch): void {
199
+ if (process_identity.is_same_process(launched.pid, launched.start) === true) discard(launched)
200
+ }
201
+
202
+ // The stamp a waiter just read is this pair's, minutes old, so the lone read and a supervised join
203
+ // signal any pid not proven gone: one whose start could not be read (no `/proc`, no `ps`) is the check
204
+ // they have just been waiting on.
205
+ function stop_current(launched: Launch): void {
206
+ if (process_identity.is_same_process(launched.pid, launched.start) !== false) discard(launched)
207
+ }
208
+
209
+ // Every start stops the last pair's check and clears its records first, so a join never reads an
210
+ // earlier tree's answer and a reopened pair leaves no check running unowned (joshuafolkken/kit#4828).
211
+ function clear(root: string = PROJECT_ROOT): void {
212
+ const last = read_launch(root)
213
+
214
+ if (last !== undefined) {
215
+ stop_last(last)
216
+ test_pinned_record.clear(last.token, root)
217
+ }
218
+
219
+ stamp_file.remove_stamp(launch_path(root))
220
+ }
221
+
222
+ // The dispatcher's claim in the core ledger (`core-budget.ts`) carries `admitted_at` from the moment
223
+ // the check is granted its cores, before its script even boots, so a claim from this checkout not yet
224
+ // admitted is a check still queued. A check that claimed nothing (a parent held the cores) is not one.
225
+ function is_queued(root: string): boolean {
226
+ return core_budget
227
+ .live_reservations()
228
+ .some(
229
+ ({ reservation }) =>
230
+ reservation.command === PINNED_COMMAND &&
231
+ reservation.checkout === root &&
232
+ reservation.admitted_at === undefined,
233
+ )
234
+ }
235
+
236
+ // A check given up on fails open and is stopped rather than left burning the cores.
237
+ function stopped(launched: Launch, note: string): PinnedSummary {
238
+ stop_current(launched)
239
+
240
+ return test_pinned_record.not_run_summary(new Error(note))
241
+ }
242
+
243
+ async function poll_within_grace(
244
+ root: string,
245
+ launched: Launch,
246
+ now: Clock,
247
+ grace_ms: number = GRACE_MS,
248
+ ): Promise<PinnedSummary | undefined> {
249
+ const started = now()
250
+ let summary = read_answer(root, launched)
251
+
252
+ while (summary === undefined && now() - started < grace_ms) {
253
+ // eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
254
+ await sleep(run_review_steps.POLL_INTERVAL_MS)
255
+ summary = read_answer(root, launched)
256
+ }
257
+
258
+ return summary
259
+ }
260
+
261
+ function given_up(launched: Launch, note: string, is_left_running: boolean): PinnedSummary {
262
+ if (!is_left_running) return stopped(launched, note)
263
+
264
+ return test_pinned_record.not_run_summary(new Error(`${note} — ${STILL_RUNNING_NOTE}`))
265
+ }
266
+
267
+ // The check usually settles while the review is still running; the grace only bounds the join's wait
268
+ // on one that fell far behind the pair while running its tests.
269
+ async function poll_answer(
270
+ root: string,
271
+ launched: Launch,
272
+ now: Clock,
273
+ is_left_running: boolean,
274
+ ): Promise<PinnedSummary> {
275
+ const summary = read_answer(root, launched)
276
+
277
+ if (summary !== undefined) return summary
278
+ if (is_queued(root)) return given_up(launched, QUEUED_NOTE, is_left_running)
279
+ const late = await poll_within_grace(root, launched, now)
280
+
281
+ return late ?? given_up(launched, TIMED_OUT_NOTE, is_left_running)
282
+ }
283
+
284
+ // The `--unpinned-ok` reason the last check was launched with; `undefined` when it carried none.
285
+ function launched_reason(root: string = PROJECT_ROOT): string | undefined {
286
+ return read_launch(root)?.reason
287
+ }
288
+
289
+ // A reason given after the check ended (joshuafolkken/kit#4920) goes on its launch record, so a later
290
+ // restart on a moved tree carries it as a launch's own reason does.
291
+ function carry_reason(reason: string, root: string = PROJECT_ROOT): void {
292
+ const launched = read_launch(root)
293
+
294
+ if (launched !== undefined) stamp_file.write_stamp(launch_path(root), { ...launched, reason })
295
+ }
296
+
297
+ // The tree the last check was launched on; `undefined` when none was, or its tree could not be read.
298
+ function launched_tree(root: string = PROJECT_ROOT): string | undefined {
299
+ return read_launch(root)?.tree
300
+ }
301
+
302
+ // `undefined` when no check was launched. `is_left_running` is false where nothing reads a late answer.
303
+ async function wait_for_answer(
304
+ root: string = PROJECT_ROOT,
305
+ now: Clock = () => Date.now(),
306
+ is_left_running = true,
307
+ ): Promise<PinnedSummary | undefined> {
308
+ const launched = read_launch(root)
309
+
310
+ if (launched === undefined) return undefined
311
+
312
+ return await poll_answer(root, launched, now, is_left_running)
313
+ }
314
+
315
+ // The answer as it stands, waiting nothing and stopping nothing; `undefined` when no check was launched.
316
+ // A check still running answers with `pending_note`, why it was not waited on.
317
+ function answer_now(
318
+ root: string = PROJECT_ROOT,
319
+ pending_note: string = FIXING_NOTE,
320
+ ): PinnedSummary | undefined {
321
+ const launched = read_launch(root)
322
+
323
+ if (launched === undefined) return undefined
324
+
325
+ return read_answer(root, launched) ?? test_pinned_record.not_run_summary(new Error(pending_note))
326
+ }
327
+
328
+ // Whether the last check left its answer, rather than ending without one or never being launched.
329
+ function is_answered(root: string = PROJECT_ROOT): boolean {
330
+ const launched = read_launch(root)
331
+
332
+ return launched !== undefined && test_pinned_record.read(launched.token, root) !== undefined
333
+ }
334
+
335
+ // Whether a check was launched and has no answer yet: still running, rather than done or lost.
336
+ function is_pending(root: string = PROJECT_ROOT): boolean {
337
+ const launched = read_launch(root)
338
+
339
+ return launched !== undefined && read_answer(root, launched) === undefined
340
+ }
341
+
342
+ // Whether the last check's process is proven to be the one launched, as `stop_last` asks before it
343
+ // signals: a stale stamp's reissued or unreadable pid is not waited on.
344
+ function is_live(root: string = PROJECT_ROOT): boolean {
345
+ const launched = read_launch(root)
346
+
347
+ return (
348
+ launched !== undefined &&
349
+ process_identity.is_same_process(launched.pid, launched.start) === true
350
+ )
351
+ }
352
+
353
+ // The lone read's wait: a queued check is waited on, not stopped. `undefined` when no check was launched.
354
+ async function wait_alone(
355
+ root: string = PROJECT_ROOT,
356
+ now: Clock = () => Date.now(),
357
+ ): Promise<PinnedSummary | undefined> {
358
+ const launched = read_launch(root)
359
+
360
+ if (launched === undefined) return undefined
361
+ const summary = await poll_within_grace(root, launched, now, ALONE_GRACE_MS)
362
+
363
+ return summary ?? stopped(launched, ALONE_TIMED_OUT_NOTE)
364
+ }
365
+
366
+ const run_review_pinned_steps = {
367
+ FIXING_NOTE,
368
+ answer_now,
369
+ carry_reason,
370
+ clear,
371
+ is_answered,
372
+ is_live,
373
+ is_pending,
374
+ launch,
375
+ launch_remaining,
376
+ launched_reason,
377
+ launched_tree,
378
+ log_path,
379
+ wait_alone,
380
+ wait_for_answer,
381
+ }
382
+
383
+ export { run_review_pinned_steps }
@@ -0,0 +1,37 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { gate_tree, type GateTree } from '#scripts/gate/gate-tree'
3
+ import { run_record_path } from '#scripts/josh/run-record-path'
4
+
5
+ // The tree a background `josh test:pinned` was launched on, as one digest (joshuafolkken/kit#4834):
6
+ // `josh run:review` writes it on the launch record, and `josh review:record` compares the fixed tree
7
+ // with it, so a round-1 fix that moved the tree starts the check again before the ship's pre-detach
8
+ // reads it. Read from the same changed-file map and base the gate stamps (`gate-tree.ts`).
9
+ //
10
+ // **A reading git could not answer is no key.** An empty map or an unresolved base is not a tree, and
11
+ // two of them would compare equal; the caller treats a missing key as "cannot tell" and starts nothing.
12
+ //
13
+ // **A run's own record is not the tree** (joshuafolkken/kit#5002): an observation line or a `josh
14
+ // metrics` approval written after the launch is read by no test, so it leaves the key — and the
15
+ // launch's answer — as it was (`run-record-path.ts`, the rule the verdict cache reads too).
16
+
17
+ function digest_of({ files, base }: GateTree): string | undefined {
18
+ const entries = Object.entries(files)
19
+
20
+ if (base === undefined || entries.length === 0) return undefined
21
+
22
+ const code = entries
23
+ .filter(([file]) => !run_record_path.is_run_record(file))
24
+ .toSorted(([left], [right]) => left.localeCompare(right))
25
+
26
+ return createHash('sha256')
27
+ .update(JSON.stringify([base, code]))
28
+ .digest('hex')
29
+ }
30
+
31
+ async function key(): Promise<string | undefined> {
32
+ return digest_of(await gate_tree.read_gate_tree())
33
+ }
34
+
35
+ const run_review_pinned_tree = { digest_of, key }
36
+
37
+ export { run_review_pinned_tree }
@@ -0,0 +1,53 @@
1
+ import { test_pinned_logic } from '#scripts/test/test-pinned-logic'
2
+ import type { PinnedSummary } from '#scripts/test/test-pinned-record'
3
+
4
+ // The round-1 check of `josh run:review` (joshuafolkken/kit#4647): `josh test:pinned` over the change,
5
+ // changed hunks and added files' functions alike. A change no test pins is the finding a review used
6
+ // to stop a lane on in round 2, so the run stops on it instead, with each place listed as `file:line`.
7
+ // It runs in the background beside the gate and the brief, and `--join` stops on its answer
8
+ // (joshuafolkken/kit#4770), so its time leaves the critical path.
9
+ //
10
+ // **`--unpinned-ok "<reason>"` carries on past it** for a change no test can pin. That check runs in
11
+ // the background too (joshuafolkken/kit#4793), so the pair never waits on it: the join prints the list
12
+ // and the reason and carries on. **Given after the join stopped, on the same tree and a green gate,
13
+ // the reason judges the finished answer** and re-runs neither the check nor the pair
14
+ // (joshuafolkken/kit#4920); an answer cut at its first unpinned hunk re-runs only the whole check
15
+ // (joshuafolkken/kit#5153). Unchecked places (over the cap, an added file `typescript` could not
16
+ // read, or no Vitest to run a probe) never stop the run.
17
+
18
+ const UNPINNED_OK_FLAG = '--unpinned-ok'
19
+ const OK_ARGUMENT_COUNT = 2
20
+ const STOP_NOTE =
21
+ 'Pin each unpinned place with a test that turns red without it, then re-run `pnpm josh run:review`; for a change no test can pin, leave the tree as it is and pass the reason with `pnpm josh run:review --unpinned-ok "<reason>"` — on this tree and a green gate it judges this answer (or, cut at its first unpinned place, checks the whole change) and re-runs neither the gate nor the pair (`pnpm josh ship --review` forwards it).'
22
+
23
+ type PinnedKind = 'clear' | 'stop' | 'accepted'
24
+
25
+ interface PinnedVerdict {
26
+ kind: PinnedKind
27
+ text: string
28
+ }
29
+
30
+ // `--unpinned-ok` with a non-blank reason; anything else carries none.
31
+ function ok_reason_of(argv: ReadonlyArray<string>): string | undefined {
32
+ const [flag, reason = ''] = argv
33
+ if (flag !== UNPINNED_OK_FLAG || argv.length !== OK_ARGUMENT_COUNT) return undefined
34
+
35
+ return reason.trim() === '' ? undefined : reason
36
+ }
37
+
38
+ // The join's answer on the background check, judged by the reason it was launched with; its note goes
39
+ // above the counts line (`test_pinned_logic.report_text`), so a `tail` keeps what to do.
40
+ function verdict_of(summary: PinnedSummary, reason: string | undefined): PinnedVerdict {
41
+ const { report_text } = test_pinned_logic
42
+
43
+ if (summary.unpinned.length === 0) return { kind: 'clear', text: report_text(summary) }
44
+ if (reason === undefined) return { kind: 'stop', text: report_text(summary, `${STOP_NOTE}\n`) }
45
+ const note = `Unpinned accepted by the implementer: ${reason}\n`
46
+
47
+ return { kind: 'accepted', text: report_text(summary, note) }
48
+ }
49
+
50
+ const run_review_pinned = { STOP_NOTE, UNPINNED_OK_FLAG, ok_reason_of, verdict_of }
51
+
52
+ export type { PinnedVerdict }
53
+ export { run_review_pinned }
@@ -3,7 +3,7 @@ import { gate_skip } from '#scripts/gate/gate-skip'
3
3
  import { gate_tree } from '#scripts/gate/gate-tree'
4
4
  import { git_location_environment } from '#scripts/git/git-location-environment'
5
5
  import { PROJECT_ROOT } from '#scripts/init/init-paths'
6
- import { file_map_stamp } from '#scripts/josh/file-map-stamp'
6
+ import { file_map_stamp, type FileMapStampAccess } from '#scripts/josh/file-map-stamp'
7
7
  import { GATE_COMMAND } from '#scripts/josh/josh-command-types'
8
8
  import { stamp_file } from '#scripts/josh/stamp-file'
9
9
  import { review_stamps } from '#scripts/review/review-stamps'
@@ -12,9 +12,10 @@ import { detached_launch, type LaunchArgv, type LaunchResult } from '#scripts/ru
12
12
  import { run_review, type GateState, type ReviewTiming, type TimingStart } from './run-review'
13
13
 
14
14
  // The side effects `josh run:review` performs, kept out of `run-review.ts` the way `run-merge-steps.ts`
15
- // is kept out of `run-merge.ts`. Three of them: launch the gate detached so
16
- // one command leaves it running in the background, read back the state the join waits on from the
17
- // stamps a real `josh gate` writes, and keep the four timestamps the overlap is measured from.
15
+ // is kept out of `run-merge.ts`. Three of them (the `test:pinned` check's are in
16
+ // `run-review-pinned-steps.ts`): launch the gate detached so one command leaves it running in the
17
+ // background, read back the state the join waits on from the stamps a real `josh gate` writes, and
18
+ // keep the four timestamps the overlap is measured from.
18
19
 
19
20
  // Resolve `josh` through the target checkout's package script. Consumer lanes have no source entry.
20
21
  const PNPM = 'pnpm'
@@ -22,6 +23,10 @@ const DIRECTORY_FLAG = '--dir'
22
23
  const JOSH_SCRIPT = 'josh'
23
24
  const GATE_LOG_PREFIX = 'josh-run-review-gate-log-'
24
25
  const TIMING_PREFIX = 'josh-run-review-timing-'
26
+ const START_TREE_PREFIX = 'josh-run-review-start-tree-'
27
+
28
+ // The tree this pair's gate started on, which tells a moved tree from a red gate at the join.
29
+ const start_tree_stamp: FileMapStampAccess = file_map_stamp.create(START_TREE_PREFIX, PROJECT_ROOT)
25
30
 
26
31
  // The gate is a local, no-network command that finishes in tens of seconds; the review it runs beside
27
32
  // is the longer pole, so by the time the child joins, the gate has almost always already settled. The
@@ -42,25 +47,70 @@ function timing_path(target: string = PROJECT_ROOT): string {
42
47
  // The argv is composed from constants and the validated checkout root, never interpolated into a
43
48
  // shell string — `detached_launch.launch` refuses one carrying anything a command line has no business
44
49
  // holding, and there is no shell on the path to escape from.
45
- function gate_argv(root: string): LaunchArgv {
50
+ function josh_argv(root: string, josh_args: ReadonlyArray<string>): LaunchArgv {
46
51
  return {
47
52
  command: PNPM,
48
- args: [DIRECTORY_FLAG, root, JOSH_SCRIPT, GATE_COMMAND],
53
+ args: [DIRECTORY_FLAG, root, JOSH_SCRIPT, ...josh_args],
49
54
  }
50
55
  }
51
56
 
52
- function launch_gate(root: string = PROJECT_ROOT): LaunchResult {
57
+ // A `josh` command left running in the background, its output kept at `log_path`: the gate, and the
58
+ // `test:pinned` check beside it (`run-review-pinned-steps.ts`).
59
+ function launch_josh(
60
+ josh_args: ReadonlyArray<string>,
61
+ log_path: string,
62
+ root: string = PROJECT_ROOT,
63
+ ): LaunchResult {
53
64
  return detached_launch.launch(
54
65
  {
55
- argv: gate_argv(root),
66
+ argv: josh_argv(root, josh_args),
56
67
  cwd: root,
57
- log_path: gate_log_path(root),
68
+ log_path,
58
69
  env: git_location_environment.location_free_environment(),
59
70
  },
60
71
  (note) => process.stderr.write(`${note}\n`),
61
72
  )
62
73
  }
63
74
 
75
+ function launch_gate(root: string = PROJECT_ROOT): LaunchResult {
76
+ return launch_josh([GATE_COMMAND], gate_log_path(root), root)
77
+ }
78
+
79
+ function to_timing(value: unknown): TimingStart | undefined {
80
+ if (typeof value !== 'object' || value === null) return undefined
81
+
82
+ const { gate_started_at, review_started_at } = value as Record<string, unknown>
83
+
84
+ if (typeof gate_started_at !== 'string' || typeof review_started_at !== 'string') return undefined
85
+
86
+ return { gate_started_at, review_started_at }
87
+ }
88
+
89
+ function read_timing_start(target: string = PROJECT_ROOT): TimingStart | undefined {
90
+ const raw = stamp_file.read_stamp_text(timing_path(target))
91
+
92
+ if (raw === undefined) return undefined
93
+
94
+ try {
95
+ return to_timing(JSON.parse(raw))
96
+ } catch {
97
+ return undefined
98
+ }
99
+ }
100
+
101
+ // **This pair's gate was green on the tree it started on** (joshuafolkken/kit#4774): a green record
102
+ // covering that tree is one the gate either wrote or reused, and a red gate leaves only a record it
103
+ // could not reuse — so none covers it. Read beside a record that no longer covers the current tree, it
104
+ // is the tree that moved after the gate passed — a round-1 reviewer's fix in place — and not a red
105
+ // gate. The record's time is no witness: a gate that reused an earlier green writes no new one.
106
+ function is_green_on_start_tree(base: string | undefined): boolean {
107
+ const start = start_tree_stamp.read()
108
+
109
+ if (start === undefined) return false
110
+
111
+ return gate_skip.reusable_green_gate(start.files, base) !== undefined
112
+ }
113
+
64
114
  // Green wins over the marker: a gate that has recorded a green result on this tree is done however its
65
115
  // in-flight marker reads. `reusable_green_gate` is the gate's own reuse test rather than a second copy
66
116
  // of it, so the join calls a gate green on exactly the trees the gate would skip.
@@ -74,7 +124,7 @@ async function read_gate_state(): Promise<GateState> {
74
124
  const tree = await gate_tree.read_gate_tree()
75
125
  const is_green = gate_skip.reusable_green_gate(tree.files, tree.base) !== undefined
76
126
 
77
- return run_review.gate_state(is_green, is_running)
127
+ return run_review.gate_state(is_green, is_running, is_green_on_start_tree(tree.base))
78
128
  }
79
129
 
80
130
  // The brief has to report the gate as in-flight, or it says `Not verified` and sends the review agent
@@ -82,9 +132,13 @@ async function read_gate_state(): Promise<GateState> {
82
132
  // that marker itself, but its cold start can outrun the brief — so the composite writes the marker up
83
133
  // front, for the tree it is about to review. The gate overwrites it with its own identity the moment it
84
134
  // starts and clears it when it finishes, so this only closes the window between launch and that write;
85
- // the marker carries this process's identity, which is live while the in-process brief reads it.
135
+ // the marker carries this process's identity, which is live while the in-process brief reads it. The
136
+ // same tree is kept as the pair's start tree, which the gate clears no marker of.
86
137
  async function mark_gate_starting(): Promise<void> {
87
- review_stamps.in_flight_stamp.write(await review_tree.read_changed_tree())
138
+ const tree = await review_tree.read_changed_tree()
139
+
140
+ review_stamps.in_flight_stamp.write(tree)
141
+ start_tree_stamp.write(tree)
88
142
  }
89
143
 
90
144
  // Waited on gate finish, for the join. The review is the longer pole, so this usually returns on its
@@ -111,28 +165,6 @@ function write_timing_start(start: TimingStart, target: string = PROJECT_ROOT):
111
165
  stamp_file.write_stamp(timing_path(target), start)
112
166
  }
113
167
 
114
- function to_timing(value: unknown): TimingStart | undefined {
115
- if (typeof value !== 'object' || value === null) return undefined
116
-
117
- const { gate_started_at, review_started_at } = value as Record<string, unknown>
118
-
119
- if (typeof gate_started_at !== 'string' || typeof review_started_at !== 'string') return undefined
120
-
121
- return { gate_started_at, review_started_at }
122
- }
123
-
124
- function read_timing_start(target: string = PROJECT_ROOT): TimingStart | undefined {
125
- const raw = stamp_file.read_stamp_text(timing_path(target))
126
-
127
- if (raw === undefined) return undefined
128
-
129
- try {
130
- return to_timing(JSON.parse(raw))
131
- } catch {
132
- return undefined
133
- }
134
- }
135
-
136
168
  // The two ends are stamped at the join: the review ended when the child returned from the subagent and
137
169
  // called this, and the gate ended at the green stamp's time where it passed, or now where it did not.
138
170
  function complete_timing(
@@ -147,13 +179,17 @@ function complete_timing(
147
179
  }
148
180
 
149
181
  const run_review_steps = {
182
+ MAX_JOIN_WAIT_MS,
183
+ POLL_INTERVAL_MS,
150
184
  complete_timing,
151
185
  gate_log_path,
152
186
  launch_gate,
187
+ launch_josh,
153
188
  mark_gate_starting,
154
189
  read_gate_log,
155
190
  read_gate_state,
156
191
  read_timing_start,
192
+ start_tree_stamp,
157
193
  timing_path,
158
194
  wait_for_gate_finish,
159
195
  write_timing_start,