@joshuafolkken/kit 1.1784.0 → 1.2116.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 +60 -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 +386 -360
  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 +16 -9
  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,111 @@
1
+ import { stripVTControlCharacters } from 'node:util'
2
+ import { gate_log } from '#scripts/gate/gate-log'
3
+ import { verification_tail } from '#scripts/gate/verification-tail'
4
+ import { PROJECT_ROOT } from '#scripts/init/init-paths'
5
+ import { stamp_file } from '#scripts/josh/stamp-file'
6
+
7
+ // A failed vitest run's answer, put where a `tail` reaches it (joshuafolkken/kit#4869).
8
+ //
9
+ // **The console's end is the last failure's stack, not the list of what failed.** vitest prints its
10
+ // failures in order and then a summary of counts, so `josh test:related 2>&1 | tail -40` kept one stack
11
+ // and no names, and the only way to learn the rest was to run the suite again. So every run's output
12
+ // goes to a file, and a failed one ends with the failed tests' names and that file's path — the same
13
+ // log and notice `josh gate` uses, imported rather than re-derived.
14
+
15
+ const UNIT_LOG_PREFIX = 'josh-unit-log-'
16
+ const UNIT_LOG_HEADER = 'vitest'
17
+ const FAILED_LINE = /^FAIL\s+(?<name>\S.*)$/u
18
+ const FAILED_HEADING = 'failed tests:'
19
+ const NONE_NAMED = ' (none named in the output — read the full output)'
20
+ const NONE_NAMED_VERDICT = 'none named'
21
+ // A name is `<file> > <suite> > <test>` or `<file> [ <file> ]`, prefixed `|<project>| ` in a workspace —
22
+ // or, with color on, ` <project> ` once its badge's escapes are stripped (joshuafolkken/kit#5171).
23
+ const FAILED_FILE = /^(?:\|[^|]*\|\s+|\S+\s{2,})?(?<file>\S+)/u
24
+ // The guarded unit run, filtered to those files: it reserves its cores and ends a failure with this
25
+ // same summary (joshuafolkken/kit#5023).
26
+ const RERUN_PREFIX = 'rerun failed: pnpm josh test:unit '
27
+
28
+ // One path per checkout and vitest mode, overwritten by each run, beside the gate's own log. **The
29
+ // mode is in the name** because the gate's whole-suite `run` and a `related` run started beside it
30
+ // overlap, and one shared file would hand the failed run's reader the other run's output.
31
+ function unit_log_path(vitest_arguments: ReadonlyArray<string>): string {
32
+ const prefix = `${UNIT_LOG_PREFIX}${vitest_arguments[0] ?? UNIT_LOG_HEADER}-`
33
+
34
+ return stamp_file.stamp_path(prefix, PROJECT_ROOT, gate_log.GATE_LOG_SUFFIX)
35
+ }
36
+
37
+ function failed_name(line: string): string | undefined {
38
+ return FAILED_LINE.exec(stripVTControlCharacters(line).trim())?.groups?.['name']
39
+ }
40
+
41
+ // vitest names each failed test on a ` FAIL <file> > <suite> > <test>` line, and a file that could
42
+ // not load on ` FAIL <file> [ <file> ]`. A name printed twice is listed once.
43
+ function failed_tests(output: string): ReadonlyArray<string> {
44
+ const names = output.split('\n').map((line) => failed_name(line))
45
+
46
+ return [...new Set(names.filter((name) => name !== undefined))]
47
+ }
48
+
49
+ // The files the failed names belong to, each once — what to run after a fix, before the whole related
50
+ // set again (joshuafolkken/kit#5023).
51
+ function failed_files(names: ReadonlyArray<string>): ReadonlyArray<string> {
52
+ const files = names.map((name) => FAILED_FILE.exec(name)?.groups?.['file'])
53
+
54
+ return [...new Set(files.filter((file) => file !== undefined))]
55
+ }
56
+
57
+ function name_lines(names: ReadonlyArray<string>): ReadonlyArray<string> {
58
+ if (names.length === 0) return [NONE_NAMED]
59
+
60
+ return [...names.map((name) => ` ${name}`), `${RERUN_PREFIX}${failed_files(names).join(' ')}`]
61
+ }
62
+
63
+ // The verdict names the failed files, so the last line alone says what to fix (joshuafolkken/kit#5162).
64
+ function format_failure_summary(
65
+ output: string,
66
+ log_path: string | undefined,
67
+ label: string = UNIT_LOG_HEADER,
68
+ ): string {
69
+ const names = failed_tests(output)
70
+ const failures = `${[FAILED_HEADING, ...name_lines(names)].join('\n')}\n`
71
+ const failed = names.length === 0 ? [NONE_NAMED_VERDICT] : failed_files(names)
72
+ const verdict = verification_tail.format_verdict(label, failed)
73
+
74
+ return verification_tail.format_tail({ failures, log: { path: log_path }, verdict })
75
+ }
76
+
77
+ function format_green_summary(log_path: string | undefined, label: string): string {
78
+ const verdict = verification_tail.format_verdict(label, [])
79
+
80
+ return verification_tail.format_tail({ failures: '', log: { path: log_path }, verdict })
81
+ }
82
+
83
+ // Written on every run, as the gate's is, headed by the run's own invocation, and ended as every josh
84
+ // check ends (`verification-tail.ts`): a green run's last line is its verdict, not vitest's counts.
85
+ function report(
86
+ vitest_arguments: ReadonlyArray<string>,
87
+ output: string,
88
+ exit_code: number,
89
+ target: string = unit_log_path(vitest_arguments),
90
+ ): void {
91
+ const header = [UNIT_LOG_HEADER, ...vitest_arguments].join(' ')
92
+ const log_path = gate_log.write_gate_log([{ header, output }], target)
93
+ const label = [UNIT_LOG_HEADER, ...vitest_arguments.slice(0, 1)].join(' ')
94
+ const summary =
95
+ exit_code === 0
96
+ ? format_green_summary(log_path, label)
97
+ : format_failure_summary(output, log_path, label)
98
+
99
+ process.stdout.write(summary)
100
+ }
101
+
102
+ const vitest_failure_summary = {
103
+ RERUN_PREFIX,
104
+ failed_files,
105
+ failed_tests,
106
+ format_failure_summary,
107
+ report,
108
+ unit_log_path,
109
+ }
110
+
111
+ export { vitest_failure_summary }
@@ -36,12 +36,16 @@ const IMPORTED_BY_EVERY_TEST: ReadonlyArray<string> = [
36
36
  'scripts/josh/process-identity.ts',
37
37
  'scripts/josh/process-owner.ts',
38
38
  'scripts/josh/stamp-file.ts',
39
+ 'scripts/lane/lane-child-mark-keys.ts',
39
40
  'scripts/lib/atomic-write.ts',
40
41
  'scripts/lib/json-value.ts',
41
42
  'scripts/lib/read-file.ts',
42
43
  'scripts/lib/timeouts.ts',
44
+ 'scripts/test/unit-durations.ts',
43
45
  'scripts/test/unit-run-marker.ts',
44
46
  'scripts/test/unit-setup-files.ts',
47
+ 'scripts/test/vitest-cache-directory.ts',
48
+ 'scripts/test/vitest-cli-excludes.ts',
45
49
  'tools/test/duration-sequencer.ts',
46
50
  'tools/test/duration-shard.ts',
47
51
  'tools/test/pure-files.ts',
@@ -58,6 +62,13 @@ const WHOLE_SUITE_FILES: ReadonlySet<string> = new Set(
58
62
  [...ROOT_FILES, ...IMPORTED_BY_EVERY_TEST, ...SETUP_FILES].map((file) => path.normalize(file)),
59
63
  )
60
64
 
61
- const whole_suite_files = { WHOLE_SUITE_FILES }
65
+ // The part of `WHOLE_SUITE_FILES` a scoped run narrows to the test files importing it directly
66
+ // (`direct-importers.ts`, joshuafolkken/kit#5122): each is a module some test imports by name, unlike a
67
+ // root or setup file, which only the whole suite reaches.
68
+ const EVERY_TEST_IMPORTS: ReadonlySet<string> = new Set(
69
+ IMPORTED_BY_EVERY_TEST.map((file) => path.normalize(file)),
70
+ )
71
+
72
+ const whole_suite_files = { EVERY_TEST_IMPORTS, WHOLE_SUITE_FILES }
62
73
 
63
74
  export { whole_suite_files }
@@ -1,7 +1,9 @@
1
1
  import { time_bundle_call, type BundleFacts } from './time-bundle-call'
2
2
  import { time_bundles } from './time-bundles'
3
3
  import { time_call_identity } from './time-call-identity'
4
+ import { time_search_call } from './time-search-call'
4
5
  import { time_spans, type Span } from './time-spans'
6
+ import type { ToolCall } from './time-tool-call'
5
7
  import { time_transcript_line } from './time-transcript-line'
6
8
 
7
9
  // Whether the call about to go out should be refused because the run has stopped batching. What a
@@ -154,6 +156,16 @@ function is_reissued_refusal(spans: ReadonlyArray<Span>, call: GuardedCall): boo
154
156
  )
155
157
  }
156
158
 
159
+ // The closed history a refusal judges; empty where the call is not refusable or is a reissue, and an
160
+ // empty history never reaches the limit.
161
+ function refusable_spans(text: string, call: GuardedCall): ReadonlyArray<Span> {
162
+ if (!is_guarded_call(call)) return []
163
+
164
+ const { spans } = time_spans.parse_timeline(text)
165
+
166
+ return is_reissued_refusal(spans, call) ? [] : spans
167
+ }
168
+
157
169
  // The lane-child notice passes `NOTICE_REFIRE_EVERY` to recur on the notice's cadence.
158
170
  function should_block(
159
171
  text: string,
@@ -161,13 +173,37 @@ function should_block(
161
173
  refused_at_ms: number,
162
174
  refire_every: number = REFIRE_EVERY,
163
175
  ): boolean {
164
- if (!is_guarded_call(call)) return false
176
+ return is_at_limit(refusable_spans(text, call), call, refused_at_ms, refire_every)
177
+ }
165
178
 
166
- const { spans } = time_spans.parse_timeline(text)
179
+ function as_tool_call(call: GuardedCall): ToolCall {
180
+ return time_spans.to_tool_call(call.name, call.input, time_spans.NO_MESSAGE_ID)
181
+ }
182
+
183
+ // Asked before the transcript is read, like `is_guarded_call`, for the search-chain refusal alone.
184
+ function is_search_chain_call(call: GuardedCall): boolean {
185
+ return is_guarded_call(call) && time_search_call.is_search_chain_call(as_tool_call(call))
186
+ }
187
+
188
+ // The turns that make the limit — the last closed ones and the call in hand — are all searches.
189
+ function is_search_chain(spans: ReadonlyArray<Span>, call: GuardedCall): boolean {
190
+ const recent = time_bundles.open_sequence(spans).slice(-SEQUENCE_BEFORE_LIMIT)
191
+
192
+ return [...recent, as_tool_call(call)].every((turn) =>
193
+ time_search_call.is_search_chain_call(turn),
194
+ )
195
+ }
167
196
 
168
- if (is_reissued_refusal(spans, call)) return false
197
+ // The narrower refusal a lane child keeps (`search-batching` in `lane-guard-policy.ts`): only a chain
198
+ // of searches issued one per turn, the largest share of the child's single-call pairs.
199
+ function should_block_search_chain(
200
+ text: string,
201
+ call: GuardedCall,
202
+ refused_at_ms: number,
203
+ ): boolean {
204
+ const spans = refusable_spans(text, call)
169
205
 
170
- return is_at_limit(spans, call, refused_at_ms, refire_every)
206
+ return is_search_chain(spans, call) && is_at_limit(spans, call, refused_at_ms, REFIRE_EVERY)
171
207
  }
172
208
 
173
209
  // `notified_at_ms` is the notice's own record, so a notice never silences a later refusal.
@@ -279,9 +315,11 @@ const time_batch_guard = {
279
315
  is_guarded_call,
280
316
  is_notice_call,
281
317
  is_read_only_call,
318
+ is_search_chain_call,
282
319
  pairing_candidates,
283
320
  recent_candidates,
284
321
  should_block,
322
+ should_block_search_chain,
285
323
  should_notify,
286
324
  }
287
325
 
@@ -1,5 +1,5 @@
1
1
  import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
2
- import { canonical_command } from '#scripts/josh/josh-command-map'
2
+ import { canonical_command } from '#scripts/josh/josh-aliases'
3
3
  import { json_value } from '#scripts/lib/json-value'
4
4
  import { time_shell } from './time-shell'
5
5
 
@@ -95,15 +95,29 @@ interface BundleTotals {
95
95
  recoverable_round_trips: number
96
96
  by_tool: ReadonlyArray<BundleToolRow>
97
97
  unattributed_round_trips: number
98
+ single_call: SingleCallSplit
98
99
  is_measured: boolean
99
100
  }
100
101
 
102
+ // The consecutive single-call turns alone, split by the target test — what `josh time:density` reads.
103
+ // **The spread-apart `Agent` launch series is left out** of every field here: the walk never sees a
104
+ // launch, so it can be neither dependent nor independent, and folding its count into one side would
105
+ // make the two sides measure different populations.
106
+ interface SingleCallSplit {
107
+ // Turns that could have gone out beside the single-call turn before them.
108
+ independent_round_trips: number
109
+ // Turns whose call shared a target with the open sequence, so needed the turn before.
110
+ dependent_round_trips: number
111
+ by_tool: ReadonlyArray<BundleToolRow>
112
+ }
113
+
101
114
  const NO_BUNDLES: BundleTotals = {
102
115
  sequence_count: 0,
103
116
  longest_sequence: 0,
104
117
  recoverable_round_trips: 0,
105
118
  by_tool: [],
106
119
  unattributed_round_trips: 0,
120
+ single_call: { independent_round_trips: 0, dependent_round_trips: 0, by_tool: [] },
107
121
  is_measured: false,
108
122
  }
109
123
 
@@ -191,10 +205,11 @@ interface Walk {
191
205
  pending: Array<Span>
192
206
  sequence: Array<Span>
193
207
  closed: Array<Array<Span>>
208
+ dependent: number
194
209
  }
195
210
 
196
211
  function new_walk(): Walk {
197
- return { pending: [], sequence: [], closed: [] }
212
+ return { pending: [], sequence: [], closed: [], dependent: NONE }
198
213
  }
199
214
 
200
215
  function flush(walk: Walk): void {
@@ -204,9 +219,13 @@ function flush(walk: Walk): void {
204
219
  }
205
220
 
206
221
  // A conflicting call does not end the run of single-call turns; it starts a new sequence at itself,
207
- // because everything after it may still have been bundleable with it.
222
+ // because everything after it may still have been bundleable with it. The conflict is the one place a
223
+ // dependent pair is seen, so it is counted here rather than by a second walk.
208
224
  function extend(walk: Walk, span: Span): void {
209
- if (conflicts(span, walk.sequence)) flush(walk)
225
+ if (conflicts(span, walk.sequence)) {
226
+ walk.dependent += ONE_CALL
227
+ flush(walk)
228
+ }
210
229
 
211
230
  walk.sequence.push(span)
212
231
  }
@@ -359,6 +378,14 @@ function with_agent_row(
359
378
  return [...rows, row].toSorted(by_weight)
360
379
  }
361
380
 
381
+ function single_call_split(walk: Walk, sizes: ReadonlyArray<number>): SingleCallSplit {
382
+ return {
383
+ independent_round_trips: recoverable_of(sizes),
384
+ dependent_round_trips: walk.dependent,
385
+ by_tool: tool_rows(walk.closed),
386
+ }
387
+ }
388
+
362
389
  function build_bundles(spans: ReadonlyArray<Span>): BundleTotals {
363
390
  const walk = new_walk()
364
391
 
@@ -373,8 +400,9 @@ function build_bundles(spans: ReadonlyArray<Span>): BundleTotals {
373
400
  // is never bundleable, so it cannot have entered a sequence above — so their counts simply add.
374
401
  const sizes = sizes_of(walk.closed)
375
402
  const agent_sizes = time_agent_bundles.agent_bundles(spans)
376
- const by_tool = with_agent_row(tool_rows(walk.closed), agent_sizes)
377
- const recoverable_round_trips = recoverable_of(sizes) + recoverable_of(agent_sizes)
403
+ const single_call = single_call_split(walk, sizes)
404
+ const by_tool = with_agent_row(single_call.by_tool, agent_sizes)
405
+ const recoverable_round_trips = single_call.independent_round_trips + recoverable_of(agent_sizes)
378
406
 
379
407
  return {
380
408
  sequence_count: sizes.length + agent_sizes.length,
@@ -382,6 +410,7 @@ function build_bundles(spans: ReadonlyArray<Span>): BundleTotals {
382
410
  recoverable_round_trips,
383
411
  by_tool,
384
412
  unattributed_round_trips: recoverable_round_trips - attributed_of(by_tool),
413
+ single_call,
385
414
  is_measured: time_spans.has_transcript_data(spans.length),
386
415
  }
387
416
  }
@@ -524,9 +553,11 @@ const time_bundles = {
524
553
  NO_BUNDLES,
525
554
  build_bundles,
526
555
  bundle_lines,
556
+ by_weight,
527
557
  is_dependent,
528
558
  open_sequence,
529
559
  shares_target,
530
560
  }
531
561
 
562
+ export type { BundleToolRow }
532
563
  export { time_bundles }
@@ -32,8 +32,8 @@ import { json_value } from '#scripts/lib/json-value'
32
32
  // which the main line loaded the skill itself.
33
33
  //
34
34
  // **The launch is recognized by its prompt carrying both `/code-review` and `review:attest`.** The
35
- // first is the instruction to invoke the review skill; the second is the attestation line
36
- // `pnpm josh review:brief` emits into the brief the launch passes verbatim.
35
+ // first is the instruction to invoke the review skill; the second is the attestation step the launch
36
+ // prompt `pnpm josh run:review` prints beside each brief file names (joshuafolkken/kit#4863).
37
37
  // Neither alone is enough: an investigation subagent about the review tooling quotes `/code-review`
38
38
  // in prose and is handed no brief, so `review:attest` is what tells a genuine review launch from a
39
39
  // discussion of one — measured against five real launches, it admits the review and rejects every
@@ -0,0 +1,38 @@
1
+ import { time_shell } from './time-shell'
2
+ import type { ToolCall } from './time-tool-call'
3
+
4
+ // Which calls are a search, the one answer the investigation guard and the batching guard both read.
5
+ // A search carries no file text but costs a turn, so it is what a run issues one per turn while it
6
+ // locates the next edit.
7
+
8
+ const SEARCH_COMMANDS: ReadonlyArray<string> = ['fd', 'find', 'grep', 'rg']
9
+ const SEARCH_LABELS: ReadonlySet<string> = new Set(
10
+ SEARCH_COMMANDS.map((command) => time_shell.bash_label(command)),
11
+ )
12
+
13
+ // A search chain also prints the lines a search found, so `sed` (read-only) joins it: `grep` → `sed`
14
+ // was measured the second-largest single-call pair in lane children.
15
+ const CHAIN_COMMANDS: ReadonlyArray<string> = [...SEARCH_COMMANDS, 'sed']
16
+ const CHAIN_LABELS: ReadonlySet<string> = new Set(
17
+ CHAIN_COMMANDS.map((command) => time_shell.bash_label(command)),
18
+ )
19
+
20
+ type SearchFacts = Pick<ToolCall, 'label' | 'is_bundleable' | 'may_write' | 'is_writing'>
21
+
22
+ // `may_write` over-calls, as the investigation guard's refusal test must.
23
+ function is_read_only_search(call: SearchFacts): boolean {
24
+ return SEARCH_LABELS.has(call.label) && call.is_bundleable && !call.may_write
25
+ }
26
+
27
+ // `may_write` holds for every `sed`, so the chain asks `is_writing` — certain, and false for `sed -n`.
28
+ function is_search_chain_call(call: SearchFacts): boolean {
29
+ return CHAIN_LABELS.has(call.label) && call.is_bundleable && !call.is_writing
30
+ }
31
+
32
+ const time_search_call = {
33
+ is_read_only_search,
34
+ is_search_chain_call,
35
+ }
36
+
37
+ export type { SearchFacts }
38
+ export { time_search_call }
@@ -1,5 +1,5 @@
1
1
  import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
2
- import { canonical_command } from '#scripts/josh/josh-command-map'
2
+ import { canonical_command } from '#scripts/josh/josh-aliases'
3
3
  import { json_value } from '#scripts/lib/json-value'
4
4
 
5
5
  // Reading a shell command well enough to name what it ran.
@@ -118,6 +118,9 @@ interface TranscriptLine {
118
118
  // every line lacking an id would otherwise fall into one bucket spanning the whole file.
119
119
  message_id: string
120
120
  blocks: Array<Block>
121
+ // The reason a Stop-hook block sent this turn back with, its label stripped, or `''` for every
122
+ // other line.
123
+ stop_feedback: string
121
124
  // The guard whose Stop-hook block sent this turn back, or `''` for every other line
122
125
  // — read the way `Block.refusal_guard` is, off the reason's opening.
123
126
  stop_guard: string
@@ -135,6 +138,11 @@ function block_names(raw: TranscriptBlock): Pick<Block, 'type' | 'name' | 'id' |
135
138
  }
136
139
  }
137
140
 
141
+ // The body a refusal opens with, the harness's own label stripped — shared with `rule-value.ts`.
142
+ function refusal_body(text: string): string {
143
+ return text.trimStart().replace(HOOK_ERROR_LABEL, '')
144
+ }
145
+
138
146
  // **A refusal is identified by what its body opens with, never by what it carries inside**.
139
147
  // The hook denies a call on behalf of the one guard whose trigger fired and
140
148
  // writes that guard's reason back as the whole result, so the speaker is the token after the `⛔` and
@@ -146,7 +154,7 @@ function block_names(raw: TranscriptBlock): Pick<Block, 'type' | 'name' | 'id' |
146
154
  // label is stripped before the opening is read. Without it no live refusal
147
155
  // carried a guard: the fixtures wrote the bare `⛔` body and passed while every recorded one read `''`.
148
156
  function guard_from_refusal(text: string): string {
149
- const trimmed = text.trimStart().replace(HOOK_ERROR_LABEL, '')
157
+ const trimmed = refusal_body(text)
150
158
  if (!trimmed.startsWith(REFUSAL_MARKER)) return ''
151
159
 
152
160
  const headline = trimmed.slice(REFUSAL_MARKER.length).split('\n', 1)[0]?.trimStart() ?? ''
@@ -157,12 +165,12 @@ function guard_from_refusal(text: string): string {
157
165
 
158
166
  // **Only the harness's own feedback line counts.** It writes a Stop block back as a user turn whose
159
167
  // whole body is `Stop hook feedback:` and the reason, so a prompt that merely quotes one never opens
160
- // with the label and reads `''`.
161
- function stop_guard_of(data: TranscriptRecord): string {
168
+ // with the label and reads `''`. The reason is kept whole, so `rule-value.ts` can match it to its row.
169
+ function stop_feedback_of(data: TranscriptRecord): string {
162
170
  const content = data.message?.content
163
171
  if (typeof content !== 'string' || !STOP_FEEDBACK_LABEL.test(content)) return ''
164
172
 
165
- return guard_from_refusal(content.replace(STOP_FEEDBACK_LABEL, ''))
173
+ return content.replace(STOP_FEEDBACK_LABEL, '').trim()
166
174
  }
167
175
 
168
176
  function token_lines_of(text: string): Array<string> {
@@ -254,12 +262,15 @@ function to_line(data: TranscriptRecord): TranscriptLine | undefined {
254
262
 
255
263
  if (timestamp_ms === undefined) return undefined
256
264
 
265
+ const stop_feedback = stop_feedback_of(data)
266
+
257
267
  return {
258
268
  type: data.type ?? '',
259
269
  timestamp_ms,
260
270
  branch: data.gitBranch ?? UNKNOWN_BRANCH,
261
271
  finished_background: finished_background(data),
262
- stop_guard: stop_guard_of(data),
272
+ stop_feedback,
273
+ stop_guard: guard_from_refusal(stop_feedback),
263
274
  ...message_fields(data.message),
264
275
  }
265
276
  }
@@ -282,6 +293,7 @@ const time_transcript_line = {
282
293
  guard_from_refusal,
283
294
  parse_line,
284
295
  parse_text,
296
+ refusal_body,
285
297
  }
286
298
 
287
299
  export type { Block, TranscriptLine }
@@ -0,0 +1,124 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { isDeepStrictEqual } from 'node:util'
3
+ import { git_spawn_sync } from '#scripts/git/git-spawn-sync'
4
+ import { PACKAGE_JSON } from '#scripts/lib/package-manifest'
5
+ import { z } from 'zod'
6
+ import { package_manager_version } from './package-manager-version'
7
+
8
+ // The `devEngines` verdict a dependency update is judged by (`.claude/skills/dependency-update`):
9
+ // the committed manifest against the working tree, printed as one line so nobody compares the two
10
+ // strings by eye. `kept` is the lockstep pnpm bump — the version equal to the `packageManager` pin,
11
+ // integrity suffix included — and `josh sync`'s `onFail` move from `error` to `download`; any other
12
+ // change is `restore`, with the reason in brackets.
13
+ const HEAD_PACKAGE_JSON = `HEAD:./${PACKAGE_JSON}`
14
+ const NOT_JUDGED = `devEngines: not judged (no ${PACKAGE_JSON} at HEAD to compare against)`
15
+ const PACKAGE_MANAGER_KEY = 'packageManager'
16
+ const VERSION_KEY = 'version'
17
+ const ON_FAIL_KEY = 'onFail'
18
+ const ON_FAIL_FROM = 'error'
19
+ const ON_FAIL_TO = 'download'
20
+ const UNCHANGED = 'devEngines: unchanged'
21
+ const KEPT = 'devEngines: kept'
22
+ const SUFFIX_MISSING = 'integrity suffix missing'
23
+ const NO_PIN = 'no pnpm@ packageManager pin to match'
24
+
25
+ type Entries = Record<string, unknown>
26
+
27
+ const entries_schema = z.record(z.string(), z.unknown())
28
+ const manifest_schema = z.looseObject({
29
+ packageManager: z.string().default(''),
30
+ devEngines: entries_schema.default({}),
31
+ })
32
+
33
+ type Manifest = z.infer<typeof manifest_schema>
34
+
35
+ function read_manifest(content: string): Manifest {
36
+ return manifest_schema.parse(JSON.parse(content))
37
+ }
38
+
39
+ function is_entries(value: unknown): value is Entries {
40
+ return entries_schema.safeParse(value).success
41
+ }
42
+
43
+ function read_package_manager(development_engines: Entries): Entries {
44
+ const value = development_engines[PACKAGE_MANAGER_KEY]
45
+
46
+ return is_entries(value) ? value : {}
47
+ }
48
+
49
+ // `packageManager` is compared key by key only when both sides are objects; the npm array form, or
50
+ // a block added or removed whole, is a structural change in itself.
51
+ function is_compared_inside(key: string, before: Entries, after: Entries): boolean {
52
+ return key === PACKAGE_MANAGER_KEY && is_entries(before[key]) && is_entries(after[key])
53
+ }
54
+
55
+ function changed_keys(before: Entries, after: Entries): Array<string> {
56
+ const keys = new Set([...Object.keys(before), ...Object.keys(after)])
57
+
58
+ return [...keys].filter((key) => !isDeepStrictEqual(before[key], after[key]))
59
+ }
60
+
61
+ // The version is judged against the pin below, so only its presence counts here.
62
+ function is_expected_change(key: string, before: Entries, after: Entries): boolean {
63
+ if (key === VERSION_KEY) return typeof after[key] === 'string'
64
+
65
+ return key === ON_FAIL_KEY && before[key] === ON_FAIL_FROM && after[key] === ON_FAIL_TO
66
+ }
67
+
68
+ function find_structure_change(before: Entries, after: Entries): string | undefined {
69
+ const outer = changed_keys(before, after).filter((key) => !is_compared_inside(key, before, after))
70
+ const pm_before = read_package_manager(before)
71
+ const pm_after = read_package_manager(after)
72
+ const inner = changed_keys(pm_before, pm_after).filter(
73
+ (key) => !is_expected_change(key, pm_before, pm_after),
74
+ )
75
+ const keys = [...outer, ...inner.map((key) => `${PACKAGE_MANAGER_KEY}.${key}`)]
76
+
77
+ return keys.length === 0 ? undefined : `structure changed: ${keys.join(', ')}`
78
+ }
79
+
80
+ // Without a `pnpm@` pin there is nothing the version could be kept in lockstep with.
81
+ function find_version_mismatch(after: Manifest): string | undefined {
82
+ const pin = package_manager_version.extract_pnpm_pin(after.packageManager)
83
+ if (pin === undefined) return NO_PIN
84
+ const current = String(read_package_manager(after.devEngines)[VERSION_KEY])
85
+ if (current === pin) return undefined
86
+ if (pin.startsWith(`${current}+`)) return SUFFIX_MISSING
87
+
88
+ return `version ${current} is not the packageManager pin ${pin}`
89
+ }
90
+
91
+ function judge(before_content: string, after_content: string): string {
92
+ const before = read_manifest(before_content).devEngines
93
+ const after = read_manifest(after_content)
94
+ if (isDeepStrictEqual(before, after.devEngines)) return UNCHANGED
95
+ const reason = find_structure_change(before, after.devEngines) ?? find_version_mismatch(after)
96
+
97
+ return reason === undefined ? KEPT : `devEngines: restore (${reason})`
98
+ }
99
+
100
+ // A manifest that does not parse is answered apart from every verdict rather than thrown, so the
101
+ // overrides check that follows still runs.
102
+ function judge_or_explain(head: string, current: string): string {
103
+ try {
104
+ return judge(head, current)
105
+ } catch (error) {
106
+ return `devEngines: not judged (${PACKAGE_JSON} unreadable: ${String(error)})`
107
+ }
108
+ }
109
+
110
+ function describe(head: string | undefined, current: string): string {
111
+ return head === undefined ? NOT_JUDGED : judge_or_explain(head, current)
112
+ }
113
+
114
+ // A `git show` that fails — no commit yet, no repository — answers apart from every verdict, so it
115
+ // never reads as a `devEngines` block the update added.
116
+ function report(): void {
117
+ const head = git_spawn_sync.read(['show', HEAD_PACKAGE_JSON])
118
+
119
+ console.info(describe(head, readFileSync(PACKAGE_JSON, 'utf8')))
120
+ }
121
+
122
+ const development_engines_verdict = { judge, describe, report }
123
+
124
+ export { development_engines_verdict }
@@ -46,6 +46,7 @@ import { PACKAGE_JSON } from '#scripts/lib/package-manifest'
46
46
  import { execaSync } from 'execa'
47
47
  import semver from 'semver'
48
48
  import { z } from 'zod'
49
+ import { development_engines_verdict } from './development-engines-verdict'
49
50
  import { npm_registry } from './npm-registry'
50
51
  import { package_manager_version } from './package-manager-version'
51
52
  import { release_age } from './release-age'
@@ -392,6 +393,7 @@ function main(): number {
392
393
  const status = bump_to_registry_target(original, extract_pnpm_major(original))
393
394
 
394
395
  sync_development_engines()
396
+ development_engines_verdict.report()
395
397
 
396
398
  return status
397
399
  }
@@ -77,6 +77,8 @@ e2e/.auth/chrome-profile/
77
77
  # Claude Code runtime artifacts
78
78
  .claude/scheduled_tasks.lock
79
79
  .claude/tmp/
80
+ # The machine-local permission allowlist (commands and paths), never shared
81
+ .claude/settings.local.json
80
82
 
81
83
  # Scratch files any agent writes (Issue / PR bodies, plans, handoffs, logs); the rest of .josh/ is tracked
82
84
  .josh/tmp/
@@ -247,6 +247,47 @@ jobs:
247
247
  echo 'size-limit is not installed — skipping the bundle-size check.'
248
248
  fi
249
249
 
250
+ # The pre-commit secretlint scan skips when its binary is missing and never runs on a commit made
251
+ # with `--no-verify`, so this job scans the pull request's changes again where neither applies
252
+ # (joshuafolkken/kit#4657). It needs no other job and none needs it, so it stays off the critical
253
+ # path, and its own `Secret Scan` context is what makes a finding block the merge. Only the files
254
+ # the pull request adds or changes are read: the merge commit's first parent is the base, which is
255
+ # why the checkout fetches two commits. secretlint comes from the same store cache every other job
256
+ # installs from, and `JOSH_SECRETLINT_REQUIRE` turns the hook's missing-binary skip into a failure.
257
+ # `shell: bash` adds `pipefail`, so a diff that fails cannot hand `xargs` an empty list and pass.
258
+ secret-scan:
259
+ name: Secret Scan
260
+ if: github.event_name == 'pull_request'
261
+ runs-on: ubuntu-latest
262
+ timeout-minutes: 5
263
+ permissions:
264
+ contents: read
265
+ packages: read
266
+ steps:
267
+ - name: Checkout code
268
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
269
+ with:
270
+ persist-credentials: false
271
+ fetch-depth: 2
272
+
273
+ - name: Setup Node.js
274
+ uses: ./.github/actions/setup-node
275
+
276
+ - name: Configure GitHub Packages authentication for existing projects
277
+ run: printf '//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n' >> "$HOME/.npmrc"
278
+
279
+ - name: Setup pnpm and install dependencies
280
+ uses: ./.github/actions/setup-pnpm
281
+ with:
282
+ node-auth-token: ${{ secrets.GITHUB_TOKEN }}
283
+
284
+ - name: Scan the changed files for secrets
285
+ id: scan
286
+ shell: bash
287
+ env:
288
+ JOSH_SECRETLINT_REQUIRE: '1'
289
+ run: git diff -z --name-only --diff-filter=d HEAD^1 HEAD | xargs -0 -r pnpm josh secretlint-scan
290
+
250
291
  security:
251
292
  name: Security Audit
252
293
  runs-on: ubuntu-latest