@joshuafolkken/kit 1.1784.0 → 1.2114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/.claude/agents/reviewer-verification.md +19 -0
  2. package/.claude/agents/reviewer.md +19 -0
  3. package/.claude/settings.json +5 -6
  4. package/.claude/skills/dependency-update/SKILL.md +9 -10
  5. package/.claude/skills/workflow-commands/SKILL.md +6 -6
  6. package/.claude/skills/workflow-commands/background-commands.md +8 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
  13. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  14. package/.claude/skills/workflow-commands/chain-rule.md +41 -32
  15. package/.claude/skills/workflow-commands/delegation.md +6 -6
  16. package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
  17. package/.claude/skills/workflow-commands/followup.md +7 -5
  18. package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
  19. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  20. package/.claude/skills/workflow-commands/issue-comments.md +9 -4
  21. package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +2 -2
  23. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  24. package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
  25. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
  26. package/.claude/skills/workflow-commands/prerequisite.md +3 -2
  27. package/.claude/skills/workflow-commands/retrospective.md +2 -2
  28. package/.claude/skills/workflow-commands/split-assessment.md +7 -5
  29. package/.codex/hooks.json +5 -5
  30. package/.github/actions/setup-pnpm/action.yml +9 -16
  31. package/.github/dependabot.yml +16 -0
  32. package/.github/workflows/auto-tag.yml +45 -0
  33. package/.github/workflows/ci.yml +155 -15
  34. package/.github/workflows/dependabot-auto-merge.yml +63 -4
  35. package/.github/workflows/production.yml +8 -0
  36. package/.github/workflows/publish.yml +59 -45
  37. package/.vscode/settings.json +19 -0
  38. package/CLAUDE.md +5 -12
  39. package/SECURITY.md +2 -2
  40. package/dist/CLAUDE.md +5 -12
  41. package/dist/commands/document-read-cli.js +2 -1
  42. package/dist/commands/document-section-main.js +2 -1
  43. package/dist/commands/read-set-cli.js +194 -134
  44. package/dist/commands/run-prep-main.js +9694 -10173
  45. package/dist/commands/run-step-cli.js +22138 -18285
  46. package/dist/hooks/audit-provision.js +23 -5
  47. package/dist/hooks/chunk-3GKE653K.js +6204 -0
  48. package/dist/hooks/chunk-45CJJOO7.js +1243 -0
  49. package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
  50. package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
  51. package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
  52. package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
  53. package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
  54. package/dist/hooks/chunk-VVNYJWEI.js +30 -0
  55. package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
  56. package/dist/hooks/codex-hook-adapter.js +8 -8
  57. package/dist/hooks/format-edited.js +4 -5
  58. package/dist/hooks/inputs.json +415 -389
  59. package/dist/hooks/pretool-guard.js +7 -6
  60. package/dist/hooks/session-lang.js +101 -258
  61. package/dist/hooks/stop-guard.js +1744 -2576
  62. package/dist/josh.js +7133 -6368
  63. package/dist/version/index.js +161 -1
  64. package/eslint/rules/comment-why-only.js +108 -0
  65. package/package.json +15 -8
  66. package/prompts/coding-standards.md +1 -1
  67. package/prompts/collaboration-workflow/file-edits.md +14 -11
  68. package/prompts/collaboration-workflow/issue-citation.md +0 -4
  69. package/prompts/collaboration-workflow/issue-template.md +40 -21
  70. package/prompts/collaboration-workflow/lane-instructions.md +3 -1
  71. package/prompts/collaboration-workflow/measurement.md +8 -1
  72. package/prompts/collaboration-workflow/operating-rules.md +11 -2
  73. package/prompts/collaboration-workflow/output-bounds.md +6 -2
  74. package/prompts/collaboration-workflow/plan-comment.md +1 -1
  75. package/prompts/collaboration-workflow/principles.md +9 -0
  76. package/prompts/collaboration-workflow/residency.md +3 -2
  77. package/prompts/collaboration-workflow/rule-delivery.md +13 -1
  78. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  79. package/prompts/collaboration-workflow/wip-cap.md +3 -1
  80. package/prompts/review-rubric.md +8 -0
  81. package/prompts/review.md +31 -22
  82. package/prompts/testing-guide.md +1 -0
  83. package/scripts/agent/agent-argv.ts +14 -16
  84. package/scripts/agent/agent-provider.ts +138 -0
  85. package/scripts/agent/agent-role-profile.ts +108 -156
  86. package/scripts/agent/agent-safe-text.ts +20 -5
  87. package/scripts/agent/api-usage-limit.ts +65 -0
  88. package/scripts/agent/claude-agent-argv.ts +19 -9
  89. package/scripts/agent/codex-agent-argv.ts +118 -22
  90. package/scripts/agent/usage-limit-reset.ts +179 -0
  91. package/scripts/auth/claude-token-output.ts +2 -2
  92. package/scripts/auto-ok/auto-ok-author.ts +53 -0
  93. package/scripts/auto-ok/auto-ok-cli.ts +20 -9
  94. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  95. package/scripts/backlog/backlog-drive-cli.ts +6 -1
  96. package/scripts/backlog/backlog-drive-hold.ts +3 -3
  97. package/scripts/backlog/backlog-drive-idle.ts +10 -0
  98. package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
  99. package/scripts/backlog/backlog-drive-launch.ts +8 -2
  100. package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
  101. package/scripts/backlog/backlog-drive-named.ts +28 -7
  102. package/scripts/backlog/backlog-drive-paused.ts +5 -4
  103. package/scripts/backlog/backlog-drive-restore.ts +11 -3
  104. package/scripts/backlog/backlog-drive.ts +21 -60
  105. package/scripts/backlog/backlog-gate-scope.ts +61 -0
  106. package/scripts/backlog/backlog-measure-close.ts +112 -0
  107. package/scripts/backlog/backlog-next.ts +39 -49
  108. package/scripts/backlog/backlog-occupied.ts +2 -2
  109. package/scripts/backlog/backlog-overlap.ts +42 -8
  110. package/scripts/backlog/backlog-plan-cli.ts +4 -1
  111. package/scripts/backlog/backlog-plan-comment.ts +81 -0
  112. package/scripts/backlog/backlog-plan-read.ts +19 -6
  113. package/scripts/backlog/backlog-pool.ts +50 -12
  114. package/scripts/backlog/backlog-rank.ts +60 -13
  115. package/scripts/backlog/backlog-stalled-detect.ts +2 -1
  116. package/scripts/backlog/backlog-waves.ts +17 -2
  117. package/scripts/backlog/backlogrun-launch.ts +7 -4
  118. package/scripts/cost-runtime/context-cut-payback.ts +27 -25
  119. package/scripts/cost-runtime/cost-cli.ts +37 -16
  120. package/scripts/cost-runtime/cost-pricing.ts +9 -0
  121. package/scripts/cost-runtime/cost-verdict.ts +4 -1
  122. package/scripts/delegation/delegation-policy.ts +2 -2
  123. package/scripts/delegation/duplicate-read-guard.ts +1 -0
  124. package/scripts/delegation/investigation-guard.ts +14 -2
  125. package/scripts/delegation/investigation-reads.ts +45 -23
  126. package/scripts/dependabot/dependabot-cooldown.ts +3 -2
  127. package/scripts/discovery/repo-party.ts +80 -20
  128. package/scripts/discovery/session-owner-pin.ts +51 -0
  129. package/scripts/document/capped-print.ts +1 -1
  130. package/scripts/document/command-reference-line-budget.ts +42 -0
  131. package/scripts/document/document-byte-budget.ts +17 -7
  132. package/scripts/document/document-checks.ts +20 -0
  133. package/scripts/document/document-ratchet-check.ts +101 -0
  134. package/scripts/document/document-section.ts +1 -0
  135. package/scripts/document/entry-read-budget.ts +7 -3
  136. package/scripts/document/issue-citation-budget.ts +94 -0
  137. package/scripts/epic/epic-cli-argv.ts +2 -2
  138. package/scripts/epic/epic-fetch.ts +4 -2
  139. package/scripts/epic/epic-graph.ts +7 -2
  140. package/scripts/epic/epic-issue.ts +3 -1
  141. package/scripts/epic/epic-rank.ts +5 -0
  142. package/scripts/exports/unused-members-check.ts +62 -0
  143. package/scripts/exports/unused-members-cli.ts +6 -51
  144. package/scripts/followup/followup-ci-path.ts +67 -0
  145. package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
  146. package/scripts/followup/followup-coderabbit-wait.ts +13 -10
  147. package/scripts/followup/followup-failed-tests.ts +160 -0
  148. package/scripts/followup/followup-job-needs.ts +100 -0
  149. package/scripts/followup/followup-job-rerun.ts +303 -0
  150. package/scripts/followup/followup-main-move.ts +113 -0
  151. package/scripts/followup/git-followup-finish.ts +43 -11
  152. package/scripts/followup/git-followup-pending.ts +3 -3
  153. package/scripts/followup/git-followup-stages.ts +41 -11
  154. package/scripts/followup/git-pr-followup-checks.ts +55 -16
  155. package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
  156. package/scripts/followup/git-pr-followup.ts +62 -27
  157. package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
  158. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
  159. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
  160. package/scripts/gate/busy-average.ts +34 -2
  161. package/scripts/gate/check-related-pinned.ts +43 -0
  162. package/scripts/gate/check-related-reuse.ts +64 -0
  163. package/scripts/gate/check-related-section.ts +57 -0
  164. package/scripts/gate/check-related-unplanned.ts +34 -0
  165. package/scripts/gate/check-related.ts +265 -0
  166. package/scripts/gate/command-memory.ts +1 -1
  167. package/scripts/gate/core-admission.ts +24 -6
  168. package/scripts/gate/core-budget-timing.ts +20 -6
  169. package/scripts/gate/core-budget.ts +40 -11
  170. package/scripts/gate/core-wait-record.ts +3 -2
  171. package/scripts/gate/core-wait.ts +25 -2
  172. package/scripts/gate/core-weights.ts +15 -7
  173. package/scripts/gate/gate-labels.ts +46 -0
  174. package/scripts/gate/gate-ledger.ts +14 -6
  175. package/scripts/gate/gate-plan.ts +103 -58
  176. package/scripts/gate/gate-report.ts +9 -7
  177. package/scripts/gate/gate-run-steps.ts +26 -3
  178. package/scripts/gate/gate-skip.ts +45 -6
  179. package/scripts/gate/gate-static-group.ts +60 -0
  180. package/scripts/gate/gate-steps.ts +2 -1
  181. package/scripts/gate/gate-tree.ts +4 -2
  182. package/scripts/gate/hook-gate-reuse.ts +19 -2
  183. package/scripts/gate/metrics-totals.ts +59 -0
  184. package/scripts/gate/scoped-green.ts +68 -15
  185. package/scripts/gate/since-green.ts +14 -6
  186. package/scripts/gate/structural-checks.ts +96 -0
  187. package/scripts/gate/verification-gate.ts +51 -16
  188. package/scripts/gate/verification-inputs.ts +21 -6
  189. package/scripts/gate/verification-tail.ts +52 -0
  190. package/scripts/gh/gh-author-association.ts +23 -2
  191. package/scripts/gh/git-ai-review-scan.ts +1 -1
  192. package/scripts/gh/git-gh-api-path.ts +12 -3
  193. package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
  194. package/scripts/gh/git-gh-exec.ts +32 -2
  195. package/scripts/gh/git-gh-issue-read.ts +4 -2
  196. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  197. package/scripts/gh/git-gh-issue-write.ts +8 -2
  198. package/scripts/gh/git-gh-issue.ts +12 -5
  199. package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
  200. package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
  201. package/scripts/gh/git-gh-pr-read.ts +74 -13
  202. package/scripts/gh/git-gh-pr-rest.ts +3 -1
  203. package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
  204. package/scripts/gh/git-gh-pr-too-large.ts +20 -0
  205. package/scripts/gh/git-gh-pr.ts +21 -7
  206. package/scripts/gh/git-gh-retry.ts +141 -0
  207. package/scripts/gh/git-pr-ai-review.ts +15 -6
  208. package/scripts/gh/git-pr-checks-eval.ts +3 -0
  209. package/scripts/gh/git-pr-checks.ts +5 -5
  210. package/scripts/git/base-tree.ts +30 -0
  211. package/scripts/git/change-base.ts +4 -10
  212. package/scripts/git/changed-paths-sync.ts +63 -0
  213. package/scripts/git/changed-paths.ts +16 -1
  214. package/scripts/git/git-common-directory.ts +11 -1
  215. package/scripts/git/git-diff-reads.ts +66 -20
  216. package/scripts/git/git-error.ts +9 -2
  217. package/scripts/git/git-schemas.ts +13 -0
  218. package/scripts/git/git-worktree.ts +13 -0
  219. package/scripts/git/gone-branch.ts +4 -1
  220. package/scripts/git/main-merge-budget-conflict.ts +89 -0
  221. package/scripts/git/main-merge-guard.ts +55 -18
  222. package/scripts/git/main-merge-regenerate.ts +59 -25
  223. package/scripts/git/main-merge.ts +53 -5
  224. package/scripts/git/no-base-checkout.ts +127 -0
  225. package/scripts/git/stash/git-stash.ts +11 -2
  226. package/scripts/git/stash/stash-push-cli.ts +10 -0
  227. package/scripts/hooks/batch-guard.ts +40 -21
  228. package/scripts/hooks/edited-cspell.ts +37 -11
  229. package/scripts/hooks/format-edited-file.ts +22 -2
  230. package/scripts/hooks/lane-plan-guard.ts +50 -0
  231. package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
  232. package/scripts/hooks/pre-commit-lint.ts +14 -1
  233. package/scripts/hooks/pre-push-related-switch.ts +10 -0
  234. package/scripts/hooks/pre-push-unit.ts +76 -10
  235. package/scripts/hooks/pretool-guard.ts +2 -0
  236. package/scripts/hooks/reserved-run.ts +14 -1
  237. package/scripts/hooks/run-hook.sh +23 -8
  238. package/scripts/hooks/step-zero-notice.ts +6 -1
  239. package/scripts/hooks/stop-actions.ts +3 -15
  240. package/scripts/hooks/stop-guard.ts +7 -6
  241. package/scripts/init/distributed-paths.ts +1 -0
  242. package/scripts/init/hook-command-rewrite.ts +5 -1
  243. package/scripts/init/hook-launch.ts +4 -2
  244. package/scripts/issue/aggregation-split.ts +52 -0
  245. package/scripts/issue/defect-origin-line.ts +19 -1
  246. package/scripts/issue/defect-rate-cli.ts +5 -102
  247. package/scripts/issue/defect-report.ts +4 -4
  248. package/scripts/issue/effect-audit-cli.ts +178 -0
  249. package/scripts/issue/effect-audit.ts +185 -0
  250. package/scripts/issue/expected-effect-lint.ts +64 -9
  251. package/scripts/issue/git-next-issues.ts +5 -0
  252. package/scripts/issue/issue-body-or-comment.ts +38 -4
  253. package/scripts/issue/issue-cite-cli.ts +1 -3
  254. package/scripts/issue/issue-classification.ts +1 -0
  255. package/scripts/issue/issue-closing-pr.ts +8 -1
  256. package/scripts/issue/issue-evidence-exit.ts +4 -2
  257. package/scripts/issue/issue-file-cap.ts +58 -0
  258. package/scripts/issue/issue-file-cli.ts +111 -38
  259. package/scripts/issue/issue-file-deferral.ts +52 -0
  260. package/scripts/issue/issue-file-fold.ts +29 -10
  261. package/scripts/issue/issue-file-interrupt.ts +85 -0
  262. package/scripts/issue/issue-file-lane-hint.ts +30 -0
  263. package/scripts/issue/issue-file.ts +43 -6
  264. package/scripts/issue/issue-labels.ts +19 -2
  265. package/scripts/issue/issue-lint-cli.ts +27 -2
  266. package/scripts/issue/issue-measured-cli.ts +13 -35
  267. package/scripts/issue/issue-measured-close.ts +58 -0
  268. package/scripts/issue/issue-not-before-label.ts +2 -2
  269. package/scripts/issue/issue-not-before.ts +4 -1
  270. package/scripts/issue/issue-priority-cli.ts +60 -0
  271. package/scripts/issue/issue-priority-raise.ts +55 -0
  272. package/scripts/issue/issue-priority.ts +174 -43
  273. package/scripts/issue/issue-rank.ts +21 -4
  274. package/scripts/issue/issue-read-cli.ts +32 -6
  275. package/scripts/issue/issue-read.ts +37 -12
  276. package/scripts/issue/issue-sample-gate.ts +95 -74
  277. package/scripts/issue/issue-sample-prime.ts +127 -0
  278. package/scripts/issue/issue-sample-required.ts +57 -0
  279. package/scripts/issue/issue-sample-source.ts +12 -11
  280. package/scripts/issue/issue-search.ts +124 -0
  281. package/scripts/issue/issue-state.ts +13 -1
  282. package/scripts/issue/issue-template-section.ts +43 -0
  283. package/scripts/issue/issue-watch-diff.ts +31 -1
  284. package/scripts/issue/issue-watch-hook.ts +11 -2
  285. package/scripts/issue/issue-watch.ts +55 -12
  286. package/scripts/issue/markdown-section.ts +1 -0
  287. package/scripts/issue/measure-declaration.ts +121 -0
  288. package/scripts/issue/measure-rerun-cli.ts +7 -70
  289. package/scripts/issue/plan-consumers-section.ts +22 -1
  290. package/scripts/issue/reproduction-measure.ts +3 -1
  291. package/scripts/issue/size-estimate.ts +90 -0
  292. package/scripts/issue/trusted-measure.ts +84 -0
  293. package/scripts/josh/command-usage.ts +3 -2
  294. package/scripts/josh/durable-state-migrate.ts +105 -0
  295. package/scripts/josh/durable-state.ts +112 -0
  296. package/scripts/josh/josh-aliases.ts +95 -0
  297. package/scripts/josh/josh-command-map.ts +1 -82
  298. package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
  299. package/scripts/josh/josh-commands-ai.ts +5 -5
  300. package/scripts/josh/josh-commands-development.ts +59 -58
  301. package/scripts/josh/josh-commands-issue-write.ts +23 -4
  302. package/scripts/josh/josh-commands-issue.ts +32 -0
  303. package/scripts/josh/josh-commands-lane.ts +87 -4
  304. package/scripts/josh/josh-commands-lint.ts +16 -3
  305. package/scripts/josh/josh-commands-maintenance.ts +15 -0
  306. package/scripts/josh/josh-commands-review.ts +41 -6
  307. package/scripts/josh/josh-commands-ship.ts +56 -8
  308. package/scripts/josh/josh-commands-test-check.ts +80 -0
  309. package/scripts/josh/josh-commands-workflow.ts +1 -1
  310. package/scripts/josh/josh-environment-file.ts +43 -3
  311. package/scripts/josh/josh-in-process.ts +2 -2
  312. package/scripts/josh/josh-logic.ts +5 -3
  313. package/scripts/josh/josh-run.ts +19 -1
  314. package/scripts/josh/run-record-path.ts +15 -0
  315. package/scripts/josh/stamp-file.ts +11 -2
  316. package/scripts/lane/lane-adopt-cli.ts +98 -0
  317. package/scripts/lane/lane-adopt.ts +208 -0
  318. package/scripts/lane/lane-await.ts +5 -8
  319. package/scripts/lane/lane-baseline.ts +123 -0
  320. package/scripts/lane/lane-breakdown-cli.ts +64 -0
  321. package/scripts/lane/lane-breakdown-implement.ts +170 -0
  322. package/scripts/lane/lane-breakdown-laps.ts +107 -0
  323. package/scripts/lane/lane-breakdown-pinned.ts +76 -0
  324. package/scripts/lane/lane-breakdown-text.ts +145 -0
  325. package/scripts/lane/lane-breakdown.ts +239 -0
  326. package/scripts/lane/lane-check-stats.ts +29 -8
  327. package/scripts/lane/lane-child-invocation.ts +9 -2
  328. package/scripts/lane/lane-child-mark-keys.ts +12 -0
  329. package/scripts/lane/lane-child-marker.ts +20 -1
  330. package/scripts/lane/lane-ci-path-stats.ts +73 -0
  331. package/scripts/lane/lane-claim-stats.ts +84 -0
  332. package/scripts/lane/lane-close.ts +8 -3
  333. package/scripts/lane/lane-dispatch-log.ts +3 -3
  334. package/scripts/lane/lane-dispatch.ts +2 -2
  335. package/scripts/lane/lane-gap-stats.ts +7 -6
  336. package/scripts/lane/lane-guard-policy.ts +7 -1
  337. package/scripts/lane/lane-implement-stats.ts +207 -0
  338. package/scripts/lane/lane-implement-subagents.ts +90 -0
  339. package/scripts/lane/lane-investigation-switch.ts +16 -0
  340. package/scripts/lane/lane-ledger-kind.ts +31 -0
  341. package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
  342. package/scripts/lane/lane-ledger-schema.ts +119 -36
  343. package/scripts/lane/lane-ledger-write.ts +257 -0
  344. package/scripts/lane/lane-ledger.ts +31 -177
  345. package/scripts/lane/lane-load.ts +1 -1
  346. package/scripts/lane/lane-measure-cli.ts +176 -59
  347. package/scripts/lane/lane-net-read.ts +44 -0
  348. package/scripts/lane/lane-net-time.ts +212 -0
  349. package/scripts/lane/lane-open.ts +16 -7
  350. package/scripts/lane/lane-output.ts +2 -4
  351. package/scripts/lane/lane-overrun-prompt.ts +147 -0
  352. package/scripts/lane/lane-overrun.ts +274 -0
  353. package/scripts/lane/lane-paths.ts +24 -1
  354. package/scripts/lane/lane-pause-cli.ts +4 -1
  355. package/scripts/lane/lane-paused-spans.ts +97 -0
  356. package/scripts/lane/lane-plan-check.ts +207 -0
  357. package/scripts/lane/lane-plan-cli.ts +41 -0
  358. package/scripts/lane/lane-plan.ts +144 -0
  359. package/scripts/lane/lane-registry.ts +14 -1
  360. package/scripts/lane/lane-relaunch.ts +1 -1
  361. package/scripts/lane/lane-round-one-stats.ts +51 -0
  362. package/scripts/lane/lane-sampler.ts +23 -4
  363. package/scripts/lane/lane-session-action.ts +93 -18
  364. package/scripts/lane/lane-session-broker.ts +49 -21
  365. package/scripts/lane/lane-session-reread-main.ts +14 -0
  366. package/scripts/lane/lane-session-reread.ts +51 -0
  367. package/scripts/lane/lane-ship-lap-stats.ts +74 -0
  368. package/scripts/lane/lane-sleep.ts +58 -0
  369. package/scripts/lane/lane-spent.ts +1 -1
  370. package/scripts/lane/lane-split-stats.ts +28 -0
  371. package/scripts/lane/lane-stage-stats.ts +13 -1
  372. package/scripts/lane/lane-state.ts +41 -9
  373. package/scripts/lane/lane-stats-tables.ts +58 -0
  374. package/scripts/lane/lane-stats.ts +21 -5
  375. package/scripts/lane/lane-stop-stats.ts +51 -19
  376. package/scripts/lane/lane-tail-stats.ts +32 -0
  377. package/scripts/lane/lane-transcript-calls.ts +67 -10
  378. package/scripts/lane/lane-transcript-stats.ts +44 -12
  379. package/scripts/lane/lane-value-cell.ts +72 -0
  380. package/scripts/lane/lane-value.ts +118 -0
  381. package/scripts/lane/lane-withdraw-closed.ts +124 -0
  382. package/scripts/lane/lane-withdraw.ts +108 -31
  383. package/scripts/lane/lane-yield.ts +85 -0
  384. package/scripts/lane/openai-lane-ship.ts +12 -11
  385. package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
  386. package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
  387. package/scripts/lane/openai-lane-supervisor.ts +5 -4
  388. package/scripts/lib/poll.ts +38 -9
  389. package/scripts/lib/settled-together.ts +10 -0
  390. package/scripts/lib/shell-quote.ts +21 -0
  391. package/scripts/lib/token-shape.ts +111 -0
  392. package/scripts/lint/eslint-daemon-sources.ts +79 -0
  393. package/scripts/lint/eslint-daemon.ts +45 -6
  394. package/scripts/lint/eslint-warm-cli.ts +7 -0
  395. package/scripts/lint/eslint-warm.ts +66 -0
  396. package/scripts/lint/import-graph.ts +141 -11
  397. package/scripts/lint/lint-gate-scope.ts +2 -2
  398. package/scripts/lint/lint-parallel.ts +54 -6
  399. package/scripts/lint/lint-related.ts +154 -33
  400. package/scripts/lint/rename-cli.ts +179 -0
  401. package/scripts/lint/rename-identifier.ts +192 -0
  402. package/scripts/lint/type-only-imports.ts +58 -0
  403. package/scripts/notify/telegram-notify.ts +12 -3
  404. package/scripts/observations/observation-record.ts +42 -16
  405. package/scripts/observations/observations-flush.ts +32 -9
  406. package/scripts/overrides/overrides-check.ts +9 -1
  407. package/scripts/release/release-approval-prune.ts +7 -1
  408. package/scripts/repo/required-checks-logic.ts +4 -1
  409. package/scripts/repo/ruleset-check.ts +4 -3
  410. package/scripts/report/markdown-shape.ts +28 -0
  411. package/scripts/report/report-format-reference.ts +15 -1
  412. package/scripts/report/report-labels.ts +12 -1
  413. package/scripts/report/report-lint-child-summary.ts +94 -0
  414. package/scripts/report/report-lint-cli.ts +26 -7
  415. package/scripts/report/report-lint.ts +2 -20
  416. package/scripts/review/prose-stale-cli.ts +88 -0
  417. package/scripts/review/prose-stale-scan.ts +152 -0
  418. package/scripts/review/review-attest.ts +73 -23
  419. package/scripts/review/review-brief-cli.ts +68 -19
  420. package/scripts/review/review-brief-paths.ts +22 -0
  421. package/scripts/review/review-brief.ts +14 -7
  422. package/scripts/review/review-comment-carry.ts +47 -0
  423. package/scripts/review/review-comment-round.ts +85 -22
  424. package/scripts/review/review-finding-ledger.ts +109 -7
  425. package/scripts/review/review-finding-text.ts +43 -0
  426. package/scripts/review/review-interaction.ts +11 -0
  427. package/scripts/review/review-lane-handover.ts +68 -0
  428. package/scripts/review/review-lint-cli.ts +57 -0
  429. package/scripts/review/review-lint-findings.ts +92 -0
  430. package/scripts/review/review-lint-sections.ts +80 -0
  431. package/scripts/review/review-lint.ts +221 -0
  432. package/scripts/review/review-prior-findings.ts +102 -0
  433. package/scripts/review/review-readers-scan.ts +4 -2
  434. package/scripts/review/review-record-cli.ts +177 -54
  435. package/scripts/review/review-record-code-round.ts +55 -0
  436. package/scripts/review/review-record-comment.ts +60 -0
  437. package/scripts/review/review-record-interaction.ts +35 -0
  438. package/scripts/review/review-record-pinned.ts +108 -0
  439. package/scripts/review/review-record.ts +25 -1
  440. package/scripts/review/review-reviewed-tree.ts +307 -0
  441. package/scripts/review/review-round-one-closure.ts +62 -0
  442. package/scripts/review/review-round-reuse.ts +106 -0
  443. package/scripts/review/review-round2-cli.ts +91 -29
  444. package/scripts/review/review-round2-skip-comment.ts +55 -0
  445. package/scripts/review/review-round2.ts +24 -8
  446. package/scripts/review/review-stamps.ts +7 -3
  447. package/scripts/review/review-stop-log.ts +20 -6
  448. package/scripts/review/review-stops-cli.ts +2 -1
  449. package/scripts/review/review-submit-cli.ts +101 -0
  450. package/scripts/rules/agent-model.ts +1 -1
  451. package/scripts/rules/bash-triggers.ts +3 -3
  452. package/scripts/rules/concise-reply.ts +88 -0
  453. package/scripts/rules/credential-body.ts +77 -0
  454. package/scripts/rules/decision-oracle.ts +1 -1
  455. package/scripts/rules/delivered-rules.ts +36 -17
  456. package/scripts/rules/destructive-command.ts +3 -11
  457. package/scripts/rules/direct-issue-comment.ts +34 -12
  458. package/scripts/rules/direct-issue-read.ts +191 -0
  459. package/scripts/rules/direct-pr-create.ts +4 -24
  460. package/scripts/rules/early-heartbeat.ts +2 -0
  461. package/scripts/rules/edit-tool.ts +27 -0
  462. package/scripts/rules/file-edit-rows.ts +17 -0
  463. package/scripts/rules/file-rewrite.ts +146 -0
  464. package/scripts/rules/firing-point.ts +3 -2
  465. package/scripts/rules/gh-api.ts +128 -7
  466. package/scripts/rules/gh-repo-selector.ts +84 -0
  467. package/scripts/rules/git-force.ts +2 -2
  468. package/scripts/rules/guard-remedy.ts +14 -5
  469. package/scripts/rules/implementation-cut.ts +61 -28
  470. package/scripts/rules/investigator-foreground.ts +231 -0
  471. package/scripts/rules/issue-citation.ts +15 -60
  472. package/scripts/rules/issue-comments.ts +9 -4
  473. package/scripts/rules/josh-git-bare.ts +1 -1
  474. package/scripts/rules/lane-background.ts +41 -13
  475. package/scripts/rules/lane-direct-tool.ts +107 -9
  476. package/scripts/rules/lane-interactive-ask.ts +4 -1
  477. package/scripts/rules/lane-park.ts +4 -2
  478. package/scripts/rules/lane-spilled-read.ts +90 -0
  479. package/scripts/rules/lane-split-park.ts +1 -1
  480. package/scripts/rules/lane-switch-main.ts +4 -4
  481. package/scripts/rules/last-prompt.ts +20 -47
  482. package/scripts/rules/oracle-consulted.ts +1 -1
  483. package/scripts/rules/permission-guards.ts +5 -1
  484. package/scripts/rules/piped-verification-shapes.ts +110 -0
  485. package/scripts/rules/piped-verification.ts +67 -17
  486. package/scripts/rules/poll-loop.ts +72 -9
  487. package/scripts/rules/pr-merge.ts +5 -5
  488. package/scripts/rules/pre-gate-cut.ts +3 -3
  489. package/scripts/rules/protected-files.ts +88 -24
  490. package/scripts/rules/reply-language.ts +3 -17
  491. package/scripts/rules/reply-prose.ts +34 -0
  492. package/scripts/rules/repo-admin.ts +132 -0
  493. package/scripts/rules/reviewer-foreground.ts +88 -0
  494. package/scripts/rules/round-one-check-related.ts +120 -0
  495. package/scripts/rules/rule-body-guard.ts +5 -15
  496. package/scripts/rules/rule-value-cache.ts +53 -5
  497. package/scripts/rules/rule-value-candidates.ts +84 -0
  498. package/scripts/rules/rule-value-cli.ts +59 -11
  499. package/scripts/rules/rule-value.ts +17 -4
  500. package/scripts/rules/run-tail-rule.ts +1 -1
  501. package/scripts/rules/sed-in-place.ts +196 -0
  502. package/scripts/rules/shell-segments.ts +10 -2
  503. package/scripts/rules/stop-rule-rows.ts +1 -13
  504. package/scripts/rules/stop-rules.ts +33 -58
  505. package/scripts/rules/test-declared-commit.ts +1 -1
  506. package/scripts/rules/third-party-write.ts +110 -23
  507. package/scripts/rules/timed-rerun.ts +1 -1
  508. package/scripts/rules/turn-filings.ts +90 -0
  509. package/scripts/rules/worktree-guard.ts +22 -7
  510. package/scripts/run/board/run-board-ending-reason.ts +6 -2
  511. package/scripts/run/board/run-board-fit.ts +24 -1
  512. package/scripts/run/board/run-board-found.ts +134 -0
  513. package/scripts/run/board/run-board-header.ts +8 -0
  514. package/scripts/run/board/run-board-labels.ts +11 -1
  515. package/scripts/run/board/run-board-lane-time.ts +1 -0
  516. package/scripts/run/board/run-board-link.ts +15 -2
  517. package/scripts/run/board/run-board-notes.ts +7 -1
  518. package/scripts/run/board/run-board-operation.ts +4 -2
  519. package/scripts/run/board/run-board-phase.ts +2 -5
  520. package/scripts/run/board/run-board-plan.ts +1 -1
  521. package/scripts/run/board/run-board-read.ts +46 -1
  522. package/scripts/run/board/run-board-reason.ts +20 -8
  523. package/scripts/run/board/run-board-render-checks.ts +3 -2
  524. package/scripts/run/board/run-board-render-drawn.ts +11 -0
  525. package/scripts/run/board/run-board-render-found.ts +157 -0
  526. package/scripts/run/board/run-board-render-lead.ts +9 -0
  527. package/scripts/run/board/run-board-render-legend.ts +19 -0
  528. package/scripts/run/board/run-board-render-tail.ts +1 -1
  529. package/scripts/run/board/run-board-render.ts +82 -18
  530. package/scripts/run/board/run-board-spin.ts +1 -3
  531. package/scripts/run/board/run-board-status.ts +2 -0
  532. package/scripts/run/board/run-board-tick.ts +39 -10
  533. package/scripts/run/carry/run-carry-ended.ts +3 -2
  534. package/scripts/run/carry/run-carry.ts +3 -2
  535. package/scripts/run/cut/run-cut-cli.ts +33 -133
  536. package/scripts/run/cut/run-cut-digest.ts +152 -0
  537. package/scripts/run/cut/run-cut-handoff.ts +52 -8
  538. package/scripts/run/cut/run-cut-report.ts +24 -3
  539. package/scripts/run/cut/run-cut-resume.ts +129 -0
  540. package/scripts/run/cut/run-cut-review.ts +105 -0
  541. package/scripts/run/cut/run-cut.ts +18 -7
  542. package/scripts/run/detached-launch.ts +1 -1
  543. package/scripts/run/entry/run-entry-cli.ts +88 -19
  544. package/scripts/run/entry/run-entry-spill.ts +83 -0
  545. package/scripts/run/entry/run-entry-stop.ts +5 -1
  546. package/scripts/run/entry/run-entry-trust.ts +91 -0
  547. package/scripts/run/event/run-event-baseline.ts +56 -0
  548. package/scripts/run/event/run-event-filed.ts +9 -3
  549. package/scripts/run/event/run-event-overrun.ts +197 -0
  550. package/scripts/run/event/run-event-render.ts +4 -0
  551. package/scripts/run/event/run-event-stream.ts +25 -3
  552. package/scripts/run/hold/run-stop-notify.ts +3 -8
  553. package/scripts/run/merge/run-merge-cap.ts +57 -0
  554. package/scripts/run/merge/run-merge-cli.ts +66 -23
  555. package/scripts/run/merge/run-merge-outcome.ts +2 -0
  556. package/scripts/run/merge/run-merge-ship.ts +13 -3
  557. package/scripts/run/merge/run-merge-signals.ts +40 -23
  558. package/scripts/run/merge/run-merge-steps.ts +56 -36
  559. package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
  560. package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
  561. package/scripts/run/merge/run-merge.ts +41 -14
  562. package/scripts/run/review/run-review-cli.ts +306 -20
  563. package/scripts/run/review/run-review-early-push.ts +121 -0
  564. package/scripts/run/review/run-review-join-read.ts +94 -0
  565. package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
  566. package/scripts/run/review/run-review-pinned-join.ts +216 -0
  567. package/scripts/run/review/run-review-pinned-steps.ts +383 -0
  568. package/scripts/run/review/run-review-pinned-tree.ts +37 -0
  569. package/scripts/run/review/run-review-pinned.ts +53 -0
  570. package/scripts/run/review/run-review-steps.ts +70 -34
  571. package/scripts/run/review/run-review.ts +26 -6
  572. package/scripts/run/run-parent-cut-hook.ts +3 -4
  573. package/scripts/run/run-prep.ts +1 -0
  574. package/scripts/run/run-stage-read.ts +2 -6
  575. package/scripts/run/run-step-cli.ts +17 -5
  576. package/scripts/run/run-step-lane-plan.ts +27 -0
  577. package/scripts/run/run-step-section.ts +26 -7
  578. package/scripts/run/run-step.ts +5 -2
  579. package/scripts/run/ship/run-ship-args.ts +19 -0
  580. package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
  581. package/scripts/run/ship/run-ship-cli.ts +131 -39
  582. package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
  583. package/scripts/run/ship/run-ship-detach.ts +10 -4
  584. package/scripts/run/ship/run-ship-handover.ts +106 -0
  585. package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
  586. package/scripts/run/ship/run-ship-lap.ts +63 -0
  587. package/scripts/run/ship/run-ship-next.ts +27 -26
  588. package/scripts/run/ship/run-ship-overlap.ts +8 -3
  589. package/scripts/run/ship/run-ship-place.ts +9 -2
  590. package/scripts/run/ship/run-ship-planned.ts +92 -0
  591. package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
  592. package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
  593. package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
  594. package/scripts/run/ship/run-ship-preflight.ts +16 -4
  595. package/scripts/run/ship/run-ship-probe.ts +23 -9
  596. package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
  597. package/scripts/run/ship/run-ship-relaunch.ts +51 -0
  598. package/scripts/run/ship/run-ship-return.ts +103 -29
  599. package/scripts/run/ship/run-ship-review-reason.ts +24 -0
  600. package/scripts/run/ship/run-ship-review-steps.ts +230 -59
  601. package/scripts/run/ship/run-ship-review.ts +171 -51
  602. package/scripts/run/ship/run-ship-reviewer.ts +138 -42
  603. package/scripts/run/ship/run-ship-scoped.ts +39 -10
  604. package/scripts/run/ship/run-ship-stage.ts +49 -13
  605. package/scripts/run/ship/run-ship-steps.ts +24 -4
  606. package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
  607. package/scripts/run/ship/run-ship-stop-context.ts +50 -13
  608. package/scripts/run/ship/run-ship-sync.ts +171 -30
  609. package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
  610. package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
  611. package/scripts/run/ship/run-ship-waiter.ts +114 -0
  612. package/scripts/run/ship/run-ship.ts +16 -2
  613. package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
  614. package/scripts/run/wake/run-wake-cli.ts +6 -113
  615. package/scripts/run/wake/run-wake-code.ts +53 -11
  616. package/scripts/run/wake/run-wake-driver.ts +5 -3
  617. package/scripts/run/wake/run-wake-finish.ts +137 -0
  618. package/scripts/run/wake/run-wake-loop.ts +5 -3
  619. package/scripts/run/wake/run-wake-ports.ts +11 -3
  620. package/scripts/run/wake/run-wake-session.ts +2 -0
  621. package/scripts/run/wake/run-wake.ts +3 -2
  622. package/scripts/safe-chain/ci-installer-pin.ts +49 -8
  623. package/scripts/safe-chain/project-config.ts +3 -2
  624. package/scripts/security/secretlint-scan.ts +42 -9
  625. package/scripts/security/security-audit-provision-main.ts +9 -1
  626. package/scripts/security/security-audit-provision.ts +1 -1
  627. package/scripts/security/security-audit.ts +21 -1
  628. package/scripts/security/sonar-file.ts +10 -5
  629. package/scripts/split/split-assess.ts +12 -3
  630. package/scripts/split/split-early-notice.ts +116 -0
  631. package/scripts/split/split-measure.ts +20 -2
  632. package/scripts/split/split-resolution.ts +107 -0
  633. package/scripts/test/direct-importers.ts +185 -0
  634. package/scripts/test/pure-files-entries.ts +83 -0
  635. package/scripts/test/related-heavy-tests.ts +81 -0
  636. package/scripts/test/test-no-base.ts +88 -0
  637. package/scripts/test/test-pinned-cache-logic.ts +363 -0
  638. package/scripts/test/test-pinned-cache.ts +337 -0
  639. package/scripts/test/test-pinned-emit.ts +49 -0
  640. package/scripts/test/test-pinned-functions.ts +116 -0
  641. package/scripts/test/test-pinned-ledger.ts +80 -0
  642. package/scripts/test/test-pinned-logic.ts +208 -20
  643. package/scripts/test/test-pinned-main.ts +84 -18
  644. package/scripts/test/test-pinned-pool.ts +73 -0
  645. package/scripts/test/test-pinned-probe.ts +254 -0
  646. package/scripts/test/test-pinned-prune-logic.ts +118 -0
  647. package/scripts/test/test-pinned-reach.ts +194 -0
  648. package/scripts/test/test-pinned-record.ts +196 -0
  649. package/scripts/test/test-pinned-rewire.ts +148 -0
  650. package/scripts/test/test-pinned-spawn.ts +81 -0
  651. package/scripts/test/test-pinned-stage.ts +70 -0
  652. package/scripts/test/test-pinned.ts +258 -47
  653. package/scripts/test/test-reach-cli.ts +61 -0
  654. package/scripts/test/test-read-map.ts +35 -11
  655. package/scripts/test/test-read-recording.ts +17 -5
  656. package/scripts/test/test-read-trace.ts +26 -8
  657. package/scripts/test/test-red-logic.ts +46 -7
  658. package/scripts/test/test-red.ts +34 -14
  659. package/scripts/test/test-related-claim.ts +37 -0
  660. package/scripts/test/test-related-main.ts +4 -1
  661. package/scripts/test/test-related-moved.ts +91 -0
  662. package/scripts/test/test-related-record.ts +43 -23
  663. package/scripts/test/test-related-scope.ts +99 -34
  664. package/scripts/test/test-related-synced.ts +135 -0
  665. package/scripts/test/test-related-touched.ts +84 -0
  666. package/scripts/test/test-related.ts +167 -83
  667. package/scripts/test/test-unit-guard.ts +28 -5
  668. package/scripts/test/unit-durations.ts +41 -0
  669. package/scripts/test/unit-worker-share.ts +5 -4
  670. package/scripts/test/vitest-cache-directory.ts +19 -0
  671. package/scripts/test/vitest-cli-excludes.ts +14 -0
  672. package/scripts/test/vitest-failure-summary.ts +111 -0
  673. package/scripts/test/whole-suite-files.ts +12 -1
  674. package/scripts/time-runtime/time-batch-guard.ts +42 -4
  675. package/scripts/time-runtime/time-bundle-call.ts +1 -1
  676. package/scripts/time-runtime/time-bundles.ts +36 -5
  677. package/scripts/time-runtime/time-markers.ts +2 -2
  678. package/scripts/time-runtime/time-search-call.ts +38 -0
  679. package/scripts/time-runtime/time-shell.ts +1 -1
  680. package/scripts/time-runtime/time-transcript-line.ts +17 -5
  681. package/scripts/version/development-engines-verdict.ts +124 -0
  682. package/scripts/version/latest-corepack.ts +2 -0
  683. package/templates/gitignore +2 -0
  684. package/templates/workflows/ci.yml +41 -0
  685. package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
  686. package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
  687. package/dist/hooks/chunk-TUIFS5E7.js +0 -72
  688. package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
  689. package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
  690. package/scripts/rules/filing-cap.ts +0 -139
@@ -0,0 +1,60 @@
1
+ import { gate_labels } from './gate-labels'
2
+ import type { GateCheck } from './gate-plan'
3
+
4
+ // **CI runs the static set as three parallel legs, and each leg is still `josh gate`**
5
+ // (joshuafolkken/kit#5215). One runner ran all seven checks for about four minutes while every unit
6
+ // shard was done in two, so `static-checks` was the merge's critical path. The legs are a matrix over
7
+ // the names below, each running `pnpm josh gate --no-unit --static-group=<name>`, so CI keeps the
8
+ // gate's one definition of every check and its all-failures-in-one-pass summary rather than restating
9
+ // the commands per job. `gate-static-group.test.ts` pins the partition: every static check is in
10
+ // exactly one group, and the workflow's matrix names exactly these groups.
11
+ //
12
+ // The split follows the weights: lint alone (the cold-cache outlier), the type check with the
13
+ // unused-export walk (both build a TypeScript program), and the light rest together.
14
+ const STATIC_GROUP_FLAG_PREFIX = '--static-group='
15
+
16
+ const STATIC_GROUPS: Readonly<Record<string, ReadonlyArray<string>>> = {
17
+ lint: [gate_labels.LINT_LABEL],
18
+ types: [gate_labels.TYPE_CHECK_LABEL, gate_labels.EXPORTS_LABEL],
19
+ rest: [
20
+ gate_labels.SPELL_CHECK_LABEL,
21
+ gate_labels.BEHAVIOR_LABEL,
22
+ gate_labels.METRICS_LABEL,
23
+ gate_labels.LOCKFILE_LABEL,
24
+ ],
25
+ }
26
+
27
+ // The group an argument names, or `undefined` when it is not the flag or names no group — so an
28
+ // unknown group is refused with every other unknown argument.
29
+ function group_of(argument: string): string | undefined {
30
+ if (!argument.startsWith(STATIC_GROUP_FLAG_PREFIX)) return undefined
31
+
32
+ const name = argument.slice(STATIC_GROUP_FLAG_PREFIX.length)
33
+
34
+ return Object.hasOwn(STATIC_GROUPS, name) ? name : undefined
35
+ }
36
+
37
+ function find_group(arguments_: ReadonlyArray<string>): string | undefined {
38
+ return arguments_.map((argument) => group_of(argument)).find((name) => name !== undefined)
39
+ }
40
+
41
+ function narrow_checks(
42
+ checks: ReadonlyArray<GateCheck>,
43
+ group: string | undefined,
44
+ ): ReadonlyArray<GateCheck> {
45
+ if (group === undefined) return checks
46
+
47
+ const labels = STATIC_GROUPS[group] ?? []
48
+
49
+ return checks.filter((check) => labels.includes(check.label))
50
+ }
51
+
52
+ const gate_static_group = {
53
+ STATIC_GROUPS,
54
+ STATIC_GROUP_FLAG_PREFIX,
55
+ find_group,
56
+ group_of,
57
+ narrow_checks,
58
+ }
59
+
60
+ export { gate_static_group }
@@ -1,10 +1,11 @@
1
+ import { gate_labels } from './gate-labels'
1
2
  import { gate_plan, type GateCheck, type GatePlan } from './gate-plan'
2
3
  import type { GateStep } from './gate-report'
3
4
  import { project_checks } from './project-checks'
4
5
  import { type_check_step } from './type-check-step'
5
6
 
6
7
  const JOSH = 'josh'
7
- const { TYPE_CHECK_LABEL, UNIT_LABEL } = gate_plan
8
+ const { TYPE_CHECK_LABEL, UNIT_LABEL } = gate_labels
8
9
  const UNIT_WORKER_FLAG = '--maxWorkers'
9
10
 
10
11
  function unit_worker_args(check: GateCheck, plan: GatePlan): ReadonlyArray<string> {
@@ -30,9 +30,11 @@ async function read_changed_files(): Promise<Record<string, string>> {
30
30
  }
31
31
 
32
32
  // The resolver lives in `git/change-base.ts`, because the review brief
33
- // needs exactly this reading and a second copy of it is the clone `CLAUDE.md` prohibits.
33
+ // needs exactly this reading and a second copy of it is the clone `CLAUDE.md` prohibits. It is read
34
+ // through a merge in progress, as the file map is (joshuafolkken/kit#5307): a map measured from the
35
+ // later base beside `HEAD`'s older one would let a re-merged default branch reuse a stale record.
34
36
  async function read_base(): Promise<string | undefined> {
35
- return await change_base.resolved()
37
+ return await change_base.resolved_through_merge()
36
38
  }
37
39
 
38
40
  // The two readings are independent, so they are started together rather than one after the other.
@@ -103,6 +103,12 @@ interface HookReuse {
103
103
  source?: string | undefined
104
104
  }
105
105
 
106
+ const ARGUMENTS_MISS = 'arguments were passed to the hook'
107
+
108
+ function force_miss(force_environment: string): string {
109
+ return `${force_environment} is set`
110
+ }
111
+
106
112
  // The stamp rather than a boolean, for the same reason `gate_skip.gate_reuse` hands one back: the
107
113
  // caller prints `taken_at`, and a record that does not describe this tree has no timestamp worth
108
114
  // printing. A refusal carries its reason instead.
@@ -111,19 +117,30 @@ interface HookReuse {
111
117
  // asked for that run rather than for a recorded result about a whole tree, and a hook's own line passes
112
118
  // none, so this costs it nothing.
113
119
  function hook_reuse(input: HookReuse): GateReuse {
114
- if (input.extra_arguments.length > 0) return { miss: 'arguments were passed to the hook' }
115
- if (is_force_requested(input.force_env)) return { miss: `${input.force_env} is set` }
120
+ if (input.extra_arguments.length > 0) return { miss: ARGUMENTS_MISS }
121
+ if (is_force_requested(input.force_env)) return { miss: force_miss(input.force_env) }
116
122
  if (!input.is_tree_carried) return { miss: input.carry_miss }
117
123
 
118
124
  return gate_skip.gate_reuse(input.tree.files, input.tree.base, input.source)
119
125
  }
120
126
 
127
+ // joshuafolkken/kit#5234: the kind of a refusal `hook_reuse` answered, the hook's own three refusals
128
+ // told apart before the gate's, so a ledger counts each without a second wording of the sentences.
129
+ function miss_kind(miss: string, input: Pick<HookReuse, 'carry_miss' | 'force_env'>): string {
130
+ if (miss === ARGUMENTS_MISS) return 'arguments'
131
+ if (miss === force_miss(input.force_env)) return 'forced'
132
+ if (miss === input.carry_miss) return 'tree-not-carried'
133
+
134
+ return gate_skip.miss_kind(miss)
135
+ }
136
+
121
137
  const hook_gate_reuse = {
122
138
  GATE_REUSING_TARGETS,
123
139
  hook_reuse,
124
140
  is_force_requested,
125
141
  is_index_matching_worktree,
126
142
  is_worktree_clean,
143
+ miss_kind,
127
144
  read_status_lines,
128
145
  }
129
146
 
@@ -0,0 +1,59 @@
1
+ import { buffered_process } from '#scripts/lib/buffered-process'
2
+ import { release_approval_prune } from '#scripts/release/release-approval-prune'
3
+ import { project_checks } from './project-checks'
4
+
5
+ // kit's metrics ratchet totals (`josh metrics --totals-only`), brought forward from the detached
6
+ // `josh ship`'s pre-detach checks to the implementation loop: a feature grows a total almost every
7
+ // time, and a growth first met at the pre-detach stop costs a ship re-run beside the
8
+ // `josh metrics --accept` (joshuafolkken/kit#4594). `josh lint:related` reports it as a notice, never a
9
+ // failure, so a total that grows again within the loop does not demand an accept per edit; the
10
+ // pre-detach check stays the stop. A consumer's kit has no `josh metrics`, so it runs in kit alone.
11
+
12
+ const METRICS_TOTALS_COMMAND: ReadonlyArray<string> = ['josh', 'metrics', '--totals-only']
13
+ // The form ship runs before the detach and before each scoped pair: it also re-records a growth the
14
+ // branch's approval names (joshuafolkken/kit#4716).
15
+ const METRICS_RERECORD_COMMAND: ReadonlyArray<string> = [...METRICS_TOTALS_COMMAND, '--rerecord']
16
+ // Neutral on the cause: a growth and a run that never finished both stop the pre-detach check.
17
+ const NOTICE_HEADING =
18
+ '⚠ josh metrics --totals-only did not pass — settle it before `josh ship`, or the pre-detach check stops the ship (a total the branch approval names is re-recorded there):'
19
+
20
+ async function growth_notice(directory: string): Promise<string | undefined> {
21
+ if (!project_checks.is_kit_repository(directory)) return undefined
22
+
23
+ const result = await buffered_process.run_buffered_process(METRICS_TOTALS_COMMAND, {
24
+ cwd: directory,
25
+ })
26
+
27
+ if (!buffered_process.is_process_failed(result)) return undefined
28
+
29
+ return `${NOTICE_HEADING}\n${result.output.trim()}`
30
+ }
31
+
32
+ function carries_approval(files: Readonly<Record<string, string>>): boolean {
33
+ return Object.keys(files).some((file) => release_approval_prune.is_approval(file))
34
+ }
35
+
36
+ // Re-records the branch's approval before a scoped pair reads the tree, so a review fix that grows a
37
+ // named total again does not stop the gate after it (joshuafolkken/kit#4716). It runs only where the
38
+ // changed tree carries an approval, and answers whether it ran; its verdict is left to the gate's own
39
+ // metrics step.
40
+ async function rerecord(
41
+ directory: string,
42
+ files: Readonly<Record<string, string>>,
43
+ ): Promise<boolean> {
44
+ if (!project_checks.is_kit_repository(directory) || !carries_approval(files)) return false
45
+
46
+ await buffered_process.run_buffered_process(METRICS_RERECORD_COMMAND, { cwd: directory })
47
+
48
+ return true
49
+ }
50
+
51
+ const metrics_totals = {
52
+ METRICS_RERECORD_COMMAND,
53
+ METRICS_TOTALS_COMMAND,
54
+ NOTICE_HEADING,
55
+ growth_notice,
56
+ rerecord,
57
+ }
58
+
59
+ export { metrics_totals }
@@ -1,6 +1,6 @@
1
1
  import { file_map_stamp, type FileMapStampAccess } from '#scripts/josh/file-map-stamp'
2
2
  import { hook_decision } from '#scripts/josh/hook-decision'
3
- import { review_brief } from '#scripts/review/review-brief'
3
+ import { run_record_path } from '#scripts/josh/run-record-path'
4
4
  import { review_stamps } from '#scripts/review/review-stamps'
5
5
  import { test_unit_guard } from '#scripts/test/test-unit-guard'
6
6
  import { gate_tree, type GateTree } from './gate-tree'
@@ -12,16 +12,20 @@ import { gate_tree, type GateTree } from './gate-tree'
12
12
  // tree neither check has read return their findings one round trip at a time; one call running both
13
13
  // checks first returns most of them at once.
14
14
  //
15
- // **It fixes when the checks run, never whether they run.** Nothing here skips a check or
15
+ // **It fixes when the checks run, and only one writer decides whether.** Nothing here skips a check or
16
16
  // reinterprets its output, and `josh gate` still runs its four checks exactly as before. The one other
17
17
  // reader is the checks' own re-run, which narrows to what moved since this record (`since-green.ts`).
18
+ // The one writer without a run behind it is `josh ship`'s scoped pair after a merge with the default
19
+ // branch that shares nothing with the branch (`run-ship-synced-scope.ts`, joshuafolkken/kit#4985).
18
20
  //
19
21
  // **The judgement is the gate's own, not a second one.** "There is a record and nothing it covers has
20
- // moved" is `review_brief.matching_stamp` plus the base equality `gate_skip.reusable_green_gate`
22
+ // moved" is `file_map_stamp.changed_since` plus the base equality `gate_skip.reusable_green_gate`
21
23
  // already compares and the inputs fingerprint each record carries (`verification-inputs.ts`), over the
22
- // same `FileMapStamp` shape written to the same kind of file. A second
23
- // definition of "this tree is unchanged" is the clone `CLAUDE.md` prohibits, and it is exactly where
24
- // two commands would start disagreeing about one working tree.
24
+ // same `FileMapStamp` shape written to the same kind of file. A second definition of "this tree is
25
+ // unchanged" is the clone `CLAUDE.md` prohibits, and it is exactly where two commands would start
26
+ // disagreeing about one working tree. The one deliberate difference: this reader ignores josh's own
27
+ // `.josh/` run records, while the gate's own reuse (`gate_skip` via `review_brief.matching_stamp`)
28
+ // does not — safe, because the gate still lints and tests those records.
25
29
  //
26
30
  // **It refuses `josh review:brief`, and now the local `josh gate` too, but never CI's**.
27
31
  // The brief was the first refusal point because it is where `review:attest`
@@ -56,11 +60,13 @@ interface ScopedCheck {
56
60
  // The record paths, overridable so a test can plant one without overwriting the record a real run in
57
61
  // this checkout relies on — the same reason `gate_skip.reusable_green_gate` takes a `source`.
58
62
  // `is_unit_skipped` is overridable for the same reason, so a suite running in a checkout with vitest
59
- // can stand in for a project without one.
63
+ // can stand in for a project without one. `is_lane_child` is the gate's answer for a dispatched lane
64
+ // child, absent for every other caller.
60
65
  interface ScopedSources {
61
66
  lint?: string
62
67
  test?: string
63
68
  is_unit_skipped?: boolean
69
+ is_lane_child?: boolean
64
70
  }
65
71
 
66
72
  // **`test:related` is required only where it can ever answer**. In a project
@@ -88,15 +94,26 @@ function checks_of(sources: ScopedSources): ReadonlyArray<ScopedCheck> {
88
94
  : [lint, test]
89
95
  }
90
96
 
97
+ // **A record josh itself keeps under `.josh/` does not unseat a green record** (joshuafolkken/kit#4905):
98
+ // `metrics --accept` and the ship's own approval re-record write there after the pair passed, and the
99
+ // preflight re-ran both checks over nothing but that bookkeeping. The gate still lints and tests the
100
+ // whole change, and a later scoped run reads such a file as moved since its record.
101
+
91
102
  // **An unreadable record reads as an absent one, and that is the safe direction.** `read` swallows a
92
103
  // malformed or foreign file and answers `undefined`, so the two cases are indistinguishable here —
93
104
  // and the consequence of treating one as the other is a single 14-second scoped run, against the
94
105
  // alternative of briefing a review on a tree nothing has checked.
95
- function describes_tree(check: ScopedCheck, tree: Record<string, string>, base: string): boolean {
96
- const stamp = review_brief.matching_stamp(check.stamp.read(check.source), tree)
106
+ function describes_tree(
107
+ check: ScopedCheck,
108
+ tree: Record<string, string>,
109
+ base: string,
110
+ is_overlooked: (relative: string) => boolean = run_record_path.is_run_record,
111
+ ): boolean {
112
+ const stamp = check.stamp.read(check.source)
97
113
 
98
114
  return (
99
115
  stamp !== undefined &&
116
+ file_map_stamp.changed_since(stamp, tree).every((relative) => is_overlooked(relative)) &&
100
117
  file_map_stamp.describes_base(stamp, base) &&
101
118
  check.stamp.describes_inputs(stamp)
102
119
  )
@@ -151,25 +168,60 @@ function missing_scripts(
151
168
  return stale_checks(tree, base, sources).map((check) => check.script)
152
169
  }
153
170
 
171
+ // The scripts whose green record still describes this tree, which `check:related` need not run again
172
+ // (joshuafolkken/kit#5156). A tree with no answer reuses nothing, never reading silence as green. A
173
+ // moved `.josh/` record counts here: the gate is not behind `check:related` to lint and test it.
174
+ function overlooks_nothing(): boolean {
175
+ return false
176
+ }
177
+
178
+ function green_scripts(
179
+ tree: Record<string, string>,
180
+ base: string | undefined,
181
+ sources: ScopedSources = {},
182
+ ): ReadonlyArray<string> {
183
+ if (!is_answerable(tree, base)) return []
184
+
185
+ return checks_of(sources)
186
+ .filter((check) => describes_tree(check, tree, base, overlooks_nothing))
187
+ .map((check) => check.script)
188
+ }
189
+
154
190
  const REFUSAL_HEADLINE = '⛔ scoped checks not green on this tree'
155
191
 
156
192
  // **The way out travels with the refusal.** A message saying only "not verified" leaves the caller
157
193
  // holding the same question and no sanctioned answer, which is how the checks ended up after the
158
194
  // review in the first place. So the text names the one command that closes it, what it costs, and
159
- // that reissuing this call is the expected next move.
160
- function refusal(missing: ReadonlyArray<string>): string {
195
+ // the expected next move: reissuing this call, or for a lane child's gate, handing over to ship.
196
+ const REISSUE_STEP = 'and reissue this command.'
197
+
198
+ // joshuafolkken/kit#4776: a lane child that reissued the gate here ran it in the foreground for three
199
+ // minutes before `ship`, which runs its own gate beside the review, so that green was never used.
200
+ const LANE_CHILD_STEP =
201
+ 'then hand over to `pnpm josh ship --detach --review "<title> #<N>"` rather than reissuing the ' +
202
+ 'gate — this is a dispatched lane child, and ship runs the gate beside the review ' +
203
+ '(`.claude/skills/workflow-commands/chain-rule.md` step 0).'
204
+
205
+ function refusal(missing: ReadonlyArray<string>, next_step: string): string {
161
206
  return (
162
207
  `${REFUSAL_HEADLINE}: ${missing.join(' and ')} ` +
163
208
  'have not passed on the files this branch changed. The gate and review round 1 start together, ' +
164
209
  'so a brief composed now describes a tree neither check has read — joshuafolkken/kit#1503 spent ' +
165
210
  '282 seconds, 31% of the run, picking up one lint rule and one failing test at a time after its ' +
166
211
  'review had already started. Run them in one call — ' +
167
- `\`${LINT_COMMAND} && ${TEST_COMMAND}\`, measured at 13.8 seconds — and reissue this command. ` +
168
- 'The record fixes when the checks run and never whether they run: nothing is skipped or reused ' +
169
- 'because of it. `prompts/review.md` → "The scoped checks answer on the last edit".'
212
+ `\`${LINT_COMMAND} && ${TEST_COMMAND}\`, measured at 13.8 seconds — ${next_step} ` +
213
+ 'The record fixes when the checks run: only a merge with the default branch sharing nothing ' +
214
+ "with this branch's change is recorded without a run. " +
215
+ '`.claude/skills/workflow-commands/chain-rule.md` → "Orchestration facts single-sourced here".'
170
216
  )
171
217
  }
172
218
 
219
+ // **Only the gate's pre-check sets `is_lane_child`**: a lane child refused at `josh run:review` reissues
220
+ // it as chain-rule step 0 says, so `review:brief`'s callers keep the reissue wording.
221
+ function next_step_of(sources: ScopedSources): string {
222
+ return sources.is_lane_child === true ? LANE_CHILD_STEP : REISSUE_STEP
223
+ }
224
+
173
225
  function refusal_for(
174
226
  tree: Record<string, string>,
175
227
  base: string | undefined,
@@ -177,7 +229,7 @@ function refusal_for(
177
229
  ): string | undefined {
178
230
  const missing = missing_checks(tree, base, sources)
179
231
 
180
- return missing.length === 0 ? undefined : refusal(missing)
232
+ return missing.length === 0 ? undefined : refusal(missing, next_step_of(sources))
181
233
  }
182
234
 
183
235
  // **A record is written only by a bare invocation, and any argument at all withholds it.** A path
@@ -254,6 +306,7 @@ const scoped_green = {
254
306
  SWITCH_ENV_KEY,
255
307
  TEST_COMMAND,
256
308
  TEST_SCRIPT,
309
+ green_scripts,
257
310
  is_recordable_scope,
258
311
  is_unmoved,
259
312
  missing_checks,
@@ -1,5 +1,5 @@
1
1
  import type { FileMapStampAccess } from '#scripts/josh/file-map-stamp'
2
- import { gate_tree } from './gate-tree'
2
+ import { gate_tree, type GateTree } from './gate-tree'
3
3
  import { moved_paths, type MovedParts } from './moved-paths'
4
4
  import { verification_inputs } from './verification-inputs'
5
5
 
@@ -11,7 +11,9 @@ import { verification_inputs } from './verification-inputs'
11
11
  // **The check is no weaker.** The green record (`scoped-green.ts`) vouches that the check passed over
12
12
  // the branch diff of the tree it names; what moved since is every path whose content can differ from
13
13
  // that tree (`moved-paths.ts`), a main sync's own changes included. A record written by a narrowed run
14
- // vouches for the same thing, by the same argument one run earlier.
14
+ // vouches for the same thing, by the same argument one run earlier — except a bare `josh test:related`
15
+ // over a file every test imports, which ran only its direct importers, so the gate's unit leg never
16
+ // narrows by it while the branch changes one (`is_green_narrowable` in `test-related.ts`, #5122).
15
17
  //
16
18
  // **Whatever cannot be narrowed answers `undefined`**, and the caller reads the whole branch change as
17
19
  // before: no record, one that cannot be compared, one written under other tools or environment, or a
@@ -19,6 +21,9 @@ import { verification_inputs } from './verification-inputs'
19
21
  // says nothing under the new one. An empty list is
20
22
  // the positive fact that nothing moved: the gate's unit step on the tree the preflight just passed.
21
23
 
24
+ // Why a command that found nothing moved ran nothing, in the words both scoped commands print.
25
+ const UNMOVED_REASON = 'nothing moved since its last green run on this checkout'
26
+
22
27
  // The narrowing line each command prints before it runs, so a since-green run is never read as one
23
28
  // over the whole branch.
24
29
  function describe(label: string, moved: ReadonlyArray<string>): string {
@@ -31,17 +36,19 @@ function is_narrowable(label: string, parts: MovedParts | undefined): parts is M
31
36
 
32
37
  // What moved with a main sync's part kept apart, for a caller that records how much of its input the
33
38
  // sync brought in (`josh test:related`, joshuafolkken/kit#4465). `label` names the gate check whose
34
- // declaration (`verification-inputs.ts`) the record vouches for.
39
+ // declaration (`verification-inputs.ts`) the record vouches for. `tree` is the caller's own reading of
40
+ // the gate tree, so a run that also records its green tree reads it once (joshuafolkken/kit#4655).
35
41
  async function read_moved_parts(
36
42
  stamp: FileMapStampAccess,
37
43
  label: string,
38
44
  source?: string,
45
+ tree?: GateTree,
39
46
  ): Promise<MovedParts | undefined> {
40
47
  const record = stamp.read(source)
41
48
 
42
49
  if (record === undefined || !stamp.describes_inputs(record)) return undefined
43
50
 
44
- const parts = await moved_paths.moved_parts(record, await gate_tree.read_gate_tree())
51
+ const parts = await moved_paths.moved_parts(record, tree ?? (await gate_tree.read_gate_tree()))
45
52
 
46
53
  return is_narrowable(label, parts) ? parts : undefined
47
54
  }
@@ -51,12 +58,13 @@ async function read_moved(
51
58
  stamp: FileMapStampAccess,
52
59
  label: string,
53
60
  source?: string,
61
+ tree?: GateTree,
54
62
  ): Promise<ReadonlyArray<string> | undefined> {
55
- const parts = await read_moved_parts(stamp, label, source)
63
+ const parts = await read_moved_parts(stamp, label, source, tree)
56
64
 
57
65
  return parts === undefined ? undefined : moved_paths.union_of(parts)
58
66
  }
59
67
 
60
- const since_green = { describe, read_moved, read_moved_parts }
68
+ const since_green = { UNMOVED_REASON, describe, read_moved, read_moved_parts }
61
69
 
62
70
  export { since_green }
@@ -0,0 +1,96 @@
1
+ import { project_checks } from '#scripts/gate/project-checks'
2
+ import type { ChangedFileScope } from '#scripts/git/changed-file-scope'
3
+ import { buffered_process, type BufferedProcessResult } from '#scripts/lib/buffered-process'
4
+
5
+ // The guard tests that judge source text alone, yet run only in `josh test:related` or a detached
6
+ // ship's pre-detach stop: a bare Issue citation, a source with no colocated test and a `gh` spawn
7
+ // outside `gh api` (the static half of the network guard; the calls a test *makes* are observed by
8
+ // `test-network-guard.ts`, which only a run can answer). `josh lint:related` runs them beside its
9
+ // lint, so a between-edits loop meets them in seconds rather than at the ship (joshuafolkken/kit#4643).
10
+ // The targets are the pre-detach stop's too, so the two never drift.
11
+ //
12
+ // `josh exports:unused` stays out: its whole-program type-checker pass is several times the lint's
13
+ // own wall clock. `josh lint:related` runs its narrowed members check after a green lint instead
14
+ // (joshuafolkken/kit#4645).
15
+ //
16
+ // They run in kit alone: the guard tests are kit's own, and a consumer meets the pre-detach static
17
+ // checks there. A change with no `.ts` file in it moves none of their verdicts.
18
+
19
+ // A present directory need not hold a test the project's Vitest includes — a consumer's own
20
+ // `scripts/claude` of shell helpers, say — and a filter that matches nothing would stop every ship.
21
+ const GUARD_TEST_COMMAND: ReadonlyArray<string> = ['josh', 'test:unit', '--passWithNoTests']
22
+ const STRUCTURAL_GUARD_TARGETS: ReadonlyArray<string> = [
23
+ 'scripts/issue/session-cite-scan.test.ts',
24
+ 'tools/test/untested-sources.test.ts',
25
+ 'tools/gh/gh-subcommand-guard.test.ts',
26
+ ]
27
+ const STRUCTURAL_LISTS: ReadonlyArray<ReadonlyArray<string>> = [
28
+ [...GUARD_TEST_COMMAND, ...STRUCTURAL_GUARD_TARGETS],
29
+ ]
30
+ const TYPESCRIPT_SUFFIX = '.ts'
31
+ const FAILURE_EXIT_CODE = 1
32
+
33
+ interface StructuralRun {
34
+ list: ReadonlyArray<string>
35
+ result: BufferedProcessResult
36
+ }
37
+
38
+ // The front of a failed check's heading — `josh lane:value metrics-stops` counts a ship output that
39
+ // carries the metrics totals' one.
40
+ function failure_prefix(list: ReadonlyArray<string>): string {
41
+ return `✗ ${list.join(' ')} — `
42
+ }
43
+
44
+ // Each failure is headed by the check's own command and how it ended: a check that failed with no
45
+ // output of its own (`ELIFECYCLE Command failed` alone, or nothing) otherwise names nothing to fix.
46
+ function failure_heading(list: ReadonlyArray<string>, result: BufferedProcessResult): string {
47
+ const ending =
48
+ result.exit_code === undefined
49
+ ? 'no exit code (timed out or ended by a signal)'
50
+ : `exit code ${String(result.exit_code)}`
51
+
52
+ return `${failure_prefix(list)}${ending}`
53
+ }
54
+
55
+ function failure_section(list: ReadonlyArray<string>, result: BufferedProcessResult): string {
56
+ const parts = [failure_heading(list, result), result.output.trim()]
57
+
58
+ return parts.filter((part) => part !== '').join('\n')
59
+ }
60
+
61
+ // The whole-tree fallback is due as the narrowed run with a source in it is; both stay kit-only.
62
+ function is_due(scope: ChangedFileScope, root: string): boolean {
63
+ if (!project_checks.is_kit_repository(root)) return false
64
+
65
+ return scope.mode === 'all' || scope.files.some((file) => file.endsWith(TYPESCRIPT_SUFFIX))
66
+ }
67
+
68
+ async function run_in(root: string, list: ReadonlyArray<string>): Promise<StructuralRun> {
69
+ return { list, result: await buffered_process.run_buffered_process(list, { cwd: root }) }
70
+ }
71
+
72
+ // All at once and buffered, every failure reported, so one run shows all there is to fix.
73
+ async function run_structural(scope: ChangedFileScope, root: string): Promise<number> {
74
+ if (!is_due(scope, root)) return 0
75
+
76
+ const runs = await Promise.all(STRUCTURAL_LISTS.map(async (list) => await run_in(root, list)))
77
+ const failed = runs.filter(({ result }) => buffered_process.is_process_failed(result))
78
+
79
+ if (failed.length === 0) return 0
80
+
81
+ const sections = failed.map(({ list, result }) => failure_section(list, result))
82
+
83
+ process.stdout.write(`\n${sections.join('\n\n')}\n`)
84
+
85
+ return FAILURE_EXIT_CODE
86
+ }
87
+
88
+ const structural_checks = {
89
+ GUARD_TEST_COMMAND,
90
+ STRUCTURAL_GUARD_TARGETS,
91
+ failure_prefix,
92
+ failure_section,
93
+ run_structural,
94
+ }
95
+
96
+ export { structural_checks }
@@ -3,7 +3,8 @@ import { fileURLToPath } from 'node:url'
3
3
  import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
4
4
  import { GATE_COMMAND } from '#scripts/josh/josh-command-types'
5
5
  import { composite_arguments, USAGE_ERROR_EXIT_CODE } from '#scripts/josh/josh-composite-arguments'
6
- import { lane_ledger } from '#scripts/lane/lane-ledger'
6
+ import { lane_child_marker } from '#scripts/lane/lane-child-marker'
7
+ import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
7
8
  import { FAIL_EXIT_CODE } from '#scripts/lib/buffered-process'
8
9
  import { review_stamps } from '#scripts/review/review-stamps'
9
10
  import { review_tree } from '#scripts/review/review-tree'
@@ -14,6 +15,7 @@ import { gate_plan, type GatePlan } from './gate-plan'
14
15
  import { gate_report, type GateStepResult } from './gate-report'
15
16
  import { gate_run_steps, type ReusedChecks } from './gate-run-steps'
16
17
  import { gate_skip } from './gate-skip'
18
+ import { gate_static_group } from './gate-static-group'
17
19
  import { build_gate_step, build_gate_steps, UNIT_WORKER_FLAG } from './gate-steps'
18
20
  import { gate_tree, type GateTree } from './gate-tree'
19
21
  import { project_checks } from './project-checks'
@@ -168,15 +170,26 @@ async function run_marked_gate<T>(
168
170
  // **The free cores do not cap the plan** (joshuafolkken/kit#4167). The unit claim's admission narrows it
169
171
  // to the room the outside load leaves, read over its wait rather than once at the gate's start — a
170
172
  // width fixed off one busy instant held for the whole suite (`core_admission.is_too_narrow`).
171
- function announce_gate_plan(is_unit_included: boolean, available_cores: number): GatePlan {
173
+ //
174
+ // A `--static-group` narrows the resolved plan's checks and keeps its concurrency, which the full static
175
+ // set sized: a cap above the narrowed count never binds.
176
+ function announce_gate_plan(
177
+ is_unit_included: boolean,
178
+ available_cores: number,
179
+ static_group?: string,
180
+ ): GatePlan {
172
181
  const concurrent_runs = unit_worker_share.live_run_count() + unit_worker_share.SOLO_RUNS
173
182
  const is_kit_repository = project_checks.is_kit_repository(process.cwd())
174
- const plan = gate_plan.resolve_gate_plan(
183
+ const resolved = gate_plan.resolve_gate_plan(
175
184
  available_cores,
176
185
  concurrent_runs,
177
186
  is_unit_included,
178
187
  is_kit_repository,
179
188
  )
189
+ const plan = {
190
+ ...resolved,
191
+ checks: gate_static_group.narrow_checks(resolved.checks, static_group),
192
+ }
180
193
 
181
194
  process.stdout.write(`${gate_plan.format_gate_plan(plan, available_cores, concurrent_runs)}\n`)
182
195
 
@@ -195,6 +208,8 @@ interface GateOptions {
195
208
  // Whether the unit suite is one of this gate's checks. Defaults to true everywhere; only
196
209
  // `--no-unit` turns it off, for the CI job that runs the suite on a runner of its own.
197
210
  is_unit_included?: boolean
211
+ // The one static group a CI matrix leg runs (`gate-static-group.ts`); set only with the unit suite off.
212
+ static_group?: string | undefined
198
213
  stamp_path?: string
199
214
  marker_path?: string
200
215
  // The gate log's destination, a parameter for the same reason the two above are: this suite runs
@@ -253,7 +268,10 @@ function scoped_precheck_refusal(tree: GateTree, options: GateOptions): string |
253
268
  return undefined
254
269
  }
255
270
 
256
- return scoped_green.refusal_for(tree.files, tree.base, options.scoped_sources)
271
+ return scoped_green.refusal_for(tree.files, tree.base, {
272
+ is_lane_child: lane_child_marker.is_child_of(process.cwd()),
273
+ ...options.scoped_sources,
274
+ })
257
275
  }
258
276
 
259
277
  // **A partial gate records nothing green, and this is the same rule as the skip one layer out**.
@@ -318,7 +336,7 @@ async function run_checked_gate(
318
336
  // Resolved at the gate's entry, before this gate sets `JOSH_UNIT_RUN_MARKED`, so a gate nested in the
319
337
  // unit suite reads the flag as set and reserves nothing.
320
338
  const budget = gate_run_steps.default_budget()
321
- const plan = announce_gate_plan(is_unit_included, budget.available_cores)
339
+ const plan = announce_gate_plan(is_unit_included, budget.available_cores, options.static_group)
322
340
  const reused = await checks_to_reuse(plan, tree, options)
323
341
 
324
342
  // **The gate holds the unit-run marker for its whole run, not just its unit step.** The step that
@@ -406,31 +424,48 @@ const ACCEPTED_FLAGS: ReadonlyArray<string> = [VERBOSE_FLAG, gate_skip.FORCE_FLA
406
424
 
407
425
  // The flags are the caller's, the destinations are the run's, so the two are merged here rather than
408
426
  // letting an option override a flag the user typed.
427
+ function unknown_arguments(extra_arguments: ReadonlyArray<string>): ReadonlyArray<string> {
428
+ return extra_arguments.filter(
429
+ (argument) =>
430
+ !ACCEPTED_FLAGS.includes(argument) && gate_static_group.group_of(argument) === undefined,
431
+ )
432
+ }
433
+
434
+ // The shared refusal, plus the arguments it is actually about. Some flags are accepted, so the bare
435
+ // "takes no extra arguments" would send a reader to drop the ones that work. The list is interpolated
436
+ // rather than spelled out here, so adding a flag cannot leave this line stale.
437
+ function write_refusal(unknown: ReadonlyArray<string>): void {
438
+ const groups = Object.keys(gate_static_group.STATIC_GROUPS).join('|')
439
+
440
+ process.stderr.write(
441
+ `${composite_arguments.format_rejection(GATE_COMMAND, GATE_TARGETS)}\n` +
442
+ ` refused: ${unknown.join(' ')}\n` +
443
+ ` accepted here: ${ACCEPTED_FLAGS.join(' ')} ${gate_static_group.STATIC_GROUP_FLAG_PREFIX}<${groups}>\n`,
444
+ )
445
+ }
446
+
409
447
  async function run_gate_command(
410
448
  extra_arguments: ReadonlyArray<string>,
411
449
  options: GateOptions = {},
412
450
  ): Promise<number> {
413
- const unknown = extra_arguments.filter((argument) => !ACCEPTED_FLAGS.includes(argument))
451
+ const unknown = unknown_arguments(extra_arguments)
414
452
 
415
453
  if (unknown.length > 0) {
416
- // The shared refusal, plus the arguments it is actually about. Some flags are accepted, so the
417
- // bare "takes no extra arguments" would send a reader to drop the ones that work. The list is
418
- // interpolated rather than spelled out here, so adding a flag cannot leave this line stale.
419
- process.stderr.write(
420
- `${composite_arguments.format_rejection(GATE_COMMAND, GATE_TARGETS)}\n` +
421
- ` refused: ${unknown.join(' ')}\n` +
422
- ` accepted here: ${ACCEPTED_FLAGS.join(' ')}\n`,
423
- )
454
+ write_refusal(unknown)
424
455
 
425
456
  return USAGE_ERROR_EXIT_CODE
426
457
  }
427
458
 
459
+ // A static group is a part of the static set, so it implies `--no-unit` and every withholding that flag carries.
460
+ const static_group = gate_static_group.find_group(extra_arguments)
461
+
428
462
  return await run_verification_gate({
429
463
  is_scoped_enforced: true,
430
464
  ...options,
431
465
  is_verbose: extra_arguments.includes(VERBOSE_FLAG),
432
466
  is_forced: extra_arguments.includes(gate_skip.FORCE_FLAG),
433
- is_unit_included: !extra_arguments.includes(NO_UNIT_FLAG),
467
+ is_unit_included: !extra_arguments.includes(NO_UNIT_FLAG) && static_group === undefined,
468
+ static_group,
434
469
  })
435
470
  }
436
471
 
@@ -440,7 +475,7 @@ async function run_gate_command(
440
475
  // process end on its own, which it can, since every child has already exited by here.
441
476
  if (process.argv[1] === fileURLToPath(import.meta.url)) {
442
477
  process.exitCode = await run_gate_command(process.argv.slice(FIRST_ARGUMENT_INDEX), {
443
- ledger_path: await lane_ledger.target(),
478
+ ledger_path: await lane_ledger_write.target(),
444
479
  check_records: check_reuse.DEFAULT_RECORDS,
445
480
  })
446
481
  }