@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
@@ -2,16 +2,19 @@ import path from 'node:path'
2
2
  import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
3
3
  import { cost_transcript } from '#scripts/cost-runtime/cost-transcript'
4
4
  import { capped_print_part } from '#scripts/document/capped-print-part'
5
+ import { changed_paths_sync } from '#scripts/git/changed-paths-sync'
5
6
  import { hook_decision, type GuardRun } from '#scripts/josh/hook-decision'
6
7
  import { time_batch_guard, type GuardedCall } from '#scripts/time-runtime/time-batch-guard'
7
8
  import { time_bundle_call } from '#scripts/time-runtime/time-bundle-call'
9
+ import { time_search_call } from '#scripts/time-runtime/time-search-call'
8
10
  import { time_shell } from '#scripts/time-runtime/time-shell'
9
11
  import { time_spans, type Span } from '#scripts/time-runtime/time-spans'
10
12
  import { delegation_policy } from './delegation-policy'
11
13
 
12
14
  // How many files a run has read and not edited since its last delegated unit, counted off the
13
15
  // transcript rather than left to the agent's memory, so the threshold fires again on a second
14
- // accumulation. A delegation clears the set; a read adds to it; an edit exempts its file.
16
+ // accumulation. A delegation clears the set; a read adds to it; an edit exempts its file, as does
17
+ // the branch's own change since its merge base, which an earlier session of the run wrote.
15
18
 
16
19
  const READ_TOOLS: ReadonlySet<string> = new Set(['Read'])
17
20
  // Both spellings of the subagent tool are in use: `Task` (published) and `Agent` (this harness).
@@ -32,12 +35,6 @@ const CONTENT_READ_LABELS: ReadonlySet<string> = new Set(
32
35
  CONTENT_READ_COMMANDS.map((command) => time_shell.bash_label(command)),
33
36
  )
34
37
 
35
- // A search carries no file text but costs a turn, so searches count in turns: parallel ones count once.
36
- const SEARCH_COMMANDS: ReadonlyArray<string> = ['fd', 'find', 'grep', 'rg']
37
- const SEARCH_LABELS: ReadonlySet<string> = new Set(
38
- SEARCH_COMMANDS.map((command) => time_shell.bash_label(command)),
39
- )
40
-
41
38
  const { INVESTIGATOR_AGENT } = delegation_policy
42
39
 
43
40
  // The deny reason is the only text that reaches the model, so it names the count, command and return shape.
@@ -55,7 +52,8 @@ interface ReadTally {
55
52
  // The search-side `pending` / `pending_since`, reset by a delegation or a successful write.
56
53
  searches: number
57
54
  searches_since: number
58
- // Files successfully edited in the window; a read of one stays in the main line, so it never counts.
55
+ // Files successfully edited in the window or changed on the branch; a read of one stays in the main
56
+ // line, so it never counts.
59
57
  edited: ReadonlySet<string>
60
58
  }
61
59
 
@@ -187,10 +185,6 @@ interface Accumulation {
187
185
  searches: Map<string, number>
188
186
  }
189
187
 
190
- function is_read_only_search(span: Span): boolean {
191
- return SEARCH_LABELS.has(span.label) && span.is_bundleable && !span.may_write
192
- }
193
-
194
188
  // A span with no message id is a turn of its own, as in every other turn counter.
195
189
  function search_turn_key(searches: ReadonlyMap<string, number>, span: Span): string {
196
190
  if (span.message_id !== time_spans.NO_MESSAGE_ID) return span.message_id
@@ -209,19 +203,27 @@ function apply_search(searches: Map<string, number>, span: Span): void {
209
203
 
210
204
  const turn = search_turn_key(searches, span)
211
205
 
212
- if (is_read_only_search(span) && !searches.has(turn)) searches.set(turn, span.ended_ms)
206
+ // A search carries no file text but costs a turn, so searches count in turns: parallel ones count once.
207
+ if (time_search_call.is_read_only_search(span) && !searches.has(turn)) {
208
+ searches.set(turn, span.ended_ms)
209
+ }
210
+ }
211
+
212
+ // A refused dispatch (`investigator-foreground`, `agent-model`) ran no unit, so it resets nothing.
213
+ function is_delegation(span: Span): boolean {
214
+ return DELEGATION_TOOLS.has(span.label) && !is_failed_outcome(span)
213
215
  }
214
216
 
215
217
  function apply_span(accumulation: Accumulation, span: Span, edited: ReadonlySet<string>): void {
216
- if (DELEGATION_TOOLS.has(span.label)) {
218
+ if (is_failed_outcome(span)) return
219
+
220
+ if (is_delegation(span)) {
217
221
  accumulation.pending.clear()
218
222
  accumulation.searches.clear()
219
223
 
220
224
  return
221
225
  }
222
226
 
223
- if (is_failed_outcome(span)) return
224
-
225
227
  apply_search(accumulation.searches, span)
226
228
 
227
229
  if (!is_content_read(span.label)) return
@@ -232,7 +234,7 @@ function apply_span(accumulation: Accumulation, span: Span, edited: ReadonlySet<
232
234
  }
233
235
 
234
236
  function last_delegation_ms(spans: ReadonlyArray<Span>): number {
235
- return delegation_instant(spans.findLast((span) => DELEGATION_TOOLS.has(span.label)))
237
+ return delegation_instant(spans.findLast((span) => is_delegation(span)))
236
238
  }
237
239
 
238
240
  // The window is the tail the hook read; the set clears at every delegation, so the tail suffices.
@@ -240,9 +242,15 @@ function pending_after(pending: ReadonlyMap<string, number>, since_ms: number):
240
242
  return [...pending.values()].filter((at_ms) => at_ms > since_ms).length
241
243
  }
242
244
 
243
- function tally_of(text: string, since_ms: number = hook_decision.NEVER_MS): ReadTally {
245
+ // `branch_files` (repository-relative) are the branch's own change since its merge base: an earlier
246
+ // session of this run wrote them, and a resumed session's transcript holds none of those writes.
247
+ function tally_of(
248
+ text: string,
249
+ since_ms: number = hook_decision.NEVER_MS,
250
+ branch_files: ReadonlyArray<string> = [],
251
+ ): ReadTally {
244
252
  const { spans, started_ms } = time_spans.parse_timeline(text)
245
- const edited = edited_files(spans)
253
+ const edited = new Set([...edited_files(spans), ...subject_targets(branch_files)])
246
254
  const accumulation: Accumulation = { pending: new Map(), searches: new Map() }
247
255
 
248
256
  for (const span of spans) apply_span(accumulation, span, edited)
@@ -313,7 +321,9 @@ function is_read_only_command(call: GuardedCall, labels: ReadonlySet<string>): b
313
321
 
314
322
  // A search is refusable without a subject target: a bare-directory sweep is what this count is for.
315
323
  function is_search_call(call: GuardedCall): boolean {
316
- return is_read_only_command(call, SEARCH_LABELS)
324
+ return time_search_call.is_read_only_search(
325
+ time_spans.to_tool_call(call.name, call.input, time_spans.NO_MESSAGE_ID),
326
+ )
317
327
  }
318
328
 
319
329
  // This guard counts reading, so a `Bash` line that also writes is never refused, and neither is a call
@@ -335,6 +345,20 @@ function is_growing_past_threshold(tally: ReadTally, call: GuardedCall): boolean
335
345
  return projected_count(tally.pending, call, tally.edited) > tally.pending.length
336
346
  }
337
347
 
348
+ function is_blocking(tally: ReadTally, call: GuardedCall, refused_at_ms: number): boolean {
349
+ return is_growing_past_threshold(tally, call) && is_rearmed(tally, refused_at_ms)
350
+ }
351
+
352
+ // The branch diff is a git read, so it is asked only of a call the transcript alone would refuse; more
353
+ // edited files can only lower the counts, so a call that passes without it passes with it.
354
+ function is_blocking_past_branch(text: string, call: GuardedCall, refused_at_ms: number): boolean {
355
+ if (!is_blocking(tally_of(text, refused_at_ms), call, refused_at_ms)) return false
356
+
357
+ const branch_files = changed_paths_sync.read_changed_paths()
358
+
359
+ return is_blocking(tally_of(text, refused_at_ms, branch_files), call, refused_at_ms)
360
+ }
361
+
338
362
  function should_block(
339
363
  text: string,
340
364
  call: GuardedCall,
@@ -344,9 +368,7 @@ function should_block(
344
368
  // A delegated unit is never refused: the remedy — send the reading to a unit — does not exist inside one.
345
369
  if (!is_refusable_call(call) || is_unit_run(run)) return false
346
370
 
347
- const tally = tally_of(text, refused_at_ms)
348
-
349
- return is_growing_past_threshold(tally, call) && is_rearmed(tally, refused_at_ms)
371
+ return is_blocking_past_branch(text, call, refused_at_ms)
350
372
  }
351
373
 
352
374
  const investigation_reads = {
@@ -1,5 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs'
1
+ import { existsSync, readFileSync } from 'node:fs'
2
2
  import path from 'node:path'
3
+ import { file_content } from '#scripts/sync/file-content'
3
4
  import {
4
5
  DEFAULT_RELEASE_AGE_MINUTES,
5
6
  release_window,
@@ -184,7 +185,7 @@ function sync_dependabot_cooldown(root: string): boolean {
184
185
  const existing = readFileSync(config_path, 'utf8')
185
186
  const merged = merge_cooldown(existing, release_window.read_release_window(root))
186
187
  if (merged === existing) return false
187
- writeFileSync(config_path, merged)
188
+ file_content.write_contained(config_path, merged)
188
189
 
189
190
  return true
190
191
  }
@@ -1,5 +1,6 @@
1
1
  import { repo_discovery } from './repo-discovery'
2
- import { repo_origin } from './repo-origin'
2
+ import { repo_origin, type RepoIdentity } from './repo-origin'
3
+ import { session_owner_pin } from './session-owner-pin'
3
4
 
4
5
  // **First-party vs third-party, computed rather than judged**. `CLAUDE.md`
5
6
  // and `upstream-interrupt.md` both declared the test mechanical — owner equality between the target
@@ -32,45 +33,100 @@ function classify(session_owner: string | undefined, target: string | undefined)
32
33
  // The owner half of a `owner/repo` argument, parsed through the remote-URL reader so a `.git` suffix
33
34
  // and a malformed form (one segment, three segments) are handled by the one place that already knows
34
35
  // them rather than by a second split here.
36
+ function parse_owner_repo(owner_repo: string): RepoIdentity | undefined {
37
+ return repo_origin.parse_origin_url(`https://${repo_origin.GITHUB_HOST}/${owner_repo}`)
38
+ }
39
+
35
40
  function target_owner(owner_repo: string): string | undefined {
36
- return repo_origin.parse_origin_url(`https://${repo_origin.GITHUB_HOST}/${owner_repo}`)?.owner
41
+ return parse_owner_repo(owner_repo)?.owner
37
42
  }
38
43
 
39
- // The owner of the repository the session runs in, read locally from its `origin` — no network call
40
- // and no authenticated CLI, so the guard that consults it stays synchronous.
44
+ // The owner of the repository the session runs in: the one pinned at session start, else read
45
+ // locally from its `origin` — no network call and no authenticated CLI, so the guard that consults it
46
+ // stays synchronous. The pin is what keeps a mid-session remote rewrite from changing the answer
47
+ // (`session-owner-pin.ts`).
41
48
  function current_owner(repository_path: string = process.cwd()): string | undefined {
42
- return repo_discovery.resolve_current_owner(repository_path)
49
+ return (
50
+ session_owner_pin.pinned(repository_path) ??
51
+ repo_discovery.resolve_current_owner(repository_path)
52
+ )
53
+ }
54
+
55
+ // The repository a `[HOST/]OWNER/REPO` selector names — `GH_REPO`, or `gh`'s `-R` / `--repo` value —
56
+ // read from its last two segments so an optional host prefix is skipped.
57
+ function selector_repository(selector: string): RepoIdentity | undefined {
58
+ return parse_owner_repo(selector.split('/').slice(-GH_REPO_PATH_SEGMENTS).join('/'))
43
59
  }
44
60
 
45
- // The owner `GH_REPO` names, read from its last two segments so an optional host prefix is skipped.
46
- function gh_repo_owners(gh_repo: string): Array<string | undefined> {
47
- return [target_owner(gh_repo.split('/').slice(-GH_REPO_PATH_SEGMENTS).join('/'))]
61
+ function selector_owner(selector: string): string | undefined {
62
+ return selector_repository(selector)?.owner
48
63
  }
49
64
 
50
- // The owner of every remote the work tree declares. A remote whose owner cannot be read stays in the
51
- // set as `undefined` rather than being dropped: an SSH host alias (`git@github-work:o/r.git`) is one
52
- // `gh` translates to github.com and may pick, so dropping it would hide the owner `gh` writes to.
53
- function remote_owners(repository_path: string): Array<string | undefined> {
65
+ // Every remote the work tree declares. A remote that cannot be read stays in the set as `undefined`
66
+ // rather than being dropped: an SSH host alias (`git@github-work:o/r.git`) is one `gh` translates to
67
+ // github.com and may pick, so dropping it would hide the repository `gh` writes to.
68
+ function remote_repositories(repository_path: string): Array<RepoIdentity | undefined> {
54
69
  return repo_discovery
55
70
  .read_remote_urls(repository_path)
56
- .map((url) => repo_origin.parse_origin_url(url)?.owner)
71
+ .map((url) => repo_origin.parse_origin_url(url))
57
72
  }
58
73
 
59
- // Every owner `gh` could expand the `{owner}` placeholder to. `GH_REPO`
74
+ // Every repository `gh` could expand the `{owner}/{repo}` placeholders to. `GH_REPO`
60
75
  // (`[HOST/]OWNER/REPO`) overrides the remotes outright; otherwise `gh` picks its base repository among
61
76
  // the GitHub remotes — `upstream` ahead of `origin`, or a `gh repo set-default` choice — so in a fork
62
- // checkout it can expand to the upstream owner. Answering the whole candidate set rather than
77
+ // checkout it can expand to the upstream repository. Answering the whole candidate set rather than
63
78
  // re-implementing that pick keeps the caller safe whichever remote `gh` chooses. An `undefined` entry
64
- // is a candidate whose owner could not be read, which the caller must not assume is first-party.
79
+ // is a candidate that could not be read, which the caller must not assume is first-party.
80
+ function placeholder_repositories(
81
+ repository_path: string,
82
+ environment: NodeJS.ProcessEnv,
83
+ ): Array<RepoIdentity | undefined> {
84
+ const gh_repo = environment[GH_REPO_ENV_KEY]
85
+
86
+ return gh_repo === undefined || gh_repo === ''
87
+ ? remote_repositories(repository_path)
88
+ : [selector_repository(gh_repo)]
89
+ }
90
+
65
91
  function placeholder_owners(
66
92
  repository_path: string = process.cwd(),
67
93
  environment: NodeJS.ProcessEnv = process.env,
68
94
  ): Array<string | undefined> {
69
- const gh_repo = environment[GH_REPO_ENV_KEY]
95
+ return placeholder_repositories(repository_path, environment).map((identity) => identity?.owner)
96
+ }
70
97
 
71
- return gh_repo === undefined || gh_repo === ''
72
- ? remote_owners(repository_path)
73
- : gh_repo_owners(gh_repo)
98
+ // Two spellings of one repository, compared case-insensitively as GitHub resolves them.
99
+ function is_same_repository(left: RepoIdentity | undefined, right: RepoIdentity): boolean {
100
+ if (left === undefined || classify(left.owner, right.owner) !== FIRST_PARTY) return false
101
+
102
+ return left.repo.toLowerCase() === right.repo.toLowerCase()
103
+ }
104
+
105
+ // The one repository every candidate names, or nothing when there is none, one is unreadable, or two
106
+ // differ — a fork checkout whose `upstream` `gh` may pick ahead of `origin`.
107
+ function sole_repository(
108
+ candidates: ReadonlyArray<RepoIdentity | undefined>,
109
+ ): RepoIdentity | undefined {
110
+ const [first, ...rest] = candidates
111
+ if (first === undefined) return undefined
112
+
113
+ return rest.every((candidate) => is_same_repository(candidate, first)) ? first : undefined
114
+ }
115
+
116
+ // The checkout's own repository: the one `{owner}/{repo}` expands to — where `josh issue:comment`
117
+ // posts. Nothing unless that is one repository whichever candidate `gh` picks, so a spelled-out
118
+ // repository is never read as the one `issue:comment` reaches when it might not be
119
+ // (joshuafolkken/kit#4737).
120
+ function current_repository(
121
+ repository_path: string = process.cwd(),
122
+ environment: NodeJS.ProcessEnv = process.env,
123
+ ): RepoIdentity | undefined {
124
+ return sole_repository(placeholder_repositories(repository_path, environment))
125
+ }
126
+
127
+ // Whether `target` is the checkout's own repository `current_repository` answered.
128
+ function is_own_repository(target: RepoIdentity, own: RepoIdentity | undefined): boolean {
129
+ return is_same_repository(own, target)
74
130
  }
75
131
 
76
132
  const repo_party = {
@@ -79,7 +135,11 @@ const repo_party = {
79
135
  UNKNOWN,
80
136
  classify,
81
137
  current_owner,
138
+ current_repository,
139
+ is_own_repository,
82
140
  placeholder_owners,
141
+ selector_owner,
142
+ sole_repository,
83
143
  target_owner,
84
144
  }
85
145
 
@@ -0,0 +1,51 @@
1
+ import { stamp_file } from '#scripts/josh/stamp-file'
2
+ import { repo_discovery } from './repo-discovery'
3
+
4
+ // **The session's owner, fixed when the session starts** (joshuafolkken/kit#4659). The first-party
5
+ // test compares a target's owner with the session's, and the session's was read from `origin` on
6
+ // every call — so `git remote set-url origin https://github.com/<other>/x` mid-session made another
7
+ // owner first-party. The session-start hook records the owner once; `repo_party.current_owner`
8
+ // answers the record ahead of the live remote. Only the session's owner is pinned: the `{owner}`
9
+ // placeholder candidates and a selector-less `gh` write still resolve against the live remotes.
10
+ //
11
+ // **Written by the process the session start already runs** (`audit-provision --session-start`), so
12
+ // pinning adds no spawn to the launch, and read as one local file by the hooks that already run — no
13
+ // network call. A checkout with no record (a session the hook did not start) falls back to `origin`.
14
+ //
15
+ // **Per checkout, replaced at every session start.** The record is the stamp `stamp-file.ts` keeps in
16
+ // the temp directory, keyed on the account and the checkout path; a checkout whose origin owner
17
+ // cannot be read at start leaves no record rather than a stale one. A command run outside a session
18
+ // reads the last session's record for that path, which the next session start replaces.
19
+
20
+ const PIN_PREFIX = 'josh-session-owner-'
21
+ const PIN_SUFFIX = '.txt'
22
+
23
+ function pin_path(repository_path: string): string {
24
+ return stamp_file.stamp_path(PIN_PREFIX, repository_path, PIN_SUFFIX)
25
+ }
26
+
27
+ // Records the checkout's `origin` owner for the session about to run. Never throws: this runs at
28
+ // session start beside the scanner provisioning, and an unwritable temp directory only costs the pin.
29
+ function pin(repository_path: string = process.cwd()): string | undefined {
30
+ const owner = repo_discovery.resolve_current_owner(repository_path)
31
+
32
+ try {
33
+ if (owner === undefined) stamp_file.remove_stamp(pin_path(repository_path))
34
+ else stamp_file.write_text_stamp(pin_path(repository_path), owner)
35
+ } catch {
36
+ return undefined
37
+ }
38
+
39
+ return owner
40
+ }
41
+
42
+ // The owner recorded at session start, or `undefined` when the checkout holds no readable record.
43
+ function pinned(repository_path: string = process.cwd()): string | undefined {
44
+ const text = stamp_file.read_stamp_text(pin_path(repository_path))?.trim()
45
+
46
+ return text === '' ? undefined : text
47
+ }
48
+
49
+ const session_owner_pin = { pin, pin_path, pinned }
50
+
51
+ export { session_owner_pin }
@@ -68,6 +68,6 @@ function capped_print(text: string, label: string, root: string = process.cwd())
68
68
  console.info(listing(text, budget, write_parts(label, text, budget)))
69
69
  }
70
70
 
71
- const capped_output = { HEADROOM_CHARS, capped_print }
71
+ const capped_output = { HEADROOM_CHARS, capped_print, output_budget }
72
72
 
73
73
  export { capped_output }
@@ -0,0 +1,42 @@
1
+ // The long-line ceiling of the command reference — one source for `command-reference.test.ts` and the
2
+ // fast `josh lint:related` path (`document-ratchet-check.ts`), so the two can never hold different
3
+ // lines. A reference entry is a short summary; the why behind it lives in the page's maintainer
4
+ // rationale file. The pages once carried 137 lines over 400 characters (joshuafolkken/kit#3868); the
5
+ // ceiling is a ratchet at the long lines left after condensing them, so lowering it is the only way
6
+ // it moves.
7
+
8
+ // The only files under `docs/` an agent is routed to read — the four command references; the rest of
9
+ // `docs/` is reference a human browses, so the byte budget (`document/document-byte-budget.ts`)
10
+ // covers these alone.
11
+ const COMMAND_REFERENCE_DOCS: ReadonlyArray<string> = [
12
+ 'docs/josh-commands.md',
13
+ 'docs/josh-commands-automation.md',
14
+ 'docs/josh-commands-run.md',
15
+ 'docs/josh-commands-backlog.md',
16
+ ]
17
+ const LONG_LINE_CHARACTERS = 400
18
+ const MAX_LONG_LINES = 3
19
+
20
+ // The lines over the length limit across every page handed in — the ceiling holds the pages
21
+ // together, so one page's cut makes room in another.
22
+ function long_line_count(texts: ReadonlyArray<string>): number {
23
+ return texts
24
+ .flatMap((text) => text.split('\n'))
25
+ .filter((line) => line.length > LONG_LINE_CHARACTERS).length
26
+ }
27
+
28
+ // The failure for a count over the ceiling, or undefined at or under it.
29
+ function long_line_violation(count: number): string | undefined {
30
+ if (count <= MAX_LONG_LINES) return undefined
31
+
32
+ return `the command reference (${COMMAND_REFERENCE_DOCS.join(', ')}) has ${count.toString()} lines over ${LONG_LINE_CHARACTERS.toString()} characters, over its ceiling of ${MAX_LONG_LINES.toString()}. Shorten the entry — the why belongs in the page's maintainer rationale file.`
33
+ }
34
+
35
+ const command_reference_line_budget = {
36
+ COMMAND_REFERENCE_DOCS,
37
+ long_line_count,
38
+ long_line_violation,
39
+ MAX_LONG_LINES,
40
+ }
41
+
42
+ export { command_reference_line_budget }
@@ -73,10 +73,18 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
73
73
  { path: '.claude/skills/workflow-commands/prerequisite.md', bytes: 8192 },
74
74
  { path: '.claude/skills/workflow-commands/target-repository.md', bytes: 4096 },
75
75
  { path: '.claude/skills/workflow-commands/working-tree-hold.md', bytes: 8192 },
76
- { path: 'docs/josh-commands-automation.md', bytes: 45_056 },
77
- { path: 'docs/josh-commands-run.md', bytes: 69_632 },
78
- { path: 'docs/josh-commands-backlog.md', bytes: 57_344 },
79
- { path: 'docs/josh-commands.md', bytes: 53_248 },
76
+ // `command:derive`'s usage section (joshuafolkken/kit#4970) crossed the 45056 block.
77
+ { path: 'docs/josh-commands-automation.md', bytes: 49_152 },
78
+ // The carried `--unpinned-ok` record (joshuafolkken/kit#4775) crossed the 73728 block.
79
+ // `run:merge`'s withdrawn child (joshuafolkken/kit#4916) crossed the 77824 block.
80
+ // `lane:plan --probe` and the entry probe (joshuafolkken/kit#5211) crossed the 81920 block.
81
+ { path: 'docs/josh-commands-run.md', bytes: 86_016 },
82
+ // `josh effect:audit` (joshuafolkken/kit#4958) and `issue:priority`'s usage section (joshuafolkken/kit#4960) crossed the 57344 block;
83
+ // `josh prose:stale` (joshuafolkken/kit#5287) crossed the 61440 block.
84
+ { path: 'docs/josh-commands-backlog.md', bytes: 65_536 },
85
+ // `run:merge` withdrawing a lane closed without a merge (joshuafolkken/kit#4916) crossed the 53248 block;
86
+ // `gate --static-group` (joshuafolkken/kit#5215) crossed the 57344 block.
87
+ { path: 'docs/josh-commands.md', bytes: 61_440 },
80
88
  { path: 'docs/labels-and-run-states.md', bytes: 8192 },
81
89
  { path: 'prompts/coding-standards.md', bytes: 4096 },
82
90
  { path: 'prompts/collaboration-workflow.md', bytes: 4096 },
@@ -84,8 +92,9 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
84
92
  { path: 'prompts/collaboration-workflow/gh-rest.md', bytes: 4096 },
85
93
  { path: 'prompts/collaboration-workflow/glossary.md', bytes: 4096 },
86
94
  { path: 'prompts/collaboration-workflow/issue-citation.md', bytes: 4096 },
87
- // The priority declaration and its criteria (joshuafolkken/kit#4383) crossed the 8192 block.
88
- { path: 'prompts/collaboration-workflow/issue-template.md', bytes: 12_288 },
95
+ // The priority declaration and its criteria (joshuafolkken/kit#4383) crossed the 8192 block; the
96
+ // causing-lane declaration and criteria F / G (joshuafolkken/kit#4749) crossed the 12288 one.
97
+ { path: 'prompts/collaboration-workflow/issue-template.md', bytes: 16_384 },
89
98
  { path: 'prompts/collaboration-workflow/lane-instructions.md', bytes: 4096 },
90
99
  { path: 'prompts/collaboration-workflow/measurement.md', bytes: 4096 },
91
100
  { path: 'prompts/collaboration-workflow/operating-rules.md', bytes: 8192 },
@@ -103,7 +112,8 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
103
112
  { path: 'prompts/collaboration-workflow/wip-cap.md', bytes: 8192 },
104
113
  { path: 'prompts/refactoring.md', bytes: 8192 },
105
114
  { path: 'prompts/review-rubric.md', bytes: 12_288 },
106
- { path: 'prompts/review.md', bytes: 12_288 },
115
+ // Self-check 2's `josh prose:stale` pointer (joshuafolkken/kit#5287) crossed the 12288 block.
116
+ { path: 'prompts/review.md', bytes: 16_384 },
107
117
  { path: 'prompts/sonar-hotspot-handling.md', bytes: 8192 },
108
118
  { path: 'prompts/testing-guide.md', bytes: 12_288 },
109
119
  ]
@@ -0,0 +1,20 @@
1
+ import { document_byte_check } from './document-byte-check'
2
+ import { document_ratchet_check } from './document-ratchet-check'
3
+
4
+ // The document checks `josh lint:related` brings forward from the gate's tests, as one call: the
5
+ // byte ceilings (`document-byte-check.ts`) and the ratchets (`document-ratchet-check.ts`). Both run
6
+ // every time, so one edit reports every ceiling it broke in one pass rather than one per run.
7
+
8
+ const CHECKS = [document_byte_check, document_ratchet_check]
9
+
10
+ function check_files(root: string, absolute_files: ReadonlyArray<string>): number {
11
+ return Math.max(...CHECKS.map((check) => check.check_files(root, absolute_files)))
12
+ }
13
+
14
+ function check_all(root: string): number {
15
+ return Math.max(...CHECKS.map((check) => check.check_all(root)))
16
+ }
17
+
18
+ const document_checks = { check_all, check_files }
19
+
20
+ export { document_checks }
@@ -0,0 +1,101 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import path from 'node:path'
3
+ import { project_checks } from '#scripts/gate/project-checks'
4
+ import { command_reference_line_budget } from './command-reference-line-budget'
5
+ import { issue_citation_budget } from './issue-citation-budget'
6
+
7
+ // The two document ratchets the gate's tests hold — the command reference's long-line ceiling
8
+ // (`command-reference.test.ts`) and each agent-read document's issue-citation count
9
+ // (`issue-citation-budget.test.ts`) — made reachable on the fast between-edits path beside
10
+ // `document-byte-check.ts`, so a documentation edit that breaks either surfaces in seconds rather
11
+ // than in a minutes-long `check:related` (joshuafolkken/kit#4986). The ceilings, the counting and the
12
+ // messages are the budget modules' alone; this adds none of its own.
13
+ //
14
+ // It reads no change of its own: `lint:related` hands in the files it already resolved. The citation
15
+ // check holds the documents the budget records; a document with no entry, and the whole-list checks
16
+ // (a stale or a zero entry), stay with the gate's test, which knows the agent-read set.
17
+ //
18
+ // Both ratchets are kit's: the budget records kit's paths, and a consumer's own file at one of them
19
+ // holds its own count, so a consumer root is checked by neither.
20
+
21
+ const LABEL = 'document ratchet'
22
+
23
+ function read_text(absolute_path: string): string | undefined {
24
+ return existsSync(absolute_path) ? readFileSync(absolute_path, 'utf8') : undefined
25
+ }
26
+
27
+ // The long-line failure across the whole command reference, which the ceiling holds together.
28
+ function long_line_message(root: string): string | undefined {
29
+ const texts = command_reference_line_budget.COMMAND_REFERENCE_DOCS.map((relative_path) =>
30
+ read_text(path.join(root, relative_path)),
31
+ ).filter((text): text is string => text !== undefined)
32
+
33
+ return command_reference_line_budget.long_line_violation(
34
+ command_reference_line_budget.long_line_count(texts),
35
+ )
36
+ }
37
+
38
+ function touches_command_reference(root: string, absolute_files: ReadonlyArray<string>): boolean {
39
+ const references = new Set(command_reference_line_budget.COMMAND_REFERENCE_DOCS)
40
+
41
+ return absolute_files.some((absolute_path) => references.has(path.relative(root, absolute_path)))
42
+ }
43
+
44
+ // The citation failure for one file the budget records, or undefined when it holds its count, has no
45
+ // entry, or is gone from the tree.
46
+ function citation_message(root: string, absolute_path: string): string | undefined {
47
+ const relative_path = path.relative(root, absolute_path)
48
+ const recorded = issue_citation_budget.recorded_citations_for(relative_path)
49
+ const text = read_text(absolute_path)
50
+ if (recorded === 0 || text === undefined) return undefined
51
+
52
+ const current = issue_citation_budget.count_citations(text)
53
+
54
+ return issue_citation_budget.citation_violation(relative_path, current, recorded)
55
+ }
56
+
57
+ function citation_messages(
58
+ root: string,
59
+ absolute_files: ReadonlyArray<string>,
60
+ ): ReadonlyArray<string> {
61
+ return absolute_files
62
+ .map((absolute_path) => citation_message(root, absolute_path))
63
+ .filter((message): message is string => message !== undefined)
64
+ }
65
+
66
+ // Prints one labelled line per failure and answers non-zero, or stays silent and answers zero.
67
+ function report(messages: ReadonlyArray<string | undefined>): number {
68
+ const lines = messages
69
+ .filter((message): message is string => message !== undefined)
70
+ .map((message) => `${LABEL}: ${message}`)
71
+ if (lines.length === 0) return 0
72
+
73
+ process.stdout.write(`${lines.join('\n')}\n`)
74
+
75
+ return 1
76
+ }
77
+
78
+ // The check over a resolved change: the long-line ceiling when it touches a command reference page,
79
+ // and the citation count of every changed file the budget records. An unrelated change reads nothing.
80
+ function check_files(root: string, absolute_files: ReadonlyArray<string>): number {
81
+ if (!project_checks.is_kit_repository(root)) return 0
82
+ const long_line = touches_command_reference(root, absolute_files)
83
+ ? long_line_message(root)
84
+ : undefined
85
+
86
+ return report([long_line, ...citation_messages(root, absolute_files)])
87
+ }
88
+
89
+ // The whole-tree fallback: the long-line ceiling and every recorded document, a superset of any change.
90
+ function check_all(root: string): number {
91
+ if (!project_checks.is_kit_repository(root)) return 0
92
+ const absolute_files = issue_citation_budget.ISSUE_CITATION_BUDGET.map((entry) =>
93
+ path.join(root, entry.path),
94
+ )
95
+
96
+ return report([long_line_message(root), ...citation_messages(root, absolute_files)])
97
+ }
98
+
99
+ const document_ratchet_check = { check_all, check_files }
100
+
101
+ export { document_ratchet_check }
@@ -262,6 +262,7 @@ function read_optional(file_path: string): string | undefined {
262
262
 
263
263
  const document_section = {
264
264
  candidates,
265
+ fenced_flags,
265
266
  headings,
266
267
  read_optional,
267
268
  section,
@@ -34,6 +34,9 @@ const { block_ceiling } = document_byte_budget
34
34
  const { LANE_CHILD } = lane_child_read_set
35
35
  const { BACKLOGRUN } = backlogrun_parent_read_set
36
36
  const NOTHING = 0
37
+ // The command that re-measures every entry total in seconds, named by the over-budget message so a
38
+ // trim is checked without re-running the whole lint or gate.
39
+ const RECHECK_COMMAND = 'pnpm josh bytes'
37
40
 
38
41
  interface EntryBudget {
39
42
  entry: string
@@ -46,8 +49,8 @@ interface EntryBudget {
46
49
  const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
47
50
  { entry: 'kickoff', bytes: 36_864 },
48
51
  { entry: 'fullrun', bytes: 53_248 },
49
- { entry: 'halfrun', bytes: 49_152 },
50
- { entry: 'prrun', bytes: 53_248 },
52
+ { entry: 'halfrun', bytes: 45_056 },
53
+ { entry: 'prrun', bytes: 57_344 },
51
54
  { entry: 'backlogrun', bytes: 81_920 },
52
55
  { entry: LANE_CHILD, bytes: 45_056 },
53
56
  ]
@@ -95,7 +98,7 @@ function over_budget_message(entry: string, current_bytes: number, recorded_byte
95
98
  const next = block_ceiling(current_bytes).toString()
96
99
  const raise_hint = `raise its recorded size to ${next} in scripts/document/entry-read-budget.ts so the reason shows in the PR diff`
97
100
 
98
- return `entry ${entry} reads ${current_bytes.toString()} bytes, over its ${recorded_bytes.toString()}-byte ceiling. Trim what it reads, or ${raise_hint}.`
101
+ return `entry ${entry} reads ${current_bytes.toString()} bytes, over its ${recorded_bytes.toString()}-byte ceiling. Trim what it reads, or ${raise_hint}. Recheck with \`${RECHECK_COMMAND}\`.`
99
102
  }
100
103
 
101
104
  // The stale-ratchet message: a recorded ceiling more than a block above the entry's actual total was
@@ -113,6 +116,7 @@ function stale_budget_message(
113
116
 
114
117
  const entry_read_budget = {
115
118
  ENTRY_READ_BUDGET,
119
+ RECHECK_COMMAND,
116
120
  entry_total_bytes,
117
121
  over_budget_message,
118
122
  recorded_bytes_for,