@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,8 +2,10 @@
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { cost_transcript, type SessionFile } from '#scripts/cost-runtime/cost-transcript'
4
4
  import { delivered_rules } from './delivered-rules'
5
+ import { piped_verification_shapes, type ShapeCounts } from './piped-verification-shapes'
5
6
  import { rule_value, type RuleReading } from './rule-value'
6
- import { rule_value_cache } from './rule-value-cache'
7
+ import { rule_value_cache, type FreshReading } from './rule-value-cache'
8
+ import { rule_value_candidates } from './rule-value-candidates'
7
9
 
8
10
  // `josh rule:value` — what each delivered rule earns on the channel that carries it, one row per rule.
9
11
  // `rule_value.measure` was public but had no caller outside its own test, so
@@ -17,6 +19,8 @@ import { rule_value_cache } from './rule-value-cache'
17
19
 
18
20
  const ARGV_OFFSET = 2
19
21
  const REFRESH_FLAG = '--refresh'
22
+ const FRESH_FLAG = '--fresh'
23
+ const CACHED_NOTE = 'cached reading taken at'
20
24
  const PERCENT_SIGN = '%'
21
25
  const COLUMN_GAP = ' '
22
26
  const NO_TARGETS = 'no measurement targets'
@@ -70,8 +74,10 @@ function gather_runs(cwd: string = process.cwd()): Generator<Array<string>> {
70
74
  return runs_of(group_by_run(files).values())
71
75
  }
72
76
 
77
+ // The `piped-verification` refusals are named by shape in the same pass, so the corpus is read once.
73
78
  interface Tally {
74
79
  runs: number
80
+ shapes: ShapeCounts
75
81
  }
76
82
 
77
83
  function* counted(
@@ -80,6 +86,7 @@ function* counted(
80
86
  ): Generator<ReadonlyArray<string>> {
81
87
  for (const texts of runs) {
82
88
  tally.runs += 1
89
+ for (const text of texts) piped_verification_shapes.count(text, tally.shapes)
83
90
  yield texts
84
91
  }
85
92
  }
@@ -108,26 +115,65 @@ function row(reading: RuleReading): string {
108
115
  return [id, reached, rate_text(reading), refused, ...rewritten].join(COLUMN_GAP)
109
116
  }
110
117
 
118
+ // `candidates` is set only where something measurable was found, so `NO_TARGETS` stays the whole answer.
119
+ interface Rendered {
120
+ table: string
121
+ candidates?: string
122
+ }
123
+
111
124
  // The reading as text, or `NO_TARGETS` when nothing measurable was found — never an empty output that
112
125
  // would read as a broken command.
113
126
  // The runs are counted as `measure` consumes them, since a lazy source cannot be asked its length
114
127
  // up front.
115
- function render(runs: Iterable<ReadonlyArray<string>>): string {
116
- const tally: Tally = { runs: 0 }
128
+ function render_parts(runs: Iterable<ReadonlyArray<string>>): Rendered {
129
+ const tally: Tally = { runs: 0, shapes: new Map() }
117
130
  const readings = rule_value.measure(counted(runs, tally))
118
131
 
119
- if (tally.runs === 0) return NO_TARGETS
132
+ if (tally.runs === 0) return { table: NO_TARGETS }
120
133
 
121
- return readings.map((reading) => row(reading)).join('\n')
134
+ const judged = rule_value_candidates.judge(readings, tally.runs)
135
+ const shapes = piped_verification_shapes.render(tally.shapes)
136
+
137
+ return {
138
+ table: readings.map((reading) => row(reading)).join('\n'),
139
+ candidates: `${rule_value_candidates.render(judged, tally.runs)}\n${shapes}`,
140
+ }
122
141
  }
123
142
 
124
- // `--refresh` is the detached measurement the `backlogrun` loop head starts: the reading goes to the
125
- // cache `backlog:offer` prints from, never to stdout (`rule-value-cache.ts`).
126
- function run_rule_value(argv: ReadonlyArray<string> = [], cwd: string = process.cwd()): number {
127
- const reading = render(gather_runs(cwd))
143
+ function render(runs: Iterable<ReadonlyArray<string>>): string {
144
+ return render_parts(runs).table
145
+ }
146
+
147
+ // The cached table and when it was taken. The cache holds the table alone, so the line names the flag
148
+ // that measures now for the `candidates:` section too.
149
+ function cached_text(fresh: FreshReading): string {
150
+ const taken_at = fresh.taken_at.toISOString()
128
151
 
129
- if (argv.includes(REFRESH_FLAG)) rule_value_cache.store(reading, cwd)
130
- else process.stdout.write(`${reading}\n`)
152
+ return `${fresh.text.trimEnd()}\n\n${CACHED_NOTE} ${taken_at} — ${FRESH_FLAG} to measure now, with candidates`
153
+ }
154
+
155
+ function print_measured(cwd: string): void {
156
+ const { table, candidates } = render_parts(gather_runs(cwd))
157
+
158
+ process.stdout.write(`${[table, candidates].filter(Boolean).join('\n\n')}\n`)
159
+ }
160
+
161
+ function print_reading(argv: ReadonlyArray<string>, cwd: string): void {
162
+ const fresh = argv.includes(FRESH_FLAG) ? undefined : rule_value_cache.read_fresh(cwd)
163
+
164
+ if (fresh === undefined) print_measured(cwd)
165
+ else process.stdout.write(`${cached_text(fresh)}\n`)
166
+ }
167
+
168
+ // `--refresh` is the detached measurement the `backlogrun` loop head starts: the table goes to the
169
+ // cache `backlog:offer` prints from, never to stdout (`rule-value-cache.ts`). The candidates are read
170
+ // when a rule is retired, not at the loop head, so they print to stdout only and the cache keeps its
171
+ // shape. Bare, a reading cached inside the refresh interval is printed instead of measuring again —
172
+ // the measurement re-reads every transcript and takes minutes (joshuafolkken/kit#5186); `--fresh`
173
+ // measures regardless.
174
+ function run_rule_value(argv: ReadonlyArray<string> = [], cwd: string = process.cwd()): number {
175
+ if (argv.includes(REFRESH_FLAG)) rule_value_cache.store(render_parts(gather_runs(cwd)).table, cwd)
176
+ else print_reading(argv, cwd)
131
177
 
132
178
  return 0
133
179
  }
@@ -139,9 +185,11 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
139
185
  const rule_value_cli = {
140
186
  group_by_run,
141
187
  render,
188
+ render_parts,
142
189
  row,
143
190
  run_rule_value,
144
191
  runs_of,
192
+ CACHED_NOTE,
145
193
  NO_TARGETS,
146
194
  UNMEASURED,
147
195
  UNREACHED,
@@ -159,7 +159,8 @@ function observe_call(
159
159
  }
160
160
 
161
161
  // One transcript line, parsed once, as the things a reading is taken from: the calls it issued, the
162
- // bodies of the results the harness wrote back as failures, and the context a hook attached.
162
+ // refusals the harness wrote back (failed results and Stop-hook blocks), and the context a hook
163
+ // attached.
163
164
  interface DatedLine {
164
165
  at_ms: number
165
166
  message_id: string
@@ -183,6 +184,14 @@ function errors_of(parsed: TranscriptLine): Array<string> {
183
184
  .filter((text) => text !== '')
184
185
  }
185
186
 
187
+ // **A Stop-hook block is a refusal too** — of ending the turn — so its reason joins the
188
+ // tool results the harness wrote back as failures (joshuafolkken/kit#4978).
189
+ function refusals_of(parsed: TranscriptLine): Array<string> {
190
+ const errors = errors_of(parsed)
191
+
192
+ return parsed.stop_feedback === '' ? errors : [...errors, parsed.stop_feedback]
193
+ }
194
+
186
195
  function dated_line(line: string): DatedLine | undefined {
187
196
  const parsed = line === '' ? undefined : time_transcript_line.parse_line(line)
188
197
 
@@ -192,7 +201,7 @@ function dated_line(line: string): DatedLine | undefined {
192
201
  at_ms: parsed.timestamp_ms,
193
202
  message_id: parsed.message_id,
194
203
  calls: calls_of(parsed),
195
- errors: errors_of(parsed),
204
+ errors: refusals_of(parsed),
196
205
  contexts: hook_context_line.hook_contexts_of(line),
197
206
  }
198
207
  }
@@ -250,10 +259,14 @@ function turns_by_key(entries: ReadonlyArray<DatedLine>): Map<string, Array<Issu
250
259
  // each of them; the file opens with its imports, so under this test nothing claims it. Position is
251
260
  // what separates the two, so no count of how many signatures appear is needed — and a body naming
252
261
  // several can no longer be attributed to any of them.
262
+ //
263
+ // **The harness's own label is not the body.** It writes a deny as `PreToolUse:<Tool> hook error: ⛔ …`,
264
+ // so the label is stripped before the opening is read; compared raw, no recorded refusal matched
265
+ // (joshuafolkken/kit#4961).
253
266
  function refused_id(text: string): string | undefined {
254
- const body = text.trimStart()
267
+ const body = time_transcript_line.refusal_body(text)
255
268
  const found = delivered_rules.MEASURED_RULES.find((rule) =>
256
- [rule.reason, ...(rule.former_reasons ?? [])].some((reason) =>
269
+ [rule.reason, ...(rule.former_reasons ?? []), ...(rule.stop_reasons ?? [])].some((reason) =>
257
270
  body.startsWith(reason.slice(0, REASON_SIGNATURE_LENGTH)),
258
271
  ),
259
272
  )
@@ -35,7 +35,7 @@ import { shell_segments } from './shell-segments'
35
35
  // one command cannot disagree: an opening subshell parenthesis, environment assignments, and the
36
36
  // package-manager wrapper in either of its spellings. Left out, `JOSH_CI_TIMEOUT_SECONDS=600 pnpm
37
37
  // josh git -y` and `(pnpm josh git -y)` are silently not the push step, and the run pays the tail the
38
- // rule exists to stop. `g` is the alias `josh-command-map.ts` resolves to `git`.
38
+ // rule exists to stop. `g` is the alias `josh-aliases.ts` resolves to `git`.
39
39
  const SEGMENT_PREFIX = String.raw`(?:\(\s*)?(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*(?:npx\s+|pnpm\s+(?:exec\s+|run\s+)?)?`
40
40
  // A closing parenthesis ends a word as a space does, so every boundary below admits it — without
41
41
  // that, a subshell defeats the flag test even where it does not defeat the command test.
@@ -0,0 +1,196 @@
1
+ import { file_reader } from '#scripts/lib/read-file'
2
+ import { bash_triggers } from './bash-triggers'
3
+ import { git_argv } from './git-argv'
4
+ import { shell_segments } from './shell-segments'
5
+
6
+ // The trigger and the delivered text behind the `sed-in-place` row of `delivered-rules.ts`: a `sed -i`
7
+ // that `file-edits.md`'s countable conditions do not clear (joshuafolkken/kit#5284).
8
+ //
9
+ // **The conditions are counts, so the guard counts them.** `file-edits.md` allows `sed -i` only for a
10
+ // single `s` command whose pattern carries no meta character and matches exactly once in each target
11
+ // file. Anything else — a meta character, a count other than one, a script that is not one `s` command —
12
+ // can rewrite a line the run never meant to touch, and `Edit` is the tool that cannot.
13
+ //
14
+ // It fires on every occurrence, for the reason `file-body.ts` does.
15
+
16
+ const SED = 'sed'
17
+ // `-i`, `-i.bak`, `-Ei`: a short cluster carrying the in-place switch; `--in-place[=suffix]` long.
18
+ const IN_PLACE_SHORT = /^-[A-Za-z]*i/u
19
+ const IN_PLACE_LONG = /^--in-place(?:=|$)/u
20
+ // A short cluster ending in the in-place switch: BSD reads its suffix from the next word.
21
+ const IN_PLACE_CLUSTER = /^-[A-Za-z]*i$/u
22
+ // `-E`, `-r`, `--regexp-extended`: the pattern is read as an extended regex, where `+ ? ( ) { } |`
23
+ // are meta characters too, so a plain-text count no longer says what sed matches.
24
+ const EXTENDED_SHORT = /^-[A-Za-z]*[Er]/u
25
+ const EXTENDED_LONG = '--regexp-extended'
26
+ // The `s` flags that keep the match plain text: none, or `g` (each line holds one match at most when
27
+ // the count is one). `I`, `w file`, a number and the rest change what or where sed writes.
28
+ const PLAIN_FLAGS: ReadonlySet<string> = new Set(['', 'g'])
29
+ const SCRIPT_FLAG = '-e'
30
+ const FLAG_PREFIX = '-'
31
+ const SURROUNDING_QUOTES = /^(['"])(.*)\1$/su
32
+ const QUOTED_BODY = 2
33
+ // `file-edits.md`'s meta characters: `/ & \ . * [ ] ^ $`.
34
+ const META_CHARACTER = /[/&\\.*[\]^$]/u
35
+ const SUBSTITUTE = 's'
36
+ const SUBSTITUTE_PARTS = 3
37
+ const ONE_MATCH = 1
38
+
39
+ interface SedCall {
40
+ scripts: Array<string>
41
+ files: Array<string>
42
+ }
43
+
44
+ function unquoted(word: string): string {
45
+ return SURROUNDING_QUOTES.exec(word)?.[QUOTED_BODY] ?? word
46
+ }
47
+
48
+ function is_in_place_flag(word: string): boolean {
49
+ return IN_PLACE_LONG.test(word) || IN_PLACE_SHORT.test(word)
50
+ }
51
+
52
+ // The words after the in-place flags: BSD's `-i ''` carries its suffix as the next (empty) word.
53
+ function without_suffix(words: ReadonlyArray<string>): Array<string> {
54
+ const index = words.findIndex((word) => IN_PLACE_CLUSTER.test(word))
55
+
56
+ if (index === -1 || words[index + 1] !== '') return [...words]
57
+
58
+ return words.toSpliced(index + 1, 1)
59
+ }
60
+
61
+ function sed_words(segment: string): Array<string> | undefined {
62
+ const words = git_argv.arguments_of(segment, SED)?.map((word) => unquoted(word))
63
+
64
+ return words?.some((word) => is_in_place_flag(word)) === true ? without_suffix(words) : undefined
65
+ }
66
+
67
+ function is_script_value(words: ReadonlyArray<string>, index: number): boolean {
68
+ return words[index - 1] === SCRIPT_FLAG
69
+ }
70
+
71
+ // The scripts (`-e <script>`, or the first operand without one) and the files of an in-place `sed`.
72
+ function sed_call(words: ReadonlyArray<string>): SedCall {
73
+ const scripts = words.filter((_word, index) => is_script_value(words, index))
74
+ const operands = words.filter(
75
+ (word, index) => !word.startsWith(FLAG_PREFIX) && !is_script_value(words, index),
76
+ )
77
+
78
+ if (scripts.length > 0) return { scripts, files: operands }
79
+
80
+ const [script, ...files] = operands
81
+
82
+ return { scripts: script === undefined ? [] : [script], files }
83
+ }
84
+
85
+ // An `s` command split on its delimiter into the pattern, the replacement and the flags; `undefined`
86
+ // for any other script.
87
+ function substitute_parts(script: string): Array<string> | undefined {
88
+ if (!script.startsWith(SUBSTITUTE)) return undefined
89
+
90
+ const parts = script.slice(SUBSTITUTE.length + 1).split(script.charAt(SUBSTITUTE.length))
91
+
92
+ return parts.length === SUBSTITUTE_PARTS ? parts : undefined
93
+ }
94
+
95
+ // `file-edits.md` condition 2 reads both sides: no meta character in the pattern or the replacement,
96
+ // and no flag that turns the plain-text match into something else.
97
+ function has_plain_flags(parts: ReadonlyArray<string>): boolean {
98
+ return PLAIN_FLAGS.has(parts.at(-1) ?? '')
99
+ }
100
+
101
+ function is_plain(parts: ReadonlyArray<string>): boolean {
102
+ const [pattern = '', replacement = ''] = parts
103
+
104
+ return pattern !== '' && has_plain_flags(parts) && !META_CHARACTER.test(pattern + replacement)
105
+ }
106
+
107
+ function is_extended(words: ReadonlyArray<string>): boolean {
108
+ return words.some((word) => word === EXTENDED_LONG || EXTENDED_SHORT.test(word))
109
+ }
110
+
111
+ // The one script of a basic-regex in-place `sed`; `undefined` for several, none, or `-E`.
112
+ function single_script(words: ReadonlyArray<string>): string | undefined {
113
+ if (is_extended(words)) return undefined
114
+
115
+ const { scripts } = sed_call(words)
116
+
117
+ return scripts.length === 1 ? scripts[0] : undefined
118
+ }
119
+
120
+ // The pattern of a single plain `s` command; `undefined` for anything else.
121
+ function plain_pattern(words: ReadonlyArray<string>): string | undefined {
122
+ const script = single_script(words)
123
+ const parts = script === undefined ? undefined : substitute_parts(script)
124
+
125
+ return parts !== undefined && is_plain(parts) ? parts[0] : undefined
126
+ }
127
+
128
+ function match_count(text: string, pattern: string): number {
129
+ return text.split(pattern).length - 1
130
+ }
131
+
132
+ type ReadFile = (file_path: string) => string
133
+
134
+ // The trigger: an in-place `sed` the conditions do not clear in any of its target files.
135
+ function is_unsafe_segment(segment: string, read: ReadFile): boolean {
136
+ const words = sed_words(segment)
137
+
138
+ if (words === undefined) return false
139
+
140
+ const { files } = sed_call(words)
141
+ const pattern = plain_pattern(words)
142
+
143
+ if (pattern === undefined || files.length === 0) return true
144
+
145
+ return files.some((file) => match_count(read(file), pattern) !== ONE_MATCH)
146
+ }
147
+
148
+ function is_unsafe_sed(command: string, read: ReadFile = file_reader.read_file_or_empty): boolean {
149
+ return shell_segments.segments_of(command).some((segment) => is_unsafe_segment(segment, read))
150
+ }
151
+
152
+ // The occasion the rule governs: any in-place `sed`.
153
+ function is_in_place_sed(command: string): boolean {
154
+ return shell_segments.segments_of(command).some((segment) => sed_words(segment) !== undefined)
155
+ }
156
+
157
+ // Keeping the rule: an in-place `sed` whose script clears the static conditions. The per-file count
158
+ // is not re-read here, so a transcript is measured without the file as it stood then.
159
+ function keeps_plain_sed(command: string): boolean {
160
+ return shell_segments.segments_of(command).some((segment) => {
161
+ const words = sed_words(segment)
162
+
163
+ return words !== undefined && plain_pattern(words) !== undefined
164
+ })
165
+ }
166
+
167
+ const SED_IN_PLACE_REASON =
168
+ '⛔ unsafe `sed -i`: its pattern or replacement carries a meta character (`/ & \\ . * [ ] ^ $`), ' +
169
+ 'it is not one plain `s` command (a flag other than `g`, or `-E`), or it does not match exactly once in each target file, so it can rewrite a line you did ' +
170
+ 'not mean to. Change the region with the Edit tool. This rule fires on every occurrence ' +
171
+ '(`prompts/collaboration-workflow/file-edits.md`).'
172
+
173
+ const ROW = {
174
+ id: 'sed-in-place',
175
+ is_trigger: bash_triggers.on_bash_command((command: string) => is_unsafe_sed(command)),
176
+ reason: SED_IN_PLACE_REASON,
177
+ decide: (): boolean => true,
178
+ keeps: bash_triggers.on_bash_command(keeps_plain_sed),
179
+ reaches: bash_triggers.on_bash_command(is_in_place_sed),
180
+ about: {
181
+ title: 'No `sed -i` its countable conditions do not clear',
182
+ topic: '`file-edits.md`',
183
+ fires:
184
+ 'a `sed -i` whose script is not one `s` command, whose pattern or replacement has a meta character, or which does not match exactly once in a target file. Every occurrence',
185
+ quiet: 'not in place, or one plain `s` command matching once in each file',
186
+ },
187
+ }
188
+
189
+ const sed_in_place = {
190
+ ROW,
191
+ is_in_place_sed,
192
+ is_unsafe_sed,
193
+ keeps_plain_sed,
194
+ }
195
+
196
+ export { sed_in_place }
@@ -20,8 +20,8 @@ const SEGMENT_PARTS = new RegExp(`(${SEGMENT_SEPARATOR.source})`, 'u')
20
20
  // question is which program a segment launches, and `true | git push -f` or `x & git push -f`
21
21
  // launches git all the same; a `--jq` fragment cut off by a `|` begins with no program word, so it
22
22
  // launches nothing they look for. A `&` that belongs to a redirection (`2>&1`, `&>file`, `<&3`) does
23
- // not cut.
24
- const COMMAND_SEPARATOR = /&&|\|\||;|\n|\|&?|(?<![<>])&(?!>)/u
23
+ // not cut, nor does the `|` of the clobber redirect `>|`.
24
+ const COMMAND_SEPARATOR = /&&|\|\||;|\n|(?<!>)\|&?|(?<![<>])&(?!>)/u
25
25
 
26
26
  /** The line's commands, trimmed. Empty segments are kept: a caller anchoring at `^` rejects them. */
27
27
  function segments_of(command: string): Array<string> {
@@ -54,6 +54,13 @@ function quote_aware_commands_of(command: string): Array<string> {
54
54
  return starts.map((start, index) => command.slice(start, stops[index] ?? command.length).trim())
55
55
  }
56
56
 
57
+ const LINE_CONTINUATION = /\\\n/gu
58
+
59
+ /** The line with each backslash-newline joined, so a continued call stays one command at a `\n` cut. */
60
+ function joined_continuations(command: string): string {
61
+ return command.replaceAll(LINE_CONTINUATION, ' ')
62
+ }
63
+
57
64
  /** The same cut, untrimmed and with each separator kept between its commands, so the parts join back to the line. */
58
65
  function parts_of(command: string): Array<string> {
59
66
  return command.split(SEGMENT_PARTS)
@@ -91,6 +98,7 @@ const shell_segments = {
91
98
  commands_of,
92
99
  first_of,
93
100
  is_josh_command,
101
+ joined_continuations,
94
102
  parts_of,
95
103
  quote_aware_commands_of,
96
104
  segments_of,
@@ -13,7 +13,7 @@ const ROWS: ReadonlyArray<StopRule> = [
13
13
  id: 'stop-notify',
14
14
  about: {
15
15
  title: 'Stop notification',
16
- topic: '`CLAUDE.md` → "Mid-workflow stop notification"',
16
+ topic: '`rule-delivery.md` → "stop-notify"',
17
17
  entry: STOP_GUARD,
18
18
  fires:
19
19
  'a stop with the work tree held and no `confirmation` notify that turn. Sends the notify itself; blocks only when the send fails',
@@ -32,18 +32,6 @@ const ROWS: ReadonlyArray<StopRule> = [
32
32
  'a dirty tree (a `halfrun` stop before the commit, a `needs-human-review` stop) or already released',
33
33
  },
34
34
  },
35
- {
36
- id: 'issue-citation',
37
- about: {
38
- title: 'Issue citation format',
39
- topic: '`prompts/collaboration-workflow/issue-citation.md`',
40
- entry: STOP_GUARD,
41
- fires:
42
- 'a bare `#N` in the prose of the last reply. Prints the citation lines itself; blocks only when a number cannot be cited',
43
- quiet:
44
- 'link form, inside code or a quote, `#N` right after `PR`, or a GitHub-facing artifact',
45
- },
46
- },
47
35
  {
48
36
  id: 'filing-offer',
49
37
  about: {
@@ -1,8 +1,8 @@
1
1
  import { hook_decision } from '#scripts/josh/hook-decision'
2
2
  import { json_value } from '#scripts/lib/json-value'
3
3
  import { z } from 'zod'
4
+ import { concise_reply } from './concise-reply'
4
5
  import { filing_offer } from './filing-offer'
5
- import { issue_citation } from './issue-citation'
6
6
  import { lane_background } from './lane-background'
7
7
  import { reply_language } from './reply-language'
8
8
  import { stop_decision, type StopDecision } from './stop-decision'
@@ -22,7 +22,7 @@ import { stop_decision, type StopDecision } from './stop-decision'
22
22
  // `pretool-guard` is one — not a second copy of the plumbing.
23
23
  //
24
24
  // **A rule whose answer is fixed acts; only a rule that needs judgement refuses** (joshuafolkken/kit#3919).
25
- // The stop notification, the hold release and the citation lines are each one mechanical operation the
25
+ // The stop notification and the hold release are each one mechanical operation the
26
26
  // hook can run itself, so they come back as `actions` and the stop goes through: a refusal is a whole
27
27
  // model turn spent re-reading the context to run a command the hook already knew. `stop-actions.ts`
28
28
  // runs them, and only an action that fails falls back to its refusal. The rest — the reply's language,
@@ -34,7 +34,7 @@ const SWITCH_ENV_KEY = 'JOSH_STOP_GUARD'
34
34
  const BLOCK_DECISION = 'block'
35
35
 
36
36
  // Only the fields the stop rules read. `last_assistant_message` is the turn's session-facing text —
37
- // Claude Code hands it directly so the transcript's async lag is never in the citation path.
37
+ // Claude Code hands it directly so the transcript's async lag is never in the rules' path.
38
38
  const stop_payload_schema = z.object({
39
39
  transcript_path: z.string().min(1),
40
40
  stop_hook_active: z.boolean().nullish(),
@@ -64,8 +64,8 @@ interface StopContext {
64
64
  notified: boolean
65
65
  // The turn's session-facing reply text.
66
66
  message: string
67
- // The person's last prompt — a `#N` it already carried is quoted, not cited.
68
- prompt: string
67
+ // The person's last prompt on the tail — `undefined` when none is on it (`last_prompt.person_prompt`).
68
+ prompt: string | undefined
69
69
  // This stop already forced a continuation earlier in the prompt — the loop-breaker Claude Code
70
70
  // documents, so a run that will not comply is not blocked forever.
71
71
  stop_hook_active: boolean
@@ -74,7 +74,7 @@ interface StopContext {
74
74
  // the cut holding the tree by design and hands off to a fresh process (`pre-gate-cut.md`), so that
75
75
  // turn-end is an automatic continuation, not the person-waiting pause the block rules are for.
76
76
  cut_pending: boolean
77
- // An Issue filing no guard refused is on this run's transcript tail — `filing_cap`'s count.
77
+ // An Issue filing no guard refused is on this run's transcript tail — `turn_filings`' count.
78
78
  filed: boolean
79
79
  // The owner of the repository the session runs in, or `undefined` when it cannot be read.
80
80
  session_owner: string | undefined
@@ -91,6 +91,9 @@ interface StopContext {
91
91
  // A backgrounded subagent this run launched has not finished — `lane_background.pending_agent_ids`.
92
92
  // Apart from `background_pending`, since only a subagent is sure to end.
93
93
  agent_pending: boolean
94
+ // A backgrounded command this run launched has not finished — `lane_background.pending_command_ids`.
95
+ // Apart from `agent_pending`, since the two are waited on differently.
96
+ command_pending: boolean
94
97
  // This lane child handed its region to a running detached ship — `lane_handoff.is_handed_off`.
95
98
  // The supervisor notifies on its own outcome, so nobody waits on this stop.
96
99
  handed_off: boolean
@@ -108,7 +111,6 @@ interface StopContext {
108
111
  type StopAction =
109
112
  | { kind: 'notify'; issue: string; message: string; decision: StopDecision }
110
113
  | { kind: 'release'; issue: string }
111
- | { kind: 'cite'; references: ReadonlyArray<string> }
112
114
 
113
115
  // `reason` refuses the stop; otherwise `actions` run, and `blocks_on_failure` says whether a failed one
114
116
  // falls back to its refusal — never past the loop-breaker.
@@ -128,8 +130,8 @@ const STOP_NOTIFY_REASON =
128
130
  '⛔ mid-workflow stop notification: this working tree is still held by a run and no `confirmation` ' +
129
131
  'Telegram was sent this turn. Send ' +
130
132
  '`pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\' ' +
131
- '--recommended "<choice>" --alternative "<choice>"` (`CLAUDE.md` → "Mid-workflow stop ' +
132
- 'notification"), then confirm in one line that it was sent — do not repeat your previous reply.'
133
+ '--recommended "<choice>" --alternative "<choice>"` (`rule-delivery.md` → "stop-notify"), ' +
134
+ 'then confirm in one line that it was sent — do not repeat your previous reply.'
133
135
 
134
136
  // **A clean tree that still holds is a tree the next run will trample.** `working-tree-hold.md`: a stop that
135
137
  // leaves the tree clean releases the hold; a `halfrun` pre-commit stop and a `needs-human-review` stop
@@ -141,28 +143,6 @@ const HOLD_RELEASE_REASON =
141
143
  '`working-tree-hold.md`), then confirm in one line that it was released — do not repeat your ' +
142
144
  'previous reply.'
143
145
 
144
- // **The fallback when the hook could not print the citation lines itself**. The bare `#N` is already
145
- // on screen and the hook cannot unsay it, so the reason does the one thing that helps the *next* reply:
146
- // it names the numbers it detected and hands over the exact `issue:cite` call that prints the
147
- // paste-ready lines, then asks for the corrected citation lines alone — not the whole reply reissued.
148
- // Reprinting the whole reply is what made the correction read as a duplicate;
149
- // the bare copy already scrolled past stays, and the fix follows it as a short correction. The rule
150
- // itself is not restated — it is resident in `CLAUDE.md` — only pointed at.
151
- const ISSUE_CITATION_REASON =
152
- 'Session-facing output cites an Issue as a number-link so the reader can click through and see ' +
153
- 'which repository it is (`CLAUDE.md`, `prompts/collaboration-workflow/issue-citation.md`).'
154
-
155
- function build_citation_reason(references: ReadonlyArray<string>): string {
156
- const numbers = references.join(', ')
157
- const command = `pnpm josh issue:cite ${issue_citation.cite_arguments(references).join(' ')}`
158
-
159
- return (
160
- `⛔ issue citation: your reply names an Issue as a bare \`#N\` (${numbers}). Run \`${command}\` ` +
161
- `to print the paste-ready citation lines, then print the corrected citation lines alone — do not ` +
162
- `repeat your previous reply. ${ISSUE_CITATION_REASON}`
163
- )
164
- }
165
-
166
146
  // **An offer to file in an unattended run is a Tier A filing deferred to nobody**.
167
147
  // The judgement is already made, so the reason hands over the filing chain rather than a question; the
168
148
  // rule itself is resident in `observation-filing.md` and only pointed at. **An interactive session asks
@@ -249,6 +229,13 @@ function language_reason(context: StopContext): string | undefined {
249
229
  return build_language_reason(context.session_lang)
250
230
  }
251
231
 
232
+ // **A reply's narrative over the limit is sent back once to be shortened** — `concise-reply.ts`.
233
+ function concise_reason(context: StopContext): string | undefined {
234
+ if (!concise_reply.is_over(context.message, context.prompt)) return undefined
235
+
236
+ return concise_reply.build_reason(context.message)
237
+ }
238
+
252
239
  // The sessions no person answers: one kit launched under `claude -p` (a lane child, a woken session), a
253
240
  // dispatched lane child, or the `backlogrun` parent the user handed the backlog to.
254
241
  function is_unattended(context: StopContext): boolean {
@@ -295,24 +282,16 @@ function needs_release(context: StopContext): boolean {
295
282
  return is_held_idle(context) && context.tree_clean && !context.prrun_stopped
296
283
  }
297
284
 
298
- // The bare references the citation lines are printed for. **A headless agent's reply has no reader**:
299
- // its text lands in a stream-json log, so correcting its citations or its language costs a turn and
300
- // reaches nobody. The artifacts a person does read — a Telegram, an Issue, a pull request — are held
301
- // to the same rules by their own guards, not here.
302
- function bare_references(context: StopContext): ReadonlyArray<string> {
303
- if (context.headless_agent) return []
304
-
305
- return issue_citation.unquoted_references(context.message, context.prompt)
306
- }
307
-
308
- // The two rules that read the reply and need the model, the filing offer first. A drifted reply
285
+ // The rules that read the reply and need the model: the filing offer, then the language, then the
286
+ // length. **A headless agent's reply has no reader**: its text lands in a stream-json log, so
287
+ // correcting its language costs a turn and reaches nobody. A drifted reply
309
288
  // refused for another reason is not rewritten, but the drift still ends: `block_envelope` appends the
310
289
  // session-language note to every refusal, so the turn it continues is written in the session language.
311
290
  function reply_reason(context: StopContext): string | undefined {
312
291
  if (needs_filing(context)) return FILING_OFFER_REASON
313
292
  if (context.headless_agent) return undefined
314
293
 
315
- return language_reason(context)
294
+ return language_reason(context) ?? concise_reason(context)
316
295
  }
317
296
 
318
297
  // Sent back once; past the loop-breaker the run has said nothing is awaited, and nothing is sent.
@@ -342,11 +321,19 @@ function needs_background_wait(context: StopContext): boolean {
342
321
  return context.lane_child && context.background_pending
343
322
  }
344
323
 
324
+ // **Only subagents left take their own wait**: their notice comes in when a tool call ends, while a
325
+ // command is waited on through the detached ship.
326
+ function background_wait_reason(context: StopContext): string {
327
+ if (context.command_pending) return lane_background.LANE_BACKGROUND_STOP_REASON
328
+
329
+ return lane_background.LANE_AGENT_STOP_REASON
330
+ }
331
+
345
332
  // The two lane rules that mean the run is not pausing at all — ahead of every action, since the notify
346
333
  // would announce a pause the run has no reason to take.
347
334
  function lane_reason(context: StopContext): string | undefined {
348
335
  if (needs_index_route(context)) return LANE_INDEX_REASON
349
- if (needs_background_wait(context)) return lane_background.LANE_BACKGROUND_STOP_REASON
336
+ if (needs_background_wait(context)) return background_wait_reason(context)
350
337
 
351
338
  return undefined
352
339
  }
@@ -382,14 +369,6 @@ function hold_actions(context: StopContext): Array<StopAction> {
382
369
  return needs_release(context) ? [...notify, { kind: 'release', issue }] : notify
383
370
  }
384
371
 
385
- // The actions in the order they run: the notify ahead of the release that ends the hold it reports.
386
- function stop_actions(context: StopContext): ReadonlyArray<StopAction> {
387
- const references = bare_references(context)
388
- const cite: Array<StopAction> = references.length > 0 ? [{ kind: 'cite', references }] : []
389
-
390
- return [...hold_actions(context), ...cite]
391
- }
392
-
393
372
  // First-wins across the refusals. `stop_hook_active` is the loop-breaker, so a run that already got one
394
373
  // continuation this prompt may stop; the lane rules go first, then the filing offer, then the language.
395
374
  //
@@ -418,7 +397,7 @@ function stop_outcome(context: StopContext): StopOutcome {
418
397
  const reason = block_reason(context)
419
398
  if (!runs_actions(context, reason)) return { ...NO_OUTCOME, reason }
420
399
 
421
- const actions = stop_actions(context)
400
+ const actions = hold_actions(context)
422
401
  if (actions.length === 0) return NO_OUTCOME
423
402
 
424
403
  return { reason, actions, blocks_on_failure: !context.stop_hook_active }
@@ -426,10 +405,7 @@ function stop_outcome(context: StopContext): StopOutcome {
426
405
 
427
406
  // The refusal an action falls back to when the hook could not run it.
428
407
  function fallback_reason(action: StopAction): string {
429
- if (action.kind === 'notify') return STOP_NOTIFY_REASON
430
- if (action.kind === 'release') return HOLD_RELEASE_REASON
431
-
432
- return build_citation_reason(action.references)
408
+ return action.kind === 'notify' ? STOP_NOTIFY_REASON : HOLD_RELEASE_REASON
433
409
  }
434
410
 
435
411
  // The documented shape a `Stop` hook blocks with: `reason` is fed back to Claude, which then continues
@@ -460,7 +436,6 @@ const stop_rules = {
460
436
  HEADLESS_REFUSAL_CAP,
461
437
  HEADLESS_WAIT_REASON,
462
438
  HOLD_RELEASE_REASON,
463
- ISSUE_CITATION_REASON,
464
439
  LANE_INDEX_REASON,
465
440
  NO_OUTCOME,
466
441
  STOP_NOTIFY_REASON,
@@ -44,7 +44,7 @@ const ROW = {
44
44
  reason: REASON,
45
45
  about: {
46
46
  title: 'Test declaration',
47
- topic: '`CLAUDE.md` → Code Change Rules',
47
+ topic: '`testing-guide.md` → "0. Per-Requirement Test Coverage (MANDATORY)"',
48
48
  fires: '`pnpm josh git -y` (`josh g`) while `pnpm josh test:declared` answers `required`',
49
49
  quiet: '`exempt` (non-runtime only) or `satisfied` (tests included)',
50
50
  },