@joshuafolkken/kit 1.1455.0 → 1.1514.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 (374) hide show
  1. package/.claude/settings.json +6 -6
  2. package/.claude/skills/dependency-update/SKILL.md +26 -55
  3. package/.claude/skills/epic-commands/SKILL.md +83 -156
  4. package/.claude/skills/epic-commands/epic-bundle.md +51 -79
  5. package/.claude/skills/epic-commands/execution-waves.md +43 -81
  6. package/.claude/skills/verify-ui/SKILL.md +24 -50
  7. package/.claude/skills/workflow-commands/SKILL.md +7 -9
  8. package/.claude/skills/workflow-commands/background-commands.md +31 -46
  9. package/.claude/skills/workflow-commands/backlogrun-child.md +65 -85
  10. package/.claude/skills/workflow-commands/backlogrun-lanes.md +86 -125
  11. package/.claude/skills/workflow-commands/backlogrun-park.md +68 -118
  12. package/.claude/skills/workflow-commands/backlogrun-progress.md +17 -28
  13. package/.claude/skills/workflow-commands/backlogrun-recovery.md +17 -26
  14. package/.claude/skills/workflow-commands/backlogrun-steps.md +35 -52
  15. package/.claude/skills/workflow-commands/backlogrun.md +31 -50
  16. package/.claude/skills/workflow-commands/chain-rule.md +31 -40
  17. package/.claude/skills/workflow-commands/delegation.md +12 -8
  18. package/.claude/skills/workflow-commands/entry-sequence.md +1 -3
  19. package/.claude/skills/workflow-commands/followup.md +55 -77
  20. package/.claude/skills/workflow-commands/fullrun-steps.md +3 -5
  21. package/.claude/skills/workflow-commands/fullrun.md +4 -6
  22. package/.claude/skills/workflow-commands/halfrun.md +3 -7
  23. package/.claude/skills/workflow-commands/into-target.md +1 -2
  24. package/.claude/skills/workflow-commands/issue-comments.md +43 -60
  25. package/.claude/skills/workflow-commands/issue-scout.md +3 -2
  26. package/.claude/skills/workflow-commands/kickoff.md +65 -74
  27. package/.claude/skills/workflow-commands/latest-gate.md +2 -6
  28. package/.claude/skills/workflow-commands/needs-human-review.md +2 -5
  29. package/.claude/skills/workflow-commands/observation-filing.md +59 -118
  30. package/.claude/skills/workflow-commands/observation-ledger.md +52 -112
  31. package/.claude/skills/workflow-commands/pre-gate-cut.md +88 -142
  32. package/.claude/skills/workflow-commands/prerequisite.md +11 -17
  33. package/.claude/skills/workflow-commands/progress-watcher.md +23 -33
  34. package/.claude/skills/workflow-commands/prrun.md +3 -5
  35. package/.claude/skills/workflow-commands/retrospective.md +35 -54
  36. package/.claude/skills/workflow-commands/split-assessment.md +32 -58
  37. package/.claude/skills/workflow-commands/target-repository.md +1 -2
  38. package/.claude/skills/workflow-commands/working-tree-hold.md +54 -73
  39. package/.codex/hooks.json +6 -6
  40. package/.github/actions/setup-node/action.yml +1 -1
  41. package/.github/workflows/ci.yml +1 -1
  42. package/.github/workflows/publish.yml +2 -2
  43. package/CLAUDE.md +7 -6
  44. package/cspell/index.yaml +7 -0
  45. package/dist/CLAUDE.md +7 -6
  46. package/dist/commands/document-section-cli.js +67 -34
  47. package/dist/hooks/batch-guard.js +6 -6
  48. package/dist/hooks/chunk-4KKD2UTK.js +66 -0
  49. package/dist/hooks/{chunk-GPLGZ6IC.js → chunk-6TS2JD6D.js} +14 -3
  50. package/dist/hooks/chunk-7DSBJVFH.js +50 -0
  51. package/dist/hooks/{chunk-5H4SSDSD.js → chunk-A65Y4YOR.js} +3 -3
  52. package/dist/hooks/{chunk-3C4XRYG6.js → chunk-APUO6U4J.js} +102 -19
  53. package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
  54. package/dist/hooks/chunk-GP4IEC4O.js +20 -0
  55. package/dist/hooks/chunk-JP45SOCJ.js +56 -0
  56. package/dist/hooks/{chunk-OLMO72ON.js → chunk-LAOV7NLM.js} +1 -1
  57. package/dist/hooks/chunk-M4HGZV7U.js +83 -0
  58. package/dist/hooks/{chunk-TBJFTSUY.js → chunk-O6GDRNJB.js} +9 -7
  59. package/dist/hooks/{chunk-HFGWYGUV.js → chunk-PF2NZL2A.js} +5 -5
  60. package/dist/hooks/{chunk-WNLVGT7E.js → chunk-PTICX4OL.js} +4 -5
  61. package/dist/hooks/{chunk-HHI6FO3R.js → chunk-RT27QRJW.js} +763 -1515
  62. package/dist/hooks/{chunk-7WQBPLUN.js → chunk-SYOTDYCI.js} +1149 -1095
  63. package/dist/hooks/{chunk-LIAFCXL7.js → chunk-TNUMY6NL.js} +1519 -1332
  64. package/dist/hooks/chunk-UPUZF3AN.js +279 -0
  65. package/dist/hooks/{chunk-Z4XI7CZS.js → chunk-WK6SMBIC.js} +1932 -784
  66. package/dist/hooks/codex-hook-adapter.js +17 -14
  67. package/dist/hooks/duplicate-read-guard.js +7 -7
  68. package/dist/hooks/format-edited.js +5 -4
  69. package/dist/hooks/inputs.json +302 -247
  70. package/dist/hooks/investigation-guard.js +8 -11
  71. package/dist/hooks/pretool-guard.js +16 -13
  72. package/dist/hooks/session-lang.js +372 -26
  73. package/dist/hooks/stop-guard.js +2593 -189
  74. package/dist/josh.js +2128 -282
  75. package/dist/managed-marker/index.js +1 -2
  76. package/dist/version/index.js +15 -12
  77. package/eslint/base.js +2 -1
  78. package/eslint/rules/unicorn.js +16 -0
  79. package/lefthook/base.yml +5 -3
  80. package/package.json +14 -12
  81. package/prompts/coding-standards.md +12 -22
  82. package/prompts/collaboration-workflow/file-edits.md +25 -45
  83. package/prompts/collaboration-workflow/gh-rest.md +19 -36
  84. package/prompts/collaboration-workflow/glossary.md +5 -7
  85. package/prompts/collaboration-workflow/issue-citation.md +6 -14
  86. package/prompts/collaboration-workflow/issue-template.md +40 -32
  87. package/prompts/collaboration-workflow/lane-instructions.md +35 -0
  88. package/prompts/collaboration-workflow/operating-rules.md +33 -46
  89. package/prompts/collaboration-workflow/output-bounds.md +15 -15
  90. package/prompts/collaboration-workflow/overview.md +14 -19
  91. package/prompts/collaboration-workflow/plan-comment.md +13 -13
  92. package/prompts/collaboration-workflow/principles.md +57 -54
  93. package/prompts/collaboration-workflow/proposal-request.md +1 -9
  94. package/prompts/collaboration-workflow/report-format.md +41 -49
  95. package/prompts/collaboration-workflow/residency.md +15 -11
  96. package/prompts/collaboration-workflow/rule-delivery.md +7 -15
  97. package/prompts/collaboration-workflow/shell-body.md +10 -20
  98. package/prompts/collaboration-workflow/turn-batching.md +8 -14
  99. package/prompts/collaboration-workflow/upstream-interrupt.md +26 -34
  100. package/prompts/collaboration-workflow/wip-cap.md +26 -32
  101. package/prompts/collaboration-workflow.md +25 -25
  102. package/prompts/refactoring.md +33 -43
  103. package/prompts/review-rubric.md +84 -129
  104. package/prompts/review.md +110 -141
  105. package/prompts/sonar-hotspot-handling.md +22 -50
  106. package/prompts/testing-guide.md +48 -99
  107. package/scripts/adopt/adopt-toolkits.ts +2 -4
  108. package/scripts/agent/agent-argv.ts +20 -4
  109. package/scripts/agent/agent-exit-record.ts +23 -1
  110. package/scripts/agent/agent-role-profile.ts +101 -45
  111. package/scripts/agent/agent-safe-text.ts +60 -0
  112. package/scripts/agent/claude-agent-argv.ts +14 -0
  113. package/scripts/agent/codex-agent-argv.ts +9 -2
  114. package/scripts/agent/lane-instructions.ts +144 -0
  115. package/scripts/backlog/backlog-budget-cli.ts +1 -2
  116. package/scripts/backlog/backlog-budget.ts +4 -4
  117. package/scripts/backlog/backlog-drive-carried.ts +26 -0
  118. package/scripts/backlog/backlog-drive-cli.ts +10 -5
  119. package/scripts/backlog/backlog-drive-named.ts +3 -5
  120. package/scripts/backlog/backlog-drive-restore.ts +28 -2
  121. package/scripts/backlog/backlog-drive-seats.ts +27 -0
  122. package/scripts/backlog/backlog-drive.ts +11 -6
  123. package/scripts/backlog/backlog-occupied.ts +56 -0
  124. package/scripts/backlog/backlog-ready.ts +59 -15
  125. package/scripts/behavior/behavior-rules.ts +3 -2
  126. package/scripts/cost-runtime/codex-usage.ts +4 -2
  127. package/scripts/cost-runtime/cost-cli.ts +1 -2
  128. package/scripts/delegation/duplicate-read-guard.ts +2 -2
  129. package/scripts/delegation/investigation-guard.ts +15 -65
  130. package/scripts/delegation/investigation-reads.ts +1 -2
  131. package/scripts/discovery/repo-discovery.ts +4 -2
  132. package/scripts/discovery/repo-origin.ts +2 -4
  133. package/scripts/doctor/doctor-logic.ts +6 -3
  134. package/scripts/document/document-byte-budget.ts +28 -27
  135. package/scripts/document/document-byte-check.ts +1 -2
  136. package/scripts/document/document-section-cli.ts +17 -5
  137. package/scripts/document/entry-read-budget.ts +3 -3
  138. package/scripts/document/read-set-cli.ts +4 -1
  139. package/scripts/epic/epic-audit-orphans.ts +5 -8
  140. package/scripts/epic/epic-audit-rationale.ts +8 -2
  141. package/scripts/epic/epic-audit.ts +52 -1
  142. package/scripts/epic/epic-bundle-cli.ts +1 -2
  143. package/scripts/epic/epic-bundle-evidence.ts +49 -26
  144. package/scripts/epic/epic-bundle-referenced.ts +14 -3
  145. package/scripts/epic/epic-bundle.ts +17 -9
  146. package/scripts/epic/epic-chains.ts +6 -2
  147. package/scripts/epic/epic-close-comment.ts +1 -2
  148. package/scripts/followup/git-followup-workflow.ts +19 -1
  149. package/scripts/gate/check-inputs.ts +129 -0
  150. package/scripts/gate/check-reuse.ts +162 -0
  151. package/scripts/gate/core-budget.ts +93 -44
  152. package/scripts/gate/core-wait.ts +130 -0
  153. package/scripts/gate/free-cores.ts +49 -0
  154. package/scripts/gate/gate-ledger.ts +17 -3
  155. package/scripts/gate/gate-plan.ts +23 -1
  156. package/scripts/gate/gate-report.ts +15 -2
  157. package/scripts/gate/gate-run-steps.ts +150 -0
  158. package/scripts/gate/held-mark.ts +55 -0
  159. package/scripts/gate/machine-capacity.ts +70 -11
  160. package/scripts/gate/pre-commit-type-check.ts +14 -1
  161. package/scripts/gate/verification-gate.ts +65 -127
  162. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  163. package/scripts/gh/git-gh-issue-write.ts +24 -2
  164. package/scripts/gh/git-pr.ts +19 -3
  165. package/scripts/git/git-common-directory.ts +3 -3
  166. package/scripts/git/git-diff-reads.ts +16 -9
  167. package/scripts/git/git-execa-error.ts +1 -2
  168. package/scripts/git/main-merge-regenerate.ts +78 -0
  169. package/scripts/git/main-merge.ts +14 -5
  170. package/scripts/hooks/batch-guard.ts +5 -8
  171. package/scripts/hooks/format-edited-file.ts +10 -4
  172. package/scripts/hooks/pre-commit-lint.ts +91 -0
  173. package/scripts/hooks/pre-push-unit.ts +15 -4
  174. package/scripts/hooks/pretool-guard.ts +48 -79
  175. package/scripts/hooks/reserved-run.ts +1 -0
  176. package/scripts/hooks/run-hook.sh +10 -12
  177. package/scripts/hooks/step-zero-notice.ts +3 -2
  178. package/scripts/hooks/stop-actions.ts +204 -0
  179. package/scripts/hooks/stop-guard.ts +62 -21
  180. package/scripts/init/hook-command-rewrite.ts +6 -2
  181. package/scripts/init/hook-launch.ts +12 -7
  182. package/scripts/init/init-logic-deploy-vps.ts +4 -2
  183. package/scripts/issue/behavior-change-lint.ts +12 -6
  184. package/scripts/issue/expected-effect-lint.ts +59 -0
  185. package/scripts/issue/issue-cite-cli.ts +35 -15
  186. package/scripts/issue/issue-classification.ts +1 -1
  187. package/scripts/issue/issue-lint-cli.ts +5 -3
  188. package/scripts/issue/issue-scout-cli.ts +22 -16
  189. package/scripts/issue/markdown-section.ts +19 -5
  190. package/scripts/josh/command-usage.ts +110 -0
  191. package/scripts/josh/file-map-stamp.ts +1 -3
  192. package/scripts/josh/hook-decision.ts +17 -2
  193. package/scripts/josh/josh-command-map.ts +2 -0
  194. package/scripts/josh/josh-command-types.ts +55 -9
  195. package/scripts/josh/josh-commands-ai.ts +13 -0
  196. package/scripts/josh/josh-commands-development.ts +1 -1
  197. package/scripts/josh/josh-commands-hooks.ts +6 -0
  198. package/scripts/josh/josh-commands-lane.ts +10 -1
  199. package/scripts/josh/josh-commands-workflow.ts +5 -0
  200. package/scripts/josh/josh-logic.ts +6 -15
  201. package/scripts/josh/josh-usage-automation.ts +121 -0
  202. package/scripts/josh/josh-usage-backlog.ts +180 -0
  203. package/scripts/josh/josh-usage-developer.ts +230 -0
  204. package/scripts/josh/josh-usage-hooks.ts +38 -0
  205. package/scripts/josh/josh-usage-issue.ts +205 -0
  206. package/scripts/josh/josh-usage-review.ts +101 -0
  207. package/scripts/josh/josh-usage-run.ts +252 -0
  208. package/scripts/josh/josh-usage-session.ts +238 -0
  209. package/scripts/josh/josh-usage-workflow.ts +212 -0
  210. package/scripts/josh/josh.ts +14 -5
  211. package/scripts/josh/package-directory.ts +20 -0
  212. package/scripts/lane/lane-await.ts +3 -2
  213. package/scripts/lane/lane-capacity.ts +8 -2
  214. package/scripts/lane/lane-check-stats.ts +56 -0
  215. package/scripts/lane/lane-child-invocation.ts +49 -5
  216. package/scripts/lane/lane-dispatch.ts +3 -3
  217. package/scripts/lane/lane-guard-policy.ts +5 -5
  218. package/scripts/lane/lane-handoff.ts +8 -1
  219. package/scripts/lane/lane-install.ts +12 -4
  220. package/scripts/lane/lane-ledger.ts +52 -1
  221. package/scripts/lane/lane-limit-cli.ts +8 -9
  222. package/scripts/lane/lane-load.ts +9 -2
  223. package/scripts/lane/lane-measure-cli.ts +4 -0
  224. package/scripts/lane/lane-parked.ts +18 -0
  225. package/scripts/lane/lane-preserve.ts +29 -1
  226. package/scripts/lane/lane-process-load.ts +125 -0
  227. package/scripts/lane/lane-process-stats.ts +93 -0
  228. package/scripts/lane/lane-registry.ts +17 -10
  229. package/scripts/lane/lane-restart.ts +5 -26
  230. package/scripts/lane/lane-session-action.ts +146 -0
  231. package/scripts/lane/lane-session-broker.ts +68 -125
  232. package/scripts/lane/lane-spent.ts +14 -3
  233. package/scripts/lane/lane-stats.ts +1 -0
  234. package/scripts/lane/lane-withdraw-cli.ts +71 -0
  235. package/scripts/lane/lane-withdraw.ts +182 -0
  236. package/scripts/lint/format.ts +4 -4
  237. package/scripts/lint/lint-parallel.ts +10 -3
  238. package/scripts/lint/lint-related-scope.ts +4 -1
  239. package/scripts/managed-marker/managed-marker-logic.ts +1 -2
  240. package/scripts/notify/telegram-test.ts +10 -5
  241. package/scripts/overrides/lockfile-overrides.ts +1 -3
  242. package/scripts/propagate/propagate.ts +1 -2
  243. package/scripts/release/release-progress.ts +3 -2
  244. package/scripts/report/report-format-reference.ts +2 -0
  245. package/scripts/report/report-lint.ts +7 -4
  246. package/scripts/review/acceptance-map-check.ts +61 -0
  247. package/scripts/review/acceptance-map.ts +205 -0
  248. package/scripts/review/review-brief-cli.ts +1 -1
  249. package/scripts/review/review-record-cli.ts +6 -2
  250. package/scripts/rules/agent-model.ts +59 -0
  251. package/scripts/rules/delivered-rules.ts +11 -2
  252. package/scripts/rules/destructive-command.ts +6 -9
  253. package/scripts/rules/direct-filing.ts +3 -7
  254. package/scripts/rules/early-heartbeat.ts +7 -18
  255. package/scripts/rules/file-body.ts +6 -13
  256. package/scripts/rules/filing-cap.ts +4 -6
  257. package/scripts/rules/firing-point.ts +6 -5
  258. package/scripts/rules/git-force.ts +5 -9
  259. package/scripts/rules/guard-remedy.ts +117 -0
  260. package/scripts/rules/implementation-cut.ts +37 -35
  261. package/scripts/rules/index-guard.ts +4 -7
  262. package/scripts/rules/issue-comments.ts +67 -16
  263. package/scripts/rules/josh-git-bare.ts +3 -6
  264. package/scripts/rules/lane-background.ts +10 -21
  265. package/scripts/rules/lane-carry-conflict.ts +7 -17
  266. package/scripts/rules/lane-interactive-ask.ts +6 -11
  267. package/scripts/rules/lane-park.ts +5 -13
  268. package/scripts/rules/lane-split-park.ts +2 -7
  269. package/scripts/rules/lane-switch-main.ts +9 -12
  270. package/scripts/rules/last-prompt.ts +50 -5
  271. package/scripts/rules/oracle-consulted.ts +4 -5
  272. package/scripts/rules/oracle-list-cli.ts +7 -0
  273. package/scripts/rules/piped-verification.ts +84 -32
  274. package/scripts/rules/poll-loop.ts +7 -17
  275. package/scripts/rules/pre-gate-cut.ts +7 -21
  276. package/scripts/rules/prior-comment-read.ts +7 -5
  277. package/scripts/rules/protected-files.ts +5 -9
  278. package/scripts/rules/raw-field-body.ts +3 -7
  279. package/scripts/rules/rule-body-guard.ts +4 -11
  280. package/scripts/rules/rule-list.ts +4 -1
  281. package/scripts/rules/rule-registry-rows.ts +10 -0
  282. package/scripts/rules/rule-registry.ts +7 -4
  283. package/scripts/rules/rule-rewrite.ts +114 -0
  284. package/scripts/rules/rule-value.ts +7 -1
  285. package/scripts/rules/run-tail-rule.ts +6 -22
  286. package/scripts/rules/shell-body-trigger.ts +4 -6
  287. package/scripts/rules/shell-segments.ts +22 -0
  288. package/scripts/rules/stop-decision.ts +77 -0
  289. package/scripts/rules/stop-rules.ts +164 -77
  290. package/scripts/rules/tail-commands.ts +15 -1
  291. package/scripts/rules/test-declared-commit.ts +4 -10
  292. package/scripts/rules/third-party-write.ts +5 -10
  293. package/scripts/rules/worktree-guard.ts +5 -12
  294. package/scripts/run/board/run-board-cli.ts +2 -0
  295. package/scripts/run/board/run-board-closed.ts +8 -2
  296. package/scripts/run/board/run-board-github.ts +19 -3
  297. package/scripts/run/board/run-board-header.ts +4 -1
  298. package/scripts/run/board/run-board-idle.ts +31 -0
  299. package/scripts/run/board/run-board-labels.ts +2 -0
  300. package/scripts/run/board/run-board-layout.ts +1 -1
  301. package/scripts/run/board/run-board-read.ts +25 -12
  302. package/scripts/run/board/run-board-reason.ts +17 -4
  303. package/scripts/run/board/run-board-render-legend.ts +1 -0
  304. package/scripts/run/board/run-board-render-slot.ts +1 -1
  305. package/scripts/run/board/run-board-render-waits.ts +41 -0
  306. package/scripts/run/board/run-board-render.ts +6 -1
  307. package/scripts/run/board/run-board-row-yield.ts +1 -0
  308. package/scripts/run/board/run-board-state.ts +9 -0
  309. package/scripts/run/board/run-board-status.ts +89 -23
  310. package/scripts/run/board/run-board-thanks.ts +7 -2
  311. package/scripts/run/board/run-board-tick.ts +37 -8
  312. package/scripts/run/carry/run-carry-args.ts +1 -3
  313. package/scripts/run/carry/run-carry-conversation.ts +7 -2
  314. package/scripts/run/cut/run-cut-cli.ts +18 -22
  315. package/scripts/run/cut/run-cut-decline.ts +72 -0
  316. package/scripts/run/cut/run-cut-ready.ts +74 -0
  317. package/scripts/run/cut/run-cut-report.ts +29 -0
  318. package/scripts/run/cut/run-cut.ts +10 -1
  319. package/scripts/run/detached-launch.ts +23 -4
  320. package/scripts/run/entry/run-entry-cli.ts +8 -1
  321. package/scripts/run/event/run-event-render.ts +1 -0
  322. package/scripts/run/event/run-event-scope.ts +19 -1
  323. package/scripts/run/event/run-event-stream-emit.ts +13 -6
  324. package/scripts/run/event/run-event-stream.ts +4 -0
  325. package/scripts/run/hold/run-hold-cli.ts +14 -3
  326. package/scripts/run/hold/run-hold-session.ts +46 -0
  327. package/scripts/run/hold/run-hold.ts +13 -4
  328. package/scripts/run/launch-priority.ts +32 -0
  329. package/scripts/run/merge/run-merge-cli.ts +16 -42
  330. package/scripts/run/merge/run-merge-ship.ts +59 -1
  331. package/scripts/run/merge/run-merge-signals.ts +99 -0
  332. package/scripts/run/merge/run-merge-steps.ts +11 -1
  333. package/scripts/run/merge/run-merge.ts +22 -9
  334. package/scripts/run/run-ending.ts +31 -3
  335. package/scripts/run/run-liveness.ts +1 -1
  336. package/scripts/run/run-parent-cut-hook.ts +5 -10
  337. package/scripts/run/run-poll-hook.ts +157 -0
  338. package/scripts/run/run-preflight.ts +1 -2
  339. package/scripts/run/run-step-cli.ts +22 -15
  340. package/scripts/run/run-step-section.ts +85 -0
  341. package/scripts/run/run-stop-notify.ts +1 -2
  342. package/scripts/run/run-watcher-guard.ts +6 -3
  343. package/scripts/run/ship/run-ship-cli.ts +5 -12
  344. package/scripts/run/ship/run-ship-detach.ts +9 -0
  345. package/scripts/run/ship/run-ship-next.ts +10 -2
  346. package/scripts/run/ship/run-ship-overlap.ts +195 -0
  347. package/scripts/run/ship/run-ship-pre-detach.ts +33 -15
  348. package/scripts/run/ship/run-ship-preflight.ts +28 -2
  349. package/scripts/run/ship/run-ship-return.ts +31 -7
  350. package/scripts/run/ship/run-ship-review-steps.ts +25 -14
  351. package/scripts/run/ship/run-ship-review.ts +15 -2
  352. package/scripts/run/ship/run-ship-steps.ts +1 -1
  353. package/scripts/run/ship/run-ship-stop-context.ts +206 -0
  354. package/scripts/run/ship/run-ship-sync.ts +14 -6
  355. package/scripts/run/tidy/run-tidy-cli.ts +3 -3
  356. package/scripts/run/tidy/run-tidy-lanes.ts +3 -2
  357. package/scripts/safe-chain/project-config.ts +7 -2
  358. package/scripts/sync/sync-hook-safety.ts +8 -2
  359. package/scripts/test/test-e2e-guard.ts +30 -5
  360. package/scripts/test/test-unit-guard.ts +16 -1
  361. package/scripts/test/unit-worker-share.ts +26 -3
  362. package/scripts/time-runtime/time-batch-guard.ts +3 -3
  363. package/scripts/time-runtime/time-hook-transcript.ts +3 -2
  364. package/scripts/time-runtime/time-shell.ts +39 -4
  365. package/scripts/time-runtime/time-spans.ts +1 -0
  366. package/scripts/time-runtime/time-tool-call.ts +7 -0
  367. package/scripts/version/effective-upstream.ts +3 -4
  368. package/scripts/version/kit-version-config.ts +8 -2
  369. package/scripts/version/latest-corepack.ts +1 -2
  370. package/scripts/version/release-hold.ts +11 -4
  371. package/scripts/version/version-commands.ts +12 -4
  372. package/templates/gitignore +6 -2
  373. package/dist/hooks/chunk-D5QBTOCZ.js +0 -68
  374. package/dist/hooks/chunk-UT7YC3N2.js +0 -56
@@ -0,0 +1,150 @@
1
+ import { availableParallelism } from 'node:os'
2
+ import { bounded_pool } from '#scripts/lib/bounded-pool'
3
+ import { buffered_process } from '#scripts/lib/buffered-process'
4
+ import { unit_worker_share } from '#scripts/test/unit-worker-share'
5
+ import { core_budget } from './core-budget'
6
+ import { gate_plan, type GatePlan } from './gate-plan'
7
+ import type { GateStep, GateStepResult } from './gate-report'
8
+ import { build_gate_step } from './gate-steps'
9
+ import { project_checks } from './project-checks'
10
+
11
+ // Running the gate's checks: each one under its place in the machine-wide budget, a reused one
12
+ // (`check-reuse.ts`) without starting it. `verification-gate.ts` decides what runs and records the
13
+ // outcome; this module only runs it.
14
+
15
+ async function run_gate_step(step: GateStep): Promise<GateStepResult> {
16
+ if (step.skip_reason !== undefined) {
17
+ return {
18
+ label: step.label,
19
+ command: step.command_args.join(' '),
20
+ output: project_checks.skip_notice('check', step.skip_reason),
21
+ exit_code: 0,
22
+ elapsed_ms: 0,
23
+ }
24
+ }
25
+
26
+ const result = await buffered_process.run_buffered_process(
27
+ step.command_args,
28
+ step.cwd === undefined ? {} : { cwd: step.cwd },
29
+ )
30
+
31
+ return { label: step.label, command: step.command_args.join(' '), ...result }
32
+ }
33
+
34
+ // This gate's share of the machine, resolved once at the gate's entry and carried down as one value.
35
+ // `available_cores` is the budget every check reserves against; `is_reserved`
36
+ // is false for a gate nested inside another gate's unit suite — this repository's own gate tests — so
37
+ // the outer gate reserves and every gate beneath it does not. Captured before this gate sets
38
+ // `JOSH_UNIT_RUN_MARKED`, which is why it is read at entry rather than re-derived here.
39
+ interface BudgetContext {
40
+ available_cores: number
41
+ is_reserved: boolean
42
+ }
43
+
44
+ function default_budget(): BudgetContext {
45
+ return {
46
+ available_cores: availableParallelism(),
47
+ is_reserved: !unit_worker_share.is_nested_run() && !core_budget.is_held(),
48
+ }
49
+ }
50
+
51
+ // A gate step paired with the cores it reserves from the machine-wide budget while it runs.
52
+ // The weight travels with the step so `run_gate_steps` never has to re-pair a
53
+ // step with its check by index.
54
+ interface ReservedStep {
55
+ step: GateStep
56
+ weight: number
57
+ memory_mb: number
58
+ // When the check's own green record was taken, for a check `check-reuse.ts` found reusable.
59
+ reused_at: string | undefined
60
+ }
61
+
62
+ // The checks this gate reuses, each mapped to when it was last green.
63
+ type ReusedChecks = ReadonlyMap<string, string>
64
+
65
+ async function build_reserved_steps(
66
+ plan: GatePlan,
67
+ available_cores: number,
68
+ reused: ReusedChecks,
69
+ ): Promise<ReadonlyArray<ReservedStep>> {
70
+ return await Promise.all(
71
+ plan.checks.map(async (check) => ({
72
+ step: await build_gate_step(check, process.cwd(), plan),
73
+ weight: gate_plan.check_weight(check, plan, available_cores),
74
+ memory_mb: gate_plan.check_memory(check, plan, available_cores),
75
+ reused_at: reused.get(check.label),
76
+ })),
77
+ )
78
+ }
79
+
80
+ // A reused check starts no process and claims no core: its result is the record's, and the empty
81
+ // output is what that record was written from (`gate_report.has_nothing_to_say`).
82
+ function reused_result(step: GateStep, reused_at: string): GateStepResult {
83
+ return {
84
+ label: step.label,
85
+ command: step.command_args.join(' '),
86
+ output: '',
87
+ exit_code: 0,
88
+ elapsed_ms: 0,
89
+ reused_at,
90
+ }
91
+ }
92
+
93
+ // **A nested gate takes no reservation, and the budget is what makes that necessary rather than
94
+ // merely tidy**. The unit step spawns the suite, which runs this repository's
95
+ // own gate tests; each of those is a gate inside the outer gate's unit reservation, and a place it
96
+ // claimed would wait for cores the outer gate cannot free until the suite — this test included —
97
+ // finishes.
98
+ async function run_reserved_step(
99
+ reserved: ReservedStep,
100
+ budget: BudgetContext,
101
+ ): Promise<GateStepResult> {
102
+ if (reserved.reused_at !== undefined) return reused_result(reserved.step, reserved.reused_at)
103
+ if (!budget.is_reserved) return await run_gate_step(reserved.step)
104
+
105
+ return await core_budget.with_core_reservation(
106
+ reserved.weight,
107
+ async (admission) => ({
108
+ ...(await run_gate_step(reserved.step)),
109
+ budget_note: core_budget.describe_overflow(admission),
110
+ }),
111
+ {
112
+ budget: budget.available_cores,
113
+ memory_mb: reserved.memory_mb,
114
+ command: `gate ${reserved.step.label}`,
115
+ },
116
+ )
117
+ }
118
+
119
+ // `bounded_pool` rather than a bare `Promise.all`, so the plan's `concurrency` is what decides how
120
+ // many run at once. **The all-failures-in-one-pass property survives the
121
+ // change** because no check ever rejects: `buffered_process` reports a non-zero exit as a value, so
122
+ // the pool's first-failure abort — written for callers that spawn real Claude sessions — never
123
+ // fires here and every queued check still runs. Results come back in input order however they
124
+ // finished, which is what keeps the printed sections in declaration order. Each step first claims its
125
+ // place in the machine-wide budget and waits while the budget is full.
126
+ async function run_gate_steps(
127
+ plan: GatePlan,
128
+ budget: BudgetContext = default_budget(),
129
+ reused: ReusedChecks = new Map(),
130
+ ): Promise<ReadonlyArray<GateStepResult>> {
131
+ const reserved = await build_reserved_steps(plan, budget.available_cores, reused)
132
+
133
+ // **The checks run under the held mark, so a check's own dispatch claims nothing**.
134
+ // Each check is a `pnpm josh <target>` child, and a target that declares
135
+ // a weight would otherwise claim a second place for the cores reserved for it here. A nested gate
136
+ // sets the mark too, since its checks are covered by the outer gate's reservation.
137
+ return await core_budget.with_held_mark(
138
+ async () =>
139
+ await bounded_pool.bounded_map(
140
+ reserved,
141
+ plan.concurrency,
142
+ async (entry) => await run_reserved_step(entry, budget),
143
+ ),
144
+ )
145
+ }
146
+
147
+ const gate_run_steps = { default_budget, run_gate_step, run_gate_steps }
148
+
149
+ export type { BudgetContext, ReusedChecks }
150
+ export { gate_run_steps }
@@ -0,0 +1,55 @@
1
+ // **The environment mark that says a parent already holds this process's cores**
2
+ // `josh gate` reserves per check and then spawns `pnpm josh lint`. Without
3
+ // the mark, that child's own dispatch would claim a second place for the cores its parent already
4
+ // holds. Every holder sets it, and every child inherits it through the environment and claims nothing.
5
+ const HELD_KEY = 'JOSH_CORE_RESERVED'
6
+ const HELD_VALUE = '1'
7
+ // The ledger key of the claim a command holds for its children (`core_budget.with_command_reservation`),
8
+ // so a child reading the machine can tell that claim's admitted, not-yet-busy weight from running load.
9
+ const HELD_RESERVATION_KEY = 'JOSH_CORE_RESERVATION'
10
+
11
+ function is_held(): boolean {
12
+ return process.env[HELD_KEY] === HELD_VALUE
13
+ }
14
+
15
+ // The ledger key of the one claim a parent command holds for this process, `undefined` where none does.
16
+ function held_reservation(): string | undefined {
17
+ const key = process.env[HELD_RESERVATION_KEY] ?? ''
18
+
19
+ return key === '' ? undefined : key
20
+ }
21
+
22
+ // A function of its own so the restoring write does not sit after an `await`, where
23
+ // `require-atomic-updates` reads any write to `process` as a possible race — the reason `josh.ts`
24
+ // gives `record_exit_code`.
25
+ function set_held_mark(value: string, reservation: string): void {
26
+ process.env[HELD_KEY] = value
27
+ process.env[HELD_RESERVATION_KEY] = reservation
28
+ }
29
+
30
+ // Marks this process, and every child it spawns, as running inside a held reservation — `reservation`
31
+ // names its ledger key where one claim covers them all. The previous values come back afterwards, so a
32
+ // nested holder never clears the mark its outer holder set.
33
+ async function with_held_mark<T>(run: () => Promise<T>, reservation = ''): Promise<T> {
34
+ const previous = process.env[HELD_KEY] ?? ''
35
+ const previous_reservation = process.env[HELD_RESERVATION_KEY] ?? ''
36
+
37
+ set_held_mark(HELD_VALUE, reservation)
38
+
39
+ try {
40
+ return await run()
41
+ } finally {
42
+ set_held_mark(previous, previous_reservation)
43
+ }
44
+ }
45
+
46
+ const held_mark = {
47
+ HELD_KEY,
48
+ HELD_RESERVATION_KEY,
49
+ HELD_VALUE,
50
+ held_reservation,
51
+ is_held,
52
+ with_held_mark,
53
+ }
54
+
55
+ export { held_mark }
@@ -24,7 +24,8 @@ import { execa } from 'execa'
24
24
  // admits without looking at memory, exactly as before.
25
25
  //
26
26
  // **`run:board` draws its machine gauges from the same reading**, so what the
27
- // board shows a person is what the gate admits against. The board takes a sample with no window and
27
+ // board shows a person is what the gate admits against — the gate reading the free memory as none
28
+ // while the board's pressure and swap gauges show why. The board takes a sample with no window and
28
29
  // draws the difference from its previous one. `lane:sample` records the memory half of it, so the
29
30
  // lane-limit measurement sets the same figures beside the lane count.
30
31
 
@@ -50,29 +51,50 @@ const PAGE_SIZE_ARGUMENTS = ['PAGESIZE']
50
51
  // every second. `kern.memorystatus_level` is the kernel's own memory-pressure figure: the percentage of
51
52
  // memory still available, compressible and purgeable pages included — what `os.freemem()` leaves out on
52
53
  // macOS, where it reads near zero on a machine with gigabytes to spare. The swapper counters are the
53
- // pages swapped in and out since boot, `vm_stat`'s `Swapins` / `Swapouts`. The names stay in the output
54
+ // pages swapped in and out since boot, `vm_stat`'s `Swapins` / `Swapouts`; `vm.compressor_input_bytes` is
55
+ // the bytes the compressor has taken in since boot, `vm_stat`'s `Compressions`. The names stay in the output
54
56
  // (no `-n`), so a name an older macOS lacks drops only its own line.
55
57
  const LEVEL_NAME = 'kern.memorystatus_level'
56
58
  const SWAP_IN_NAME = 'vm.compressor.swapper.swapins_total'
57
59
  const SWAP_OUT_NAME = 'vm.compressor.swapper.swapouts_total'
60
+ const COMPRESSED_NAME = 'vm.compressor_input_bytes'
58
61
  const PAGE_SIZE_NAME = 'hw.pagesize'
59
62
  // The kernel's memory-pressure verdict — 1 normal, 2 warning, 4 critical — the one Activity Monitor
60
63
  // colors its "memory pressure" graph by.
61
64
  const PRESSURE_NAME = 'kern.memorystatus_vm_pressure_level'
62
- const SYSCTL_ARGUMENTS = [LEVEL_NAME, SWAP_IN_NAME, SWAP_OUT_NAME, PAGE_SIZE_NAME, PRESSURE_NAME]
65
+ const PRESSURE_WARNING = 2
66
+ // The swap traffic across one window that reads as pressure — 64 MB a second paged to disk. A machine
67
+ // that swapped once still pages a few megabytes back in as old pages are touched.
68
+ const SWAP_PRESSURE_MB = 16
69
+ // The compressor traffic across one window that reads as pressure. An idle machine compresses nothing
70
+ // across a window; the 2026-10-16 thrash compressed about 240,000 16 KB pages every three seconds, about
71
+ // 310 MB a window, with no swap figure to show for it.
72
+ const COMPRESSION_PRESSURE_MB = 64
73
+ const SYSCTL_ARGUMENTS = [
74
+ LEVEL_NAME,
75
+ SWAP_IN_NAME,
76
+ SWAP_OUT_NAME,
77
+ COMPRESSED_NAME,
78
+ PAGE_SIZE_NAME,
79
+ PRESSURE_NAME,
80
+ ]
63
81
  const SYSCTL_LINE = /^(?<name>[\w.]+): (?<value>\d+)$/gmu
64
82
 
83
+ // `memory_pressured` says why `available_mb` read none, so a claim that waits can name the pressure.
65
84
  interface MachineReading {
66
85
  busy_cores: number | undefined
67
86
  available_mb: number | undefined
87
+ memory_pressured?: boolean | undefined
68
88
  }
69
89
 
70
90
  // The memory half of a reading. `swapped_mb` is a counter — every page swapped in or out since boot —
71
- // so a rate is the difference between two readings. `pressure_level` is read on macOS only.
91
+ // so a rate is the difference between two readings, and so is `compressed_mb`, every byte compressed
92
+ // since boot. `pressure_level` and `compressed_mb` are read on macOS only.
72
93
  interface MemoryReading {
73
94
  available_mb: number | undefined
74
95
  swapped_mb: number | undefined
75
96
  pressure_level: number | undefined
97
+ compressed_mb?: number | undefined
76
98
  }
77
99
 
78
100
  // What the ledger's admitted reservations declare — the part of the machine's load already accounted for.
@@ -84,6 +106,7 @@ interface LedgerLoad {
84
106
  interface MachineBudget {
85
107
  cores: number
86
108
  memory_mb: number
109
+ memory_pressured?: boolean | undefined
87
110
  }
88
111
 
89
112
  interface CpuTimes {
@@ -141,10 +164,10 @@ function swapped_pages(
141
164
  return pages_in === undefined || pages_out === undefined ? undefined : pages_in + pages_out
142
165
  }
143
166
 
144
- function swapped_mb(pages: number | undefined, page_bytes: number | undefined): number | undefined {
145
- if (pages === undefined || page_bytes === undefined) return undefined
167
+ function mb_of(units: number | undefined, unit_bytes: number | undefined): number | undefined {
168
+ if (units === undefined || unit_bytes === undefined) return undefined
146
169
 
147
- return (pages * page_bytes) / BYTES_PER_MB
170
+ return (units * unit_bytes) / BYTES_PER_MB
148
171
  }
149
172
 
150
173
  // What was swapped between two readings of the counter. A counter that went back — a reboot between
@@ -163,11 +186,12 @@ function parse_darwin_memory(output: string, total_bytes: number): MemoryReading
163
186
  return {
164
187
  available_mb:
165
188
  level === undefined ? undefined : (level / PERCENT) * (total_bytes / BYTES_PER_MB),
166
- swapped_mb: swapped_mb(
189
+ swapped_mb: mb_of(
167
190
  swapped_pages(values.get(SWAP_IN_NAME), values.get(SWAP_OUT_NAME)),
168
191
  values.get(PAGE_SIZE_NAME),
169
192
  ),
170
193
  pressure_level: values.get(PRESSURE_NAME),
194
+ compressed_mb: mb_of(values.get(COMPRESSED_NAME), 1),
171
195
  }
172
196
  }
173
197
 
@@ -187,7 +211,7 @@ function pages_of(pattern: RegExp, vmstat: string): number | undefined {
187
211
  function parse_linux_swapped(vmstat: string, page_bytes: number): number | undefined {
188
212
  const pages = swapped_pages(pages_of(LINUX_SWAP_IN, vmstat), pages_of(LINUX_SWAP_OUT, vmstat))
189
213
 
190
- return swapped_mb(pages, page_bytes)
214
+ return mb_of(pages, page_bytes)
191
215
  }
192
216
 
193
217
  async function settled<T>(read: () => Promise<T>): Promise<T | undefined> {
@@ -256,16 +280,47 @@ async function default_sleep(ms: number): Promise<void> {
256
280
  })
257
281
  }
258
282
 
283
+ // Whether a counter gained more than `limit` MB across the window; an unread counter gained nothing.
284
+ function moved_over(from: number | undefined, to: number | undefined, limit: number): boolean {
285
+ return (swapped_between(from, to) ?? 0) > limit
286
+ }
287
+
288
+ function is_pressured(before: MemoryReading, after: MemoryReading): boolean {
289
+ return (
290
+ (after.pressure_level ?? 0) >= PRESSURE_WARNING ||
291
+ moved_over(before.swapped_mb, after.swapped_mb, SWAP_PRESSURE_MB) ||
292
+ moved_over(before.compressed_mb, after.compressed_mb, COMPRESSION_PRESSURE_MB)
293
+ )
294
+ }
295
+
296
+ // **Memory under compression and swap pressure has nothing free to admit against.** The level counts
297
+ // compressible pages as available, so on 2026-10-16 it read 5.4 GB free while the compressor held
298
+ // 5.9 GB and compressed and decompressed about 3.3 GB every three seconds, and 5.2 GB of reservations
299
+ // were admitted on top. A reading the kernel calls pressured, or one with swap or compressor traffic
300
+ // across the window, answers no free memory: what the ledger holds keeps running and a heavy claim waits
301
+ // for the pressure to pass.
302
+ function pressured_available(before: MemoryReading, after: MemoryReading): number | undefined {
303
+ if (after.available_mb === undefined) return undefined
304
+
305
+ return is_pressured(before, after) ? 0 : after.available_mb
306
+ }
307
+
308
+ // Memory is read at both ends of the window, so the swap counter's difference is the activity during it.
259
309
  async function read_machine(
260
310
  sleep: (ms: number) => Promise<void> = default_sleep,
261
311
  ): Promise<MachineReading> {
262
312
  const before = cpu_times()
263
- const [memory] = await Promise.all([read_memory(), sleep(SAMPLE_WINDOW_MS)])
313
+ const [memory_before] = await Promise.all([read_memory(), sleep(SAMPLE_WINDOW_MS)])
314
+ const memory_after = await read_memory()
264
315
  const busy_cores = is_unquoted()
265
316
  ? busy_cores_between(before, cpu_times(), cpus().length)
266
317
  : undefined
267
318
 
268
- return { busy_cores, available_mb: memory.available_mb }
319
+ return {
320
+ busy_cores,
321
+ available_mb: pressured_available(memory_before, memory_after),
322
+ memory_pressured: is_pressured(memory_before, memory_after),
323
+ }
269
324
  }
270
325
 
271
326
  async function read_sample(): Promise<MachineSample> {
@@ -291,6 +346,7 @@ function machine_budget(cores: number, reading: MachineReading, ledger: LedgerLo
291
346
  cores: cores - external_cores(reading.busy_cores, ledger.cores),
292
347
  memory_mb:
293
348
  reading.available_mb === undefined ? Infinity : reading.available_mb + ledger.memory_mb,
349
+ memory_pressured: reading.memory_pressured,
294
350
  }
295
351
  }
296
352
 
@@ -298,14 +354,17 @@ const machine_capacity = {
298
354
  BASELINE_CORES,
299
355
  busy_cores_between,
300
356
  busy_share_between,
357
+ COMPRESSION_PRESSURE_MB,
301
358
  external_cores,
302
359
  machine_budget,
303
360
  parse_darwin_memory,
304
361
  parse_linux_available,
305
362
  parse_linux_swapped,
363
+ pressured_available,
306
364
  read_machine,
307
365
  read_memory,
308
366
  read_sample,
367
+ SWAP_PRESSURE_MB,
309
368
  swapped_between,
310
369
  }
311
370
 
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
3
3
  import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
4
4
  import { resolve_spawn_exit } from '#scripts/lib/spawn-exit'
5
5
  import { execa } from 'execa'
6
+ import { core_budget } from './core-budget'
6
7
  import { gate_skip } from './gate-skip'
7
8
  import { gate_tree, type GateTree } from './gate-tree'
8
9
  import { hook_gate_reuse } from './hook-gate-reuse'
@@ -143,6 +144,18 @@ async function run_type_check(): Promise<number> {
143
144
  return resolve_spawn_exit(PNPM, result)
144
145
  }
145
146
 
147
+ // **The type check claims the place `josh check` claims** (joshuafolkken/kit#3944), so lefthook's
148
+ // parallel commit hooks stop reaching other lanes' admission as foreign load. Only a miss reserves:
149
+ // a reused record runs nothing, and a claim there could make a commit wait out a full ledger for a
150
+ // check it then skips.
151
+ async function run_reserved_type_check(): Promise<number> {
152
+ return await core_budget.with_command_reservation(
153
+ core_budget.CORE_WEIGHTS.type_check,
154
+ run_type_check,
155
+ { memory_mb: core_budget.MEMORY_MB.type_check, command: 'pre-commit type check' },
156
+ )
157
+ }
158
+
146
159
  function unexpected_arguments(extra_arguments: ReadonlyArray<string>): ReadonlyArray<string> {
147
160
  return extra_arguments.filter((argument) => argument !== gate_skip.FORCE_FLAG)
148
161
  }
@@ -161,7 +174,7 @@ async function run_pre_commit_type_check(
161
174
 
162
175
  const reusable = await reusable_green_commit(await read_commit_tree(), extra_arguments, source)
163
176
 
164
- if (reusable === undefined) return await run_type_check()
177
+ if (reusable === undefined) return await run_reserved_type_check()
165
178
 
166
179
  process.stdout.write(`${format_skip(reusable.taken_at)}\n`)
167
180