@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,18 +1,25 @@
1
1
  import { existsSync } from 'node:fs'
2
- import { readFile, writeFile } from 'node:fs/promises'
3
- import path from 'node:path'
4
- import { base_tree, type BaseTree } from '#scripts/git/base-tree'
2
+ import { readFile } from 'node:fs/promises'
5
3
  import { change_base } from '#scripts/git/change-base'
6
4
  import { changed_paths } from '#scripts/git/changed-paths'
7
5
  import { git_spawn } from '#scripts/git/git-spawn'
6
+ import { review_code_tree } from '#scripts/review/review-code-tree'
7
+ import { test_pinned_cache, type LoadedCache, type Reached } from './test-pinned-cache'
8
+ import { test_pinned_cache_logic, type Known, type Recheck } from './test-pinned-cache-logic'
9
+ import { test_pinned_emit } from './test-pinned-emit'
10
+ import { test_pinned_functions } from './test-pinned-functions'
11
+ import type { PinnedOrigin } from './test-pinned-ledger'
8
12
  import {
9
13
  test_pinned_logic,
10
14
  type Hunk,
15
+ type HunkOutcome,
11
16
  type PinnedReport,
12
- type UnpinnedHunk,
13
17
  } from './test-pinned-logic'
18
+ import { test_pinned_probe, type CheckOptions, type Probe } from './test-pinned-probe'
19
+ import { test_pinned_reach, type Reach } from './test-pinned-reach'
20
+ import type { Staged } from './test-pinned-stage'
14
21
  import { test_red } from './test-red'
15
- import type { RedVerdict } from './test-red-logic'
22
+ import type { RedProbe } from './test-red-logic'
16
23
 
17
24
  // `josh test:pinned` (joshuafolkken/kit#4516) — put each changed hunk of a non-test code file back to
18
25
  // the merge-base's lines, one at a time, run that file's related tests, and list the hunks no test went
@@ -22,12 +29,48 @@ import type { RedVerdict } from './test-red-logic'
22
29
  // The changed files are copied in from the working tree first, so what is reverted is the change as it
23
30
  // stands, committed or not, and each hunk's file is restored before the next one runs.
24
31
 
25
- const TREE_PREFIX = 'josh-test-pinned-'
26
32
  const HEAD = 'HEAD'
27
33
 
34
+ interface StubRead {
35
+ hunks: ReadonlyArray<Hunk>
36
+ unparsed: ReadonlyArray<string>
37
+ }
38
+
39
+ // `rewired` counts the changed hunks left out as a rewire (`test-pinned-rewire.ts`).
40
+ interface ProbeRead extends StubRead {
41
+ rewired: number
42
+ }
43
+
44
+ interface RunOptions {
45
+ max_hunks: number
46
+ is_cached_only: boolean
47
+ is_first_unpinned?: boolean
48
+ rewired?: number
49
+ origin?: PinnedOrigin | undefined
50
+ }
51
+
52
+ // One run's probing: the checked hunks, what the cache knows of each, and the cache they are written to.
53
+ interface ProbeRun {
54
+ hunks: ReadonlyArray<Hunk>
55
+ known: ReadonlyArray<Known>
56
+ cache: LoadedCache
57
+ keys: ReadonlyArray<string>
58
+ options: RunOptions
59
+ }
60
+
61
+ // Each hunk's verdict, and how many of the fresh probes ran in stages.
62
+ interface Verdicts {
63
+ verdicts: Array<RedProbe | undefined>
64
+ staged: Staged
65
+ }
66
+
67
+ const NO_OUTCOME: HunkOutcome = { unpinned: [], stopped: [] }
68
+ const NO_REPORT = { ...NO_OUTCOME, unchecked: [], unparsed: [] } as const
69
+
28
70
  // `--diff-filter=M`: an added file's one hunk is the whole file, and reverting it is a missing module
29
- // every importer fails on, which says nothing about the tests. The prefixes are pinned: a user's
30
- // `diff.mnemonicPrefix` writes `+++ w/<file>`, which would read as no hunk at all.
71
+ // every importer fails on, which says nothing about the tests — its functions are probed instead
72
+ // (`stub_hunks_of`). The prefixes are pinned: a user's `diff.mnemonicPrefix` writes `+++ w/<file>`,
73
+ // which would read as no hunk at all.
31
74
  async function read_hunks(base: string, files: ReadonlyArray<string>): Promise<Array<Hunk>> {
32
75
  if (files.length === 0) return []
33
76
  const diff_args = ['diff', '-U0', '--no-color', '--no-ext-diff', '--diff-filter=M']
@@ -38,63 +81,231 @@ async function read_hunks(base: string, files: ReadonlyArray<string>): Promise<A
38
81
  )
39
82
  }
40
83
 
41
- // The verdict with this one hunk reverted. The file is put back before the next hunk runs, or that
42
- // hunk's tests would also see this revert.
43
- async function verdict_of(tree: BaseTree, hunk: Hunk): Promise<RedVerdict> {
44
- const target = path.join(tree.tree, hunk.file)
45
- const content = await readFile(target, 'utf8')
84
+ // The key of each hunk, read off the working file its lines sit in now.
85
+ async function keys_of(hunks: ReadonlyArray<Hunk>): Promise<Array<string>> {
86
+ const contents = await Promise.all(hunks.map(async (hunk) => await readFile(hunk.file, 'utf8')))
87
+
88
+ return test_pinned_cache_logic.distinct(
89
+ hunks.map((hunk, index) => test_pinned_cache_logic.hunk_key(hunk, contents[index] ?? '')),
90
+ )
91
+ }
92
+
93
+ // The verdict the cache already holds; one left to recheck holds none yet.
94
+ function held_of(known: Known): RedProbe | undefined {
95
+ return test_pinned_cache_logic.is_recheck(known) ? undefined : known
96
+ }
97
+
98
+ function recheck_of(known: Known): Recheck | undefined {
99
+ return test_pinned_cache_logic.is_recheck(known) ? known : undefined
100
+ }
101
+
102
+ // The hunks no held verdict covers, each with its recheck where the cache named one.
103
+ function probes_of(hunks: ReadonlyArray<Hunk>, known: ReadonlyArray<Known>): Array<Probe> {
104
+ return hunks
105
+ .map((hunk, index) => ({ hunk, index, recheck: recheck_of(known[index]) }))
106
+ .filter((probe) => held_of(known[probe.index]) === undefined)
107
+ }
108
+
109
+ // The probes in the order they run: the fewest tests reached first (`test_pinned_logic.by_reach`),
110
+ // each file's count read once and carried on its probes, which a wide one runs in stages by
111
+ // (`test-pinned-stage.ts`); a file with no graph to read counts as 0.
112
+ async function by_reach(probes: ReadonlyArray<Probe>, reach: Reach): Promise<Array<Probe>> {
113
+ const files = test_pinned_cache_logic.distinct(probes.map((probe) => probe.hunk.file))
114
+ const related = await Promise.all(files.map(async (file) => await reach.related(file)))
115
+ const counts = new Map(files.map((file, index) => [file, related[index]?.length ?? 0]))
116
+ const counted = probes.map((probe) => ({ ...probe, reach: counts.get(probe.hunk.file) }))
117
+
118
+ return test_pinned_logic.by_reach(counted, counts)
119
+ }
46
120
 
47
- await writeFile(target, test_pinned_logic.revert(content, hunk))
121
+ function reached_of(verdicts: ReadonlyArray<RedProbe | undefined>): Array<Reached> {
122
+ return verdicts.flatMap((verdict, index) => (verdict === undefined ? [] : [[index, verdict]]))
123
+ }
48
124
 
49
- try {
50
- return await test_red.verdict_in(tree, ['related', hunk.file, '--run'])
51
- } finally {
52
- await writeFile(target, content)
125
+ // The fresh run's options: the hunks the cache held, then the run's own.
126
+ function check_options(held: number, options: RunOptions): CheckOptions {
127
+ return {
128
+ held,
129
+ rewired: options.rewired ?? 0,
130
+ is_first_unpinned: options.is_first_unpinned === true,
131
+ origin: options.origin,
53
132
  }
54
133
  }
55
134
 
56
- // One hunk at a time: two reverts in flight would share the tree, and each run is already a whole vitest.
57
- // Green and no-test both leave a hunk unpinned: either way nothing went red when it was reverted.
58
- async function unpinned_in(
59
- tree: BaseTree,
135
+ // Each hunk's verdict: the cached one where it still holds, else a fresh run's, in the hunks' order.
136
+ // The held ones are written first and each fresh one as it is reached (`test_pinned_cache.writer`).
137
+ // Under `is_first_unpinned` a hunk the run stopped before keeps no verdict.
138
+ async function verdicts_on(paths: ReadonlyArray<string>, probing: ProbeRun): Promise<Verdicts> {
139
+ const verdicts = probing.known.map((entry) => held_of(entry))
140
+ const reached = reached_of(verdicts)
141
+ const probes = await by_reach(probes_of(probing.hunks, probing.known), probing.cache.reach)
142
+ const writer = test_pinned_cache.writer(probing.cache, probing.keys)
143
+ const options = check_options(reached.length, probing.options)
144
+
145
+ await writer.record(reached)
146
+ const checked = await test_pinned_probe.checked_on(
147
+ paths,
148
+ probes,
149
+ async (index, verdict) => {
150
+ verdicts[index] = verdict
151
+ await writer.record([[index, verdict]])
152
+ },
153
+ options,
154
+ )
155
+
156
+ return { verdicts, staged: checked.staged }
157
+ }
158
+
159
+ function outcome_of(
60
160
  hunks: ReadonlyArray<Hunk>,
61
- ): Promise<Array<UnpinnedHunk>> {
62
- const unpinned: Array<UnpinnedHunk> = []
161
+ verdicts: ReadonlyArray<RedProbe | undefined>,
162
+ ): HunkOutcome {
163
+ const is_held = verdicts.every((verdict) => verdict !== undefined)
63
164
 
64
- for (const hunk of hunks) {
65
- // eslint-disable-next-line no-await-in-loop -- the hunks share one tree, so each revert waits for the last restore
66
- const verdict = await verdict_of(tree, hunk)
67
- if (verdict !== 'red') unpinned.push({ ...hunk, is_green: verdict === 'green' })
68
- }
165
+ return { ...test_pinned_logic.outcome(hunks, verdicts), is_held }
166
+ }
69
167
 
70
- return unpinned
168
+ // The hunks a run probed and stopped before (`is_first_unpinned`); none, no `cut` key.
169
+ function cut_of(
170
+ hunks: ReadonlyArray<Hunk>,
171
+ verdicts: ReadonlyArray<RedProbe | undefined>,
172
+ ): Pick<HunkOutcome, 'cut'> {
173
+ const cut = hunks.filter((_hunk, index) => verdicts[index] === undefined)
174
+
175
+ return cut.length === 0 ? {} : { cut }
71
176
  }
72
177
 
73
- async function unpinned_on(
178
+ // A fresh cached verdict is reused without a run (`test-pinned-cache.ts`); every hunk's verdict is
179
+ // stored again, so a rerun on the same tree starts no vitest. `is_cached_only` runs no probe: only the
180
+ // verdicts the cache holds are read, and nothing is written.
181
+ async function outcome_on(
182
+ base: string,
74
183
  paths: ReadonlyArray<string>,
75
184
  hunks: ReadonlyArray<Hunk>,
76
- ): Promise<Array<UnpinnedHunk>> {
77
- if (hunks.length === 0) return []
185
+ options: RunOptions,
186
+ ): Promise<HunkOutcome> {
187
+ if (hunks.length === 0) return NO_OUTCOME
188
+ const cache = await test_pinned_cache.load(base, hunks)
189
+ const keys = await keys_of(hunks)
190
+ const known = await test_pinned_cache.known(
191
+ cache,
192
+ keys,
193
+ hunks.map((hunk) => hunk.file),
194
+ )
195
+
196
+ if (options.is_cached_only) {
197
+ return outcome_of(
198
+ hunks,
199
+ known.map((entry) => held_of(entry)),
200
+ )
201
+ }
202
+
203
+ const { verdicts, staged } = await verdicts_on(paths, { hunks, known, cache, keys, options })
204
+
205
+ return { ...outcome_of(hunks, verdicts), ...cut_of(hunks, verdicts), staged }
206
+ }
207
+
208
+ // A target the base's tree does not hold is added — committed on the branch or still untracked.
209
+ async function added_scripts(base: string, targets: ReadonlyArray<string>): Promise<Array<string>> {
210
+ const held = new Set(await changed_paths.read_held_at(base, targets))
78
211
 
79
- return await base_tree.with_tree(TREE_PREFIX, HEAD, async (tree) => {
80
- await base_tree.copy_in(tree.tree, paths)
212
+ return targets.filter((file) => !held.has(file) && test_pinned_functions.is_script(file))
213
+ }
214
+
215
+ // Without `typescript` (an optional peer in a consumer) no function can be read, so every added file
216
+ // is reported unchecked rather than passed as pinned.
217
+ async function stub_hunks_of(added: ReadonlyArray<string>): Promise<StubRead> {
218
+ const api = review_code_tree.load_typescript(process.cwd())
219
+ if (api === undefined) return { hunks: [], unparsed: added }
220
+ const contents = await Promise.all(added.map(async (file) => await readFile(file, 'utf8')))
221
+ const hunks = added.flatMap((file, index) =>
222
+ test_pinned_functions.stub_hunks(api, file, contents[index] ?? ''),
223
+ )
224
+
225
+ return { hunks, unparsed: [] }
226
+ }
227
+
228
+ // A changed hunk whose revert emits the same JavaScript, or a rewire, is dropped before the cap
229
+ // (`test-pinned-emit.ts`), so neither is `unchecked`; without `typescript` every hunk is kept, so the
230
+ // run checks more rather than less.
231
+ async function runtime_hunks(hunks: ReadonlyArray<Hunk>): Promise<ProbeRead> {
232
+ const api = review_code_tree.load_typescript(process.cwd())
233
+ if (api === undefined) return { hunks: [...hunks], unparsed: [], rewired: 0 }
234
+ const contents = await Promise.all(hunks.map(async (hunk) => await readFile(hunk.file, 'utf8')))
235
+
236
+ return { ...test_pinned_emit.probed_of(api, hunks, contents), unparsed: [] }
237
+ }
238
+
239
+ async function read_probes(base: string, targets: ReadonlyArray<string>): Promise<ProbeRead> {
240
+ const stubs = await stub_hunks_of(await added_scripts(base, targets))
241
+ const changed = await runtime_hunks(await read_hunks(base, targets))
242
+ const hunks = [...changed.hunks, ...stubs.hunks]
243
+
244
+ return { hunks, unparsed: stubs.unparsed, rewired: changed.rewired }
245
+ }
246
+
247
+ // The work tree's files that differ from `commit`, deleted ones dropped. Read since `HEAD` they are
248
+ // what the worktree at `HEAD` needs copied in to hold the work tree — a conflicted merge's incoming
249
+ // files included; read since the change base they are this branch's own change.
250
+ async function existing_since(commit: string): Promise<Array<string>> {
251
+ const changed = await changed_paths.read_changed_since(commit)
252
+
253
+ return changed.filter((file) => existsSync(file))
254
+ }
255
+
256
+ async function report_on(
257
+ base: string,
258
+ options: RunOptions,
259
+ has_runner: boolean,
260
+ ): Promise<PinnedReport> {
261
+ const { max_hunks } = options
262
+ const paths = await existing_since(HEAD)
263
+ const changed = await existing_since(base)
264
+ const targets = await test_pinned_reach.probed_files(
265
+ changed.filter((file) => test_pinned_logic.is_target(file)),
266
+ )
267
+ const probes = await read_probes(base, targets)
268
+ const { checked, unchecked } = test_pinned_logic.split(probes.hunks, has_runner ? max_hunks : 0)
269
+ const outcome = await outcome_on(base, paths, checked, { ...options, rewired: probes.rewired })
270
+
271
+ return { ...outcome, unchecked, unparsed: probes.unparsed, max_hunks, has_runner }
272
+ }
273
+
274
+ async function report(options: RunOptions): Promise<PinnedReport> {
275
+ const base = await change_base.resolved_through_merge()
276
+ const has_runner = test_red.has_runner()
277
+ if (base === undefined) return { ...NO_REPORT, max_hunks: options.max_hunks, has_runner }
278
+
279
+ return await report_on(base, options, has_runner)
280
+ }
281
+
282
+ // No merge-base (not a repository, no default branch) checks nothing, as `josh test:red` does. The cap
283
+ // is shared: changed hunks first, then the added files' functions. Without Vitest (a consumer that has
284
+ // none) no probe runs, so every one is unchecked rather than unpinned. `is_first_unpinned` starts no
285
+ // probe once one is unpinned (joshuafolkken/kit#5153): the hunks left are reported `cut`. `origin` is
286
+ // what started the run, for its ledger row.
287
+ async function run(
288
+ max_hunks: number,
289
+ is_first_unpinned = false,
290
+ origin?: PinnedOrigin,
291
+ ): Promise<PinnedReport> {
292
+ return await report({ max_hunks, is_cached_only: false, is_first_unpinned, origin })
293
+ }
81
294
 
82
- return await unpinned_in(tree, hunks)
83
- })
295
+ // The unpinned hunks the cache already answers for this tree, probing nothing (joshuafolkken/kit#4936):
296
+ // what a check still running — or one stopped mid-way — has reached for the hunks that did not move.
297
+ async function cached(max_hunks: number): Promise<PinnedReport> {
298
+ return await report({ max_hunks, is_cached_only: true })
84
299
  }
85
300
 
86
- // No merge-base (not a repository, no default branch) checks nothing, as `josh test:red` does.
87
- async function run(max_hunks: number): Promise<PinnedReport> {
88
- const base = await change_base.resolved()
89
- const changed = await changed_paths.read_changed_paths(false)
90
- const paths = changed.filter((file) => existsSync(file))
91
- const targets = paths.filter((file) => test_pinned_logic.is_target(file))
92
- const hunks = base === undefined ? [] : await read_hunks(base, targets)
93
- const { checked, unchecked } = test_pinned_logic.split(hunks, max_hunks)
301
+ // The cached report when the cache holds every checked hunk's verdict for this tree, probing nothing
302
+ // (joshuafolkken/kit#5078); `undefined` when a hunk moved or was never probed, so the caller probes.
303
+ async function held_report(max_hunks: number): Promise<PinnedReport | undefined> {
304
+ const read = await cached(max_hunks)
94
305
 
95
- return { unpinned: await unpinned_on(paths, checked), unchecked, max_hunks }
306
+ return read.is_held === true ? read : undefined
96
307
  }
97
308
 
98
- const test_pinned = { run }
309
+ const test_pinned = { cached, held_report, run }
99
310
 
100
311
  export { test_pinned }
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env tsx
2
+ import { existsSync, statSync } from 'node:fs'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { test_pinned_reach } from './test-pinned-reach'
5
+
6
+ // `josh test:reach <file...>` — how many test files each file's imports reach, the count
7
+ // `josh test:related` sizes its core claim by, one `<count> <file>` line per file
8
+ // (joshuafolkken/kit#5238): an overrun filing or a baseline measures it without a hand-written probe.
9
+
10
+ const ARGV_OFFSET = 2
11
+ const FAILURE_EXIT_CODE = 1
12
+ const USAGE = 'Usage: josh test:reach <file...>'
13
+ const NO_GRAPH =
14
+ 'josh test:reach: no import graph can be built here (no typescript or tsconfig.json)'
15
+
16
+ function is_file(path: string): boolean {
17
+ return existsSync(path) && statSync(path).isFile()
18
+ }
19
+
20
+ // A path no file answers — absent, or a directory — reaches no test, so its `0` would read as a
21
+ // measured count: `pnpm josh` runs from the repository root, where a path typed relative to a
22
+ // subdirectory names nothing.
23
+ function refusal(argv: ReadonlyArray<string>): string | undefined {
24
+ if (argv.length === 0) return USAGE
25
+ const missing = argv.filter((file) => !is_file(file))
26
+ if (missing.length === 0) return undefined
27
+
28
+ return `josh test:reach: no such file from the repository root: ${missing.join(', ')}`
29
+ }
30
+
31
+ async function run(argv: ReadonlyArray<string>): Promise<number> {
32
+ const refused = refusal(argv)
33
+
34
+ if (refused !== undefined) {
35
+ console.error(refused)
36
+
37
+ return FAILURE_EXIT_CODE
38
+ }
39
+
40
+ const counts = await test_pinned_reach.reached_test_counts(argv)
41
+
42
+ if (counts === undefined) {
43
+ console.error(NO_GRAPH)
44
+
45
+ return FAILURE_EXIT_CODE
46
+ }
47
+
48
+ console.info(counts.map(({ file, count }) => `${String(count)} ${file}`).join('\n'))
49
+
50
+ return 0
51
+ }
52
+
53
+ async function main(argv: ReadonlyArray<string>): Promise<void> {
54
+ process.exitCode = await run(argv)
55
+ }
56
+
57
+ const test_reach_cli = { USAGE, run }
58
+
59
+ if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
60
+
61
+ export { test_reach_cli }
@@ -1,6 +1,7 @@
1
1
  import { readdirSync, readFileSync, rmSync, statSync } from 'node:fs'
2
2
  import path from 'node:path'
3
3
  import { git_common_directory } from '#scripts/git/git-common-directory'
4
+ import { run_record_path } from '#scripts/josh/run-record-path'
4
5
  import { atomic_write } from '#scripts/lib/atomic-write'
5
6
  import { json_value } from '#scripts/lib/json-value'
6
7
  import { test_read_trace, type ReadRecords } from './test-read-trace'
@@ -24,6 +25,10 @@ interface ReadMap {
24
25
  // content then (`input_fingerprint.of_files`), so one whose content has not moved since does not count as changed
25
26
  // (joshuafolkken/kit#4425).
26
27
  dirty: Record<string, string>
28
+ // The recording checkout's change as a test reads it through git — the branch diff from the change
29
+ // base plus the untracked files (`changed_paths.read_changed_paths`), committed or not
30
+ // (joshuafolkken/kit#4734).
31
+ change: Array<string>
27
32
  // The directories `commit` holds, so a directory new since then is told apart from one no test
28
33
  // read — the map's own keys cannot say which, since an unread directory has none.
29
34
  directories: Array<string>
@@ -32,10 +37,11 @@ interface ReadMap {
32
37
 
33
38
  // Bumped whenever the shape, what a reader means or how `dirty` is digested changes, so a map an older
34
39
  // kit wrote reads as no map at all and the next full run writes it afresh. 6: `dirty` is
35
- // `input-fingerprint.ts`'s digest of every changed file (joshuafolkken/kit#4497).
36
- const MAP_VERSION = 6
40
+ // `input-fingerprint.ts`'s digest of every changed file (joshuafolkken/kit#4497). 7: `change`
41
+ // (joshuafolkken/kit#4734). 8: a module is a reader only of what its evaluation read
42
+ // (joshuafolkken/kit#5033).
43
+ const MAP_VERSION = 8
37
44
 
38
- const MAP_DIRECTORY = 'josh'
39
45
  const MAP_FILE_STEM = 'test-read-map'
40
46
  const RECORD_EXTENSION = '.json'
41
47
 
@@ -84,6 +90,7 @@ const FIELD_CHECKS: ReadonlyArray<[keyof ReadMap, (field: unknown) => boolean]>
84
90
  ['version', (field) => field === MAP_VERSION],
85
91
  ['commit', (field) => typeof field === 'string'],
86
92
  ['dirty', is_digest_record],
93
+ ['change', is_string_array],
87
94
  ['directories', is_string_array],
88
95
  ['readers', is_read_records],
89
96
  ]
@@ -148,7 +155,13 @@ function created_directories(
148
155
  // it. A directory new since the recording is also a new entry in the listing above it and a probe of
149
156
  // it that now answers differently; a directory that already existed reads the same to both, whatever
150
157
  // changed deeper down.
158
+ //
159
+ // **A run record reaches only a test that read it by name** (joshuafolkken/kit#5001): a test listing or
160
+ // walking the root or `.josh/` reads the checkout around it, not the observation or accepted metrics
161
+ // a lane writes after its checks went green, so those keys would run every such test for bookkeeping.
151
162
  function lookup_keys(relative: string, recorded: ReadonlySet<string>): Array<string> {
163
+ if (run_record_path.is_run_record(relative)) return [relative]
164
+
152
165
  const directories = directories_above(relative)
153
166
  const created = created_directories(directories, recorded)
154
167
  const listed = [
@@ -170,23 +183,34 @@ function readers_of(map: ReadMap, relative: string): Array<string> {
170
183
  return [...new Set(keys.flatMap((key) => map.readers[key] ?? []))]
171
184
  }
172
185
 
173
- // **A directory the recorded tree held, inside which no recorded test listed, walked or read any
174
- // entry** (joshuafolkken/kit#4303). A file added to it since is one no test reads, as surely as one
175
- // the tree already held there: a test that builds a path into it reads some file there, and one that
176
- // enumerates it lists it, both of which leave a key whose directory is this one.
186
+ // **A directory inside which no recorded test listed, walked or read any entry** (joshuafolkken/kit#4303).
187
+ // A file added to it since is one no test reads, as surely as one the tree already held there: a test
188
+ // that builds a path into it reads some file there, and one that enumerates it lists it, both of which
189
+ // leave a key whose directory is this one — a read is recorded before it runs, so a path absent then
190
+ // still leaves its key. **A directory new since the recording counts too** (joshuafolkken/kit#4597):
191
+ // a probe of it or a listing of its parent is one of `readers_of`'s keys, which the caller asks first.
192
+ // **A read of the recording tree's own change does not count** (joshuafolkken/kit#4734): a test that
193
+ // reads this checkout's change through git reads whatever file the recording lane held, so its own
194
+ // `.josh/metrics-accepted/<N>.json` marked that directory read and sent the next lane's to the whole
195
+ // suite. A listing or a walk of the directory, or a read of any other file there, still counts — except
196
+ // for a run record, which only a read of itself reaches (`lookup_keys`).
177
197
  function in_unread_directory(map: ReadMap, relative: string): boolean {
178
- const directory = path.dirname(relative)
198
+ if (run_record_path.is_run_record(relative)) return true
179
199
 
180
- if (!map.directories.includes(directory)) return false
200
+ const directory = path.dirname(relative)
201
+ const change = new Set(map.change)
202
+ const keys = Object.keys(map.readers).filter((key) => !change.has(key))
181
203
 
182
- return Object.keys(map.readers).every((key) => path.dirname(key) !== directory)
204
+ return keys.every((key) => path.dirname(key) !== directory)
183
205
  }
184
206
 
185
207
  function map_path(root: string, version: number = MAP_VERSION): string | undefined {
186
208
  const common = git_common_directory.repository(root)
187
209
  const name = `${MAP_FILE_STEM}.v${String(version)}${RECORD_EXTENSION}`
188
210
 
189
- return common === undefined ? undefined : path.join(common, MAP_DIRECTORY, name)
211
+ return common === undefined
212
+ ? undefined
213
+ : path.join(common, git_common_directory.STATE_DIRECTORY, name)
190
214
  }
191
215
 
192
216
  // Every version's map, the unversioned one kits before joshuafolkken/kit#4423 wrote included; a
@@ -36,16 +36,28 @@ async function commit_directories(commit: string): Promise<Array<string>> {
36
36
  return output.split('\0').filter((listed) => listed.length > 0)
37
37
  }
38
38
 
39
- type TreeSnapshot = Pick<ReadMap, 'commit' | 'dirty' | 'directories'>
39
+ type TreeSnapshot = Pick<ReadMap, 'commit' | 'dirty' | 'change' | 'directories'>
40
40
 
41
- // The tree the run read: the commit, every file that differed from it with its content's digest, and
42
- // the directories it holds. **Read before the run starts**: a digest taken after it would vouch for
43
- // an edit made while the tests ran as content they read.
41
+ // A checkout with no change base (no default-branch ref) still records its map, with no change to
42
+ // discount: every read then counts, which narrows less, never wrongly.
43
+ async function own_change(): Promise<Array<string>> {
44
+ try {
45
+ return await changed_paths.read_changed_paths(false)
46
+ } catch {
47
+ return []
48
+ }
49
+ }
50
+
51
+ // The tree the run read: the commit, every file that differed from it with its content's digest, the
52
+ // checkout's change from its change base, and the directories it holds. **Read before the run
53
+ // starts**: a digest taken after it would vouch for an edit made while the tests ran as content they
54
+ // read.
44
55
  async function tree_snapshot(root: string): Promise<TreeSnapshot> {
45
56
  const commit = await git_command.head_commit()
46
57
  const dirty = input_fingerprint.of_files(root, await changed_paths.read_changed_since(commit))
58
+ const change = await own_change()
47
59
 
48
- return { commit, dirty, directories: await commit_directories(commit) }
60
+ return { commit, dirty, change, directories: await commit_directories(commit) }
49
61
  }
50
62
 
51
63
  // A map that cannot be written — no git, a read-only common directory — leaves the previous map in
@@ -12,6 +12,15 @@ import { fileURLToPath } from 'node:url'
12
12
  // makes has the test outermost, so a shared helper it reads through — every test importing it — is
13
13
  // never the reader (joshuafolkken/kit#4417). The test file is recorded beside it, because an async
14
14
  // callback (a directory walk inside a dependency) leaves no repository frame at all.
15
+ //
16
+ // **Only a module's evaluation makes a module the reader** (joshuafolkken/kit#5033). A read a named
17
+ // function makes after an `await` has that function outermost, since the continuation drops the test
18
+ // that called it: `import-graph.ts` read some 3,700 paths that way, and `vitest related` widened each
19
+ // to every test importing it. Such a read belongs to the test running it, recorded beside, so an
20
+ // outermost frame with a function name records no module. A module's top-level code runs with no
21
+ // function name; so does an anonymous callback, which keeps the widening — the safe side. A top-level
22
+ // `await` of a named function that reads after an `await` of its own narrows to the first importing
23
+ // test; no module does that today, and CI's whole suite would catch the miss.
15
24
 
16
25
  type ReadRecords = Record<string, Array<string>>
17
26
 
@@ -95,16 +104,23 @@ function read_target(argument: unknown, cwd: string): string | undefined {
95
104
  return undefined
96
105
  }
97
106
 
107
+ // One call site: its script name, and the name of the function running there — empty for a module's
108
+ // top-level code or an anonymous callback.
109
+ interface CallFrame {
110
+ script_name: string
111
+ function_name: string
112
+ }
113
+
98
114
  interface ReaderQuery {
99
115
  root: string
100
- // The script name of every call site, innermost first.
101
- scripts: ReadonlyArray<string>
116
+ // Every call site, innermost first.
117
+ frames: ReadonlyArray<CallFrame>
102
118
  // The recorder's own file, which is always the top frame and never the reader.
103
119
  recorder: string
104
120
  }
105
121
 
106
- function frame_reader(query: ReaderQuery, script_name: string): string | undefined {
107
- const file_path = script_path(script_name)
122
+ function frame_reader(query: ReaderQuery, frame: CallFrame): string | undefined {
123
+ const file_path = script_path(frame.script_name)
108
124
 
109
125
  if (file_path === undefined || file_path === query.recorder) return undefined
110
126
 
@@ -114,9 +130,11 @@ function frame_reader(query: ReaderQuery, script_name: string): string | undefin
114
130
  }
115
131
 
116
132
  function stack_reader(query: ReaderQuery): string | undefined {
117
- return query.scripts
118
- .map((script_name) => frame_reader(query, script_name))
119
- .findLast((reader) => reader !== undefined)
133
+ const outermost = query.frames.findLast((frame) => frame_reader(query, frame) !== undefined)
134
+
135
+ if (outermost?.function_name !== '') return undefined
136
+
137
+ return frame_reader(query, outermost)
120
138
  }
121
139
 
122
140
  function add_reader(records: Map<string, Set<string>>, read_path: string, reader: string): void {
@@ -144,5 +162,5 @@ const test_read_trace = {
144
162
  to_records,
145
163
  }
146
164
 
147
- export type { ReadKind, ReaderQuery, ReadRecords }
165
+ export type { CallFrame, ReadKind, ReaderQuery, ReadRecords }
148
166
  export { test_read_trace }