@joshuafolkken/kit 1.1178.0 → 1.1240.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 (442) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/settings.json +2 -2
  3. package/.claude/skills/dependency-update/SKILL.md +4 -8
  4. package/.claude/skills/epic-commands/SKILL.md +2 -2
  5. package/.claude/skills/epic-commands/epic-bundle.md +1 -1
  6. package/.claude/skills/workflow-commands/SKILL.md +2 -4
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +7 -3
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
  13. package/.claude/skills/workflow-commands/chain-rule.md +1 -1
  14. package/.claude/skills/workflow-commands/delegation.md +2 -2
  15. package/.claude/skills/workflow-commands/followup.md +10 -0
  16. package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
  17. package/.claude/skills/workflow-commands/halfrun.md +1 -1
  18. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  19. package/.claude/skills/workflow-commands/kickoff.md +2 -1
  20. package/.claude/skills/workflow-commands/latest-gate.md +1 -1
  21. package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
  22. package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
  23. package/.claude/skills/workflow-commands/prerequisite.md +2 -2
  24. package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
  25. package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
  26. package/.codex/hooks.json +2 -2
  27. package/.cursorrules +6 -41
  28. package/.github/actions/setup-node/action.yml +1 -1
  29. package/.github/workflows/auto-tag.yml +3 -4
  30. package/.github/workflows/ci.yml +32 -31
  31. package/.github/workflows/dependabot-auto-merge.yml +13 -6
  32. package/.github/workflows/publish.yml +50 -4
  33. package/.github/workflows/sonar-qube.yml +3 -0
  34. package/.pnpmfile.mjs +4 -3
  35. package/.prettierignore +8 -2
  36. package/CLAUDE.md +18 -20
  37. package/README.md +44 -38
  38. package/SECURITY.md +15 -1
  39. package/dist/CLAUDE.md +18 -20
  40. package/dist/commands/document-section-cli.js +734 -0
  41. package/dist/hooks/audit-provision.js +346 -0
  42. package/dist/hooks/batch-guard.js +5 -4
  43. package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
  44. package/dist/hooks/chunk-5VNDSNG3.js +12 -0
  45. package/dist/hooks/chunk-CDY4NFQG.js +15 -0
  46. package/dist/hooks/chunk-ELCN5LCH.js +135 -0
  47. package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
  48. package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
  49. package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
  50. package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
  51. package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
  52. package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
  53. package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
  54. package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
  55. package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
  56. package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
  57. package/dist/hooks/codex-hook-adapter.js +14 -12
  58. package/dist/hooks/duplicate-read-guard.js +6 -5
  59. package/dist/hooks/format-edited.js +4 -3
  60. package/dist/hooks/inputs.json +203 -179
  61. package/dist/hooks/investigation-guard.js +6 -5
  62. package/dist/hooks/pretool-guard.js +12 -10
  63. package/dist/hooks/session-lang.js +2 -4
  64. package/dist/hooks/stop-guard.js +66 -72
  65. package/dist/josh.js +1088 -78
  66. package/dist/version/index.d.ts +7 -2
  67. package/dist/version/index.js +152 -75
  68. package/eslint/base.js +23 -1
  69. package/eslint/rules/code-quality.js +3 -0
  70. package/eslint/rules/early-return-one-liner.js +98 -0
  71. package/eslint/rules/unicorn.js +16 -1
  72. package/package.json +7 -6
  73. package/pnpm-workspace.yaml +9 -4
  74. package/prettier/basic.d.ts +3 -1
  75. package/prettier/index.d.ts +3 -1
  76. package/prettier/static.d.ts +3 -1
  77. package/prompts/coding-standards.md +1 -1
  78. package/prompts/collaboration-workflow/file-edits.md +4 -4
  79. package/prompts/collaboration-workflow/glossary.md +1 -1
  80. package/prompts/collaboration-workflow/issue-template.md +1 -0
  81. package/prompts/collaboration-workflow/operating-rules.md +17 -9
  82. package/prompts/collaboration-workflow/overview.md +2 -2
  83. package/prompts/collaboration-workflow/principles.md +14 -12
  84. package/prompts/collaboration-workflow/report-format.md +1 -1
  85. package/prompts/collaboration-workflow/residency.md +1 -1
  86. package/prompts/collaboration-workflow/rule-delivery.md +6 -6
  87. package/prompts/collaboration-workflow/shell-body.md +6 -6
  88. package/prompts/collaboration-workflow/turn-batching.md +1 -1
  89. package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
  90. package/prompts/collaboration-workflow/wip-cap.md +10 -10
  91. package/prompts/review-rubric.md +1 -2
  92. package/prompts/review.md +1 -1
  93. package/scripts/agent/agent-headless.ts +29 -0
  94. package/scripts/agent/agent-launch-environment.ts +6 -2
  95. package/scripts/agent/codex-agent-argv.ts +2 -1
  96. package/scripts/auto-ok/auto-ok-cli.ts +1 -1
  97. package/scripts/backlog/backlog-arrival.ts +3 -3
  98. package/scripts/backlog/backlog-budget.ts +1 -1
  99. package/scripts/backlog/backlog-drive-args.ts +135 -0
  100. package/scripts/backlog/backlog-drive-cli.ts +11 -137
  101. package/scripts/backlog/backlog-drive-launch.ts +39 -0
  102. package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
  103. package/scripts/backlog/backlog-drive-named.ts +3 -3
  104. package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
  105. package/scripts/backlog/backlog-drive-owner.ts +1 -1
  106. package/scripts/backlog/backlog-drive-restore.ts +1 -1
  107. package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
  108. package/scripts/backlog/backlog-drive.ts +1 -1
  109. package/scripts/backlog/backlog-next.ts +16 -5
  110. package/scripts/backlog/backlog-offer-cli.ts +6 -6
  111. package/scripts/backlog/backlog-offer.ts +1 -1
  112. package/scripts/backlog/backlog-plan-cli.ts +1 -1
  113. package/scripts/backlog/backlog-pool.ts +57 -11
  114. package/scripts/backlog/backlog-ready.ts +1 -1
  115. package/scripts/backlog/backlog-scope.ts +1 -1
  116. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  117. package/scripts/backlog/backlog-stalled.ts +1 -1
  118. package/scripts/build/build-codex-hooks.ts +3 -1
  119. package/scripts/build/build-commands.ts +66 -0
  120. package/scripts/build/build-hooks.ts +2 -1
  121. package/scripts/build/build-library.ts +12 -3
  122. package/scripts/build/build.ts +70 -0
  123. package/scripts/build/local-bin.ts +3 -13
  124. package/scripts/delegation/investigation-reads.ts +3 -1
  125. package/scripts/discovery/repo-origin.ts +5 -5
  126. package/scripts/doctor/doctor-consumer.ts +2 -2
  127. package/scripts/doctor/doctor-io.ts +2 -3
  128. package/scripts/document/capped-print-part.ts +30 -0
  129. package/scripts/document/capped-print.ts +3 -4
  130. package/scripts/document/document-byte-budget.ts +11 -2
  131. package/scripts/document/document-scan.ts +117 -0
  132. package/scripts/document/document-section.ts +143 -16
  133. package/scripts/document/entry-read-budget.ts +5 -3
  134. package/scripts/document/entry-read-set.ts +10 -89
  135. package/scripts/document/read-set-cost.ts +98 -0
  136. package/scripts/epic/epic-add-body.ts +1 -0
  137. package/scripts/epic/epic-audit-checks.ts +1 -1
  138. package/scripts/epic/epic-audit-rationale.ts +4 -3
  139. package/scripts/epic/epic-bundle-cli.ts +14 -3
  140. package/scripts/epic/epic-bundle-poll.ts +41 -0
  141. package/scripts/epic/epic-busy.ts +1 -1
  142. package/scripts/epic/epic-candidate-confirm.ts +3 -2
  143. package/scripts/epic/epic-classify.ts +1 -1
  144. package/scripts/epic/epic-cli-argv.ts +78 -46
  145. package/scripts/epic/epic-cli-remove.ts +11 -19
  146. package/scripts/epic/epic-cli.ts +74 -73
  147. package/scripts/epic/epic-close-comment.ts +1 -1
  148. package/scripts/epic/epic-close.ts +1 -1
  149. package/scripts/epic/epic-cross-repo.ts +5 -5
  150. package/scripts/epic/epic-graph.ts +1 -1
  151. package/scripts/epic/epic-index.ts +1 -1
  152. package/scripts/epic/epic-issue.ts +2 -6
  153. package/scripts/epic/epic-lane-offer.ts +2 -2
  154. package/scripts/epic/epic-next.ts +29 -42
  155. package/scripts/epic/epic-solo-stale.ts +1 -1
  156. package/scripts/epic/epic-triage.ts +8 -4
  157. package/scripts/epic/epic-validate.ts +1 -1
  158. package/scripts/followup/git-followup-finish.ts +2 -2
  159. package/scripts/followup/git-followup-label.ts +2 -2
  160. package/scripts/followup/git-followup-merged.ts +2 -2
  161. package/scripts/followup/git-pr-followup-checks.ts +142 -0
  162. package/scripts/followup/git-pr-followup.ts +4 -129
  163. package/scripts/gate/core-admission.ts +162 -0
  164. package/scripts/gate/core-budget.ts +190 -68
  165. package/scripts/gate/gate-plan.ts +80 -7
  166. package/scripts/gate/gate-report.ts +28 -9
  167. package/scripts/gate/gate-skip.ts +38 -7
  168. package/scripts/gate/hook-gate-reuse.ts +12 -10
  169. package/scripts/gate/machine-capacity.ts +175 -0
  170. package/scripts/gate/pre-commit-type-check.ts +4 -1
  171. package/scripts/gate/project-checks.ts +6 -9
  172. package/scripts/gate/type-check-step.ts +2 -3
  173. package/scripts/gate/verification-gate.ts +34 -10
  174. package/scripts/gh/git-gh-api-path.ts +2 -2
  175. package/scripts/gh/git-gh-issue-rest.ts +3 -3
  176. package/scripts/gh/git-gh-issue-write.ts +53 -7
  177. package/scripts/gh/git-gh-pr-read.ts +64 -19
  178. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  179. package/scripts/gh/git-gh-pr.ts +5 -1
  180. package/scripts/gh/git-pr-ai-review.ts +1 -1
  181. package/scripts/gh/git-pr-checks-eval.ts +1 -3
  182. package/scripts/gh/git-pr-checks-parse.ts +1 -1
  183. package/scripts/gh/git-pr-checks-watch.ts +6 -5
  184. package/scripts/gh/git-pr-checks.ts +7 -5
  185. package/scripts/gh/git-pr-coderabbit.ts +1 -1
  186. package/scripts/gh/git-pr-error.ts +1 -3
  187. package/scripts/gh/git-pr.ts +9 -13
  188. package/scripts/gh/github-issue-url.ts +2 -2
  189. package/scripts/git/constants.ts +2 -6
  190. package/scripts/git/git-command.ts +20 -218
  191. package/scripts/git/git-diff-reads.ts +225 -0
  192. package/scripts/git/git-error.ts +4 -12
  193. package/scripts/git/git-pre-push-hook.ts +139 -0
  194. package/scripts/git/git-prompt.ts +2 -6
  195. package/scripts/git/git-spawn-sync.ts +3 -1
  196. package/scripts/git/git-spawn.ts +1 -1
  197. package/scripts/git/git-staging.ts +4 -6
  198. package/scripts/git/git-status.ts +3 -9
  199. package/scripts/hooks/codex-hook-adapter.ts +1 -1
  200. package/scripts/hooks/edited-cspell.ts +2 -4
  201. package/scripts/hooks/format-edited-file.ts +3 -12
  202. package/scripts/hooks/pre-push-unit.ts +31 -9
  203. package/scripts/hooks/reserved-run.ts +2 -2
  204. package/scripts/hooks/run-hook.sh +5 -4
  205. package/scripts/hooks/stop-guard.ts +5 -3
  206. package/scripts/init/init-actions.ts +0 -5
  207. package/scripts/init/init-ai-copy.ts +13 -2
  208. package/scripts/init/init-logic-json-merge.ts +19 -0
  209. package/scripts/init/init-logic-secretlint.ts +7 -7
  210. package/scripts/init/init-logic-workspace.ts +41 -2
  211. package/scripts/init/init-logic.ts +16 -26
  212. package/scripts/init/init.ts +10 -16
  213. package/scripts/init/initial-commit.ts +4 -4
  214. package/scripts/init/kit-development-versions.ts +40 -0
  215. package/scripts/init/kit-setup-state.ts +2 -2
  216. package/scripts/init/package-manager-pin.ts +35 -0
  217. package/scripts/init/start-exec.ts +68 -19
  218. package/scripts/init/start-setup-pr.ts +9 -9
  219. package/scripts/init/start-steps.ts +6 -10
  220. package/scripts/issue/defect-rate-cli.ts +1 -1
  221. package/scripts/issue/git-next-issues.ts +1 -1
  222. package/scripts/issue/issue-auto-ok.ts +32 -7
  223. package/scripts/issue/issue-backlinks-cli.ts +2 -2
  224. package/scripts/issue/issue-backlinks.ts +9 -9
  225. package/scripts/issue/issue-comment-cli.ts +19 -13
  226. package/scripts/issue/issue-file-cli.ts +72 -22
  227. package/scripts/issue/issue-file.ts +29 -1
  228. package/scripts/issue/issue-labels.ts +13 -0
  229. package/scripts/issue/issue-release-cli.ts +155 -0
  230. package/scripts/issue/issue-release.ts +58 -0
  231. package/scripts/issue/issue-scout-closed.ts +1 -1
  232. package/scripts/issue/issue-state-cli.ts +30 -74
  233. package/scripts/josh/josh-command-map.ts +2 -0
  234. package/scripts/josh/josh-command-types.ts +14 -0
  235. package/scripts/josh/josh-commands-ai.ts +4 -4
  236. package/scripts/josh/josh-commands-backlog.ts +4 -1
  237. package/scripts/josh/josh-commands-development.ts +18 -0
  238. package/scripts/josh/josh-commands-document.ts +1 -0
  239. package/scripts/josh/josh-commands-issue.ts +8 -1
  240. package/scripts/josh/josh-commands-lane.ts +24 -1
  241. package/scripts/josh/josh-commands-maintenance.ts +6 -6
  242. package/scripts/josh/josh-commands-run.ts +12 -7
  243. package/scripts/josh/josh-in-process.ts +28 -9
  244. package/scripts/josh/josh-logic.ts +34 -15
  245. package/scripts/josh/process-identity.ts +1 -3
  246. package/scripts/josh/session-language-cli.ts +3 -4
  247. package/scripts/lane/lane-await.ts +5 -1
  248. package/scripts/lane/lane-child-invocation.ts +2 -2
  249. package/scripts/lane/lane-cli.ts +12 -4
  250. package/scripts/lane/lane-dispatch.ts +14 -10
  251. package/scripts/lane/lane-environment.ts +5 -1
  252. package/scripts/lane/lane-handoff.ts +1 -1
  253. package/scripts/lane/lane-install.ts +2 -13
  254. package/scripts/lane/lane-launch-cli.ts +15 -1
  255. package/scripts/lane/lane-ledger.ts +145 -0
  256. package/scripts/lane/lane-leftover.ts +44 -30
  257. package/scripts/lane/lane-load.ts +108 -0
  258. package/scripts/lane/lane-measure-cli.ts +149 -0
  259. package/scripts/lane/lane-occupancy.ts +3 -0
  260. package/scripts/lane/lane-open.ts +5 -5
  261. package/scripts/lane/lane-paths.ts +13 -0
  262. package/scripts/lane/lane-reap.ts +1 -3
  263. package/scripts/lane/lane-registry.ts +11 -0
  264. package/scripts/lane/lane-relaunch.ts +1 -1
  265. package/scripts/lane/lane-stats.ts +224 -0
  266. package/scripts/lane/lane-stray.ts +140 -0
  267. package/scripts/lane/lane-vacant.ts +36 -0
  268. package/scripts/lane/openai-lane-ship.ts +2 -2
  269. package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
  270. package/scripts/lane/openai-lane-supervisor.ts +7 -165
  271. package/scripts/lane/openai-review-broker.ts +2 -4
  272. package/scripts/lib/ancestor-directories.ts +35 -0
  273. package/scripts/lib/animation-helpers.ts +2 -2
  274. package/scripts/lib/buffered-process.ts +3 -7
  275. package/scripts/lib/cli-flags.ts +71 -1
  276. package/scripts/lib/spawn-exit.ts +2 -2
  277. package/scripts/lib/timeouts.ts +35 -2
  278. package/scripts/lines/line-budget.ts +4 -6
  279. package/scripts/notify/telegram-test.ts +6 -0
  280. package/scripts/observations/observation-ledger-line.ts +6 -6
  281. package/scripts/observations/observation-ledger-migrate.ts +55 -4
  282. package/scripts/observations/observation-ledger.ts +26 -7
  283. package/scripts/overrides/lockfile-overrides.ts +1 -1
  284. package/scripts/overrides/overrides-logic.ts +1 -1
  285. package/scripts/propagate/propagate-git.ts +1 -1
  286. package/scripts/propagate/propagate-publish.ts +2 -2
  287. package/scripts/propagate/propagate-steps.ts +20 -15
  288. package/scripts/propagate/propagate.ts +36 -27
  289. package/scripts/refactor/import-graph.ts +18 -1
  290. package/scripts/refactor/refactor-lint.ts +4 -4
  291. package/scripts/release/github-release-environment.ts +1 -0
  292. package/scripts/release/github-release-runs.ts +115 -0
  293. package/scripts/release/github-release.ts +13 -67
  294. package/scripts/repo/repository-labels.ts +2 -2
  295. package/scripts/repo/required-checks-logic.ts +1 -3
  296. package/scripts/review/live-evidence.ts +2 -4
  297. package/scripts/review/review-brief-cli.ts +1 -1
  298. package/scripts/review/review-finding-ledger.ts +1 -1
  299. package/scripts/review/review-findings-cli.ts +1 -3
  300. package/scripts/rules/decision-oracle-batch.ts +128 -0
  301. package/scripts/rules/decision-oracle-tokens.ts +21 -0
  302. package/scripts/rules/decision-oracle.ts +8 -120
  303. package/scripts/rules/delivered-rules.ts +26 -182
  304. package/scripts/rules/direct-filing.ts +1 -1
  305. package/scripts/rules/early-heartbeat.ts +3 -3
  306. package/scripts/rules/git-argv.ts +1 -1
  307. package/scripts/rules/git-force.ts +1 -1
  308. package/scripts/rules/implementation-cut.ts +2 -2
  309. package/scripts/rules/index-guard.ts +1 -1
  310. package/scripts/rules/issue-comments.ts +118 -0
  311. package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
  312. package/scripts/rules/lane-background.ts +2 -2
  313. package/scripts/rules/lane-park.ts +18 -0
  314. package/scripts/rules/lane-split-park.ts +128 -0
  315. package/scripts/rules/oracle-firing.ts +1 -1
  316. package/scripts/rules/poll-loop.ts +1 -1
  317. package/scripts/rules/pre-gate-cut.ts +1 -1
  318. package/scripts/rules/protected-files.ts +59 -4
  319. package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
  320. package/scripts/rules/shell-body-trigger.ts +1 -1
  321. package/scripts/rules/stop-rules.ts +18 -7
  322. package/scripts/rules/test-declared-commit.ts +4 -4
  323. package/scripts/rules/wip-cap.ts +63 -0
  324. package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
  325. package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
  326. package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
  327. package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
  328. package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
  329. package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
  330. package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
  331. package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
  332. package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
  333. package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
  334. package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
  335. package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
  336. package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
  337. package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
  338. package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
  339. package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
  340. package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
  341. package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
  342. package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
  343. package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
  344. package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
  345. package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
  346. package/scripts/run/run-ending.ts +2 -2
  347. package/scripts/run/run-halfrun-resume.ts +6 -1
  348. package/scripts/run/run-headless.ts +1 -1
  349. package/scripts/run/run-invocation.ts +85 -84
  350. package/scripts/run/run-liveness.ts +1 -1
  351. package/scripts/run/run-parent-cut-hook.ts +1 -1
  352. package/scripts/run/run-preflight.ts +6 -6
  353. package/scripts/run/run-prep-cli.ts +1 -1
  354. package/scripts/run/run-prrun-resume.ts +1 -1
  355. package/scripts/run/run-report-cli.ts +4 -4
  356. package/scripts/run/run-report.ts +2 -2
  357. package/scripts/run/run-status-cli.ts +1 -1
  358. package/scripts/run/run-step-cli.ts +3 -3
  359. package/scripts/run/run-step.ts +7 -4
  360. package/scripts/run/run-stop-notify.ts +1 -1
  361. package/scripts/run/run-stranded-detect.ts +4 -4
  362. package/scripts/run/run-stranded.ts +2 -2
  363. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  364. package/scripts/run/run-watcher-guard.ts +5 -3
  365. package/scripts/run/run-watcher-hook.ts +1 -1
  366. package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
  367. package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
  368. package/scripts/run/ship/run-ship-lock.ts +50 -0
  369. package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
  370. package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
  371. package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
  372. package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
  373. package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
  374. package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
  375. package/scripts/run/ship/run-ship-stop-text.ts +13 -0
  376. package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
  377. package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
  378. package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
  379. package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
  380. package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
  381. package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
  382. package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
  383. package/scripts/run/wake/run-wake-restart.ts +58 -0
  384. package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
  385. package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
  386. package/scripts/safe-chain/preinstall-command.ts +20 -21
  387. package/scripts/safe-chain/project-config.ts +19 -15
  388. package/scripts/sync/sync-ai-files.ts +1 -1
  389. package/scripts/sync/sync-configs.ts +2 -1
  390. package/scripts/sync/sync.ts +15 -2
  391. package/scripts/test/unit-worker-share.ts +1 -0
  392. package/scripts/time-runtime/time-background.ts +5 -1
  393. package/scripts/time-runtime/time-instant.ts +3 -1
  394. package/scripts/time-runtime/time-spans.ts +6 -139
  395. package/scripts/time-runtime/time-tool-call.ts +160 -0
  396. package/scripts/version/effective-upstream.ts +2 -20
  397. package/scripts/version/latest-corepack.ts +124 -93
  398. package/scripts/version/latest-regression.ts +1 -1
  399. package/scripts/version/latest-update.ts +4 -4
  400. package/scripts/version/npm-registry.ts +20 -5
  401. package/scripts/version/publishable-range-check.ts +2 -2
  402. package/scripts/version/publishable-range.ts +1 -1
  403. package/scripts/version/release-age.ts +48 -38
  404. package/scripts/version/release-hold.ts +1 -1
  405. package/scripts/version/safe-chain-version-update.ts +40 -0
  406. package/scripts/version/upgrade-shell-command.ts +48 -8
  407. package/scripts/version/version-check-logic.ts +23 -14
  408. package/scripts/version/version-commands.ts +23 -11
  409. package/scripts/version/version-remote.ts +28 -13
  410. package/templates/pnpm-workspace.basic.yaml +4 -0
  411. package/templates/workflows/dependabot-auto-merge.yml +5 -5
  412. package/templates/workflows/github-release.yml +6 -0
  413. package/scripts/build/build-config-merge.ts +0 -27
  414. package/scripts/build/build-managed-marker.ts +0 -27
  415. package/scripts/build/build-self-sync-guard.ts +0 -27
  416. package/scripts/build/build-version.ts +0 -26
  417. package/scripts/version/preinstall-version-update.ts +0 -86
  418. /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
  419. /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
  420. /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
  421. /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
  422. /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
  423. /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
  424. /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
  425. /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
  426. /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
  427. /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
  428. /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
  429. /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
  430. /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
  431. /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
  432. /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
  433. /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
  434. /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
  435. /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
  436. /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
  437. /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
  438. /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
  439. /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
  440. /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
  441. /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
  442. /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
@@ -7,29 +7,29 @@ import { direct_filing } from './direct-filing'
7
7
  import { early_heartbeat } from './early-heartbeat'
8
8
  import { file_body } from './file-body'
9
9
  import { filing_cap } from './filing-cap'
10
- import { gh_api } from './gh-api'
11
10
  import { git_force } from './git-force'
12
11
  import { implementation_cut } from './implementation-cut'
13
- import { issue_fold } from './issue-fold'
12
+ import { issue_comments } from './issue-comments'
13
+ import { issue_fold_rule } from './issue-fold-rule'
14
14
  import { josh_git_bare } from './josh-git-bare'
15
15
  import { lane_background } from './lane-background'
16
16
  import { lane_carry_conflict } from './lane-carry-conflict'
17
17
  import { lane_interactive_ask } from './lane-interactive-ask'
18
18
  import { lane_park } from './lane-park'
19
+ import { lane_split_park } from './lane-split-park'
19
20
  import { lane_switch_main } from './lane-switch-main'
20
21
  import { oracle_consulted } from './oracle-consulted'
21
22
  import { permission_guards } from './permission-guards'
22
23
  import { piped_verification } from './piped-verification'
23
24
  import { poll_loop } from './poll-loop'
24
25
  import { pre_gate_cut } from './pre-gate-cut'
25
- import { prior_comment_read } from './prior-comment-read'
26
26
  import { raw_field_body } from './raw-field-body'
27
27
  import { rule_body_guard } from './rule-body-guard'
28
- import { run_tail } from './run-tail'
28
+ import { run_tail_rule } from './run-tail-rule'
29
29
  import { shell_body_trigger } from './shell-body-trigger'
30
- import { shell_segments } from './shell-segments'
31
30
  import { test_declared_commit } from './test-declared-commit'
32
31
  import { third_party_write } from './third-party-write'
32
+ import { wip_cap } from './wip-cap'
33
33
  import { worktree_guard } from './worktree-guard'
34
34
 
35
35
  // The enumeration of rules delivered at the moment they bind, rather than carried resident in
@@ -140,149 +140,12 @@ const STAMP_PREFIX = 'josh-rule-guard-'
140
140
  // function of it. `on_bash_command` went with it because those modules build their own rows.
141
141
  const { is_issue_filing, on_bash_command } = bash_triggers
142
142
 
143
- // The whole of the WIP cap, in the shape a refusal can carry: the count, the refusal, the two
144
- // exemptions and the three tests that decide the second one. The three tests are spelled out rather
145
- // than named, because a delivery that said only "an interrupt is exempt" would hand the deciding back
146
- // to judgement at exactly the moment nothing else is open to read (joshuafolkken/kit#1518).
147
- // `WIP_CAP` is the number's single source; `wip-cap.md` states it once and a test pins the two equal.
148
- const WIP_CAP = 30
149
- const WIP_CAP_REASON =
150
- `⛔ backlog WIP cap: \`pnpm josh issue:file\` counts the target repository's open Issues before filing. With more than ${String(WIP_CAP)} ` +
151
- 'open, close one first; nothing honestly closable means do not file — the command holds such a filing. ' +
152
- 'Two filings are exempt and proceed while stating the overage — one the run is blocked by (`--over-cap`, ' +
153
- 'or the `tier-a` / `split` route), and an interrupt (`--route interrupt`), decided by three tests rather ' +
154
- 'than judgement: a verification answers wrongly, a documented workflow cannot complete, or data is lost ' +
155
- 'or written outside the repository. Meeting none of the three, the finding is discretionary and waits. ' +
156
- 'Both procedures are in `prompts/collaboration-workflow/wip-cap.md`. Reissue this call and let the ' +
157
- 'command count — it fires once per run and cannot repeat on the call in hand.'
158
-
159
- // **A shell line carries several commands, and the subcommand has to be the one being invoked.**
160
- // Each segment is judged on its own, anchored at its start, so `gh issue comment <N> -b "… gh issue
161
- // view <N> …"` is read as the write it is rather than as the read it quotes. The cut itself is
162
- // `shell-segments.ts`, shared with the triggers that need the same one.
163
- // Global flags may precede the subcommand (`gh --repo o/r issue view 1`), so they are skipped —
164
- // the same optional-flag prefix `gh-api.ts` skips in front of `api`, shared from there.
165
- const ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, 'u')
166
- // `…/issues/<N>` — **one** Issue's body. The number has to end the path, so the listing
167
- // (`…/issues`) and every sub-resource under it (`…/issues/1319/comments`) are left alone.
168
- const ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u
169
- // **A write to that path is not a read of it**, which is why the body read below asks `gh_api.is_read`:
170
- // `gh api` sends POST as soon as any field flag appears, and `kickoff` PATCHes `…/issues/<N>` to
171
- // normalize a title and to fill a blank body — so without the read check the delivery would be spent
172
- // refusing a write, and the genuine body read later in the same run would never be guarded.
173
- // A segment that fetches comments: the flag, a `comments` field in a `--json` projection, or the
174
- // comments endpoint. The short `-c` is deliberately absent — it belongs to `wc`, `grep` and `sort`
175
- // far more often than to `gh`, and reading it as "comments included" silenced the rule on any line
176
- // that ended in a pipe. A run that types it pays one round trip instead.
177
- const FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u
178
- const ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u
179
-
180
- // A field projection — `--jq` for `gh api`, `--json` for `gh issue view` — whose value never names the
181
- // body. `gh api …/issues/<N> --jq '{state, labels}'` fetches the Issue only to read its state or its
182
- // labels, which is a state check and not the body read this rule guards (joshuafolkken/kit#1905); a
183
- // `--jq '.body'` still names the body and stays a body read. The value is taken quoted or bare, so the
184
- // comma list `--json state,labels` and the expression `'{state, labels}'` are read the same way.
185
- const FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u
186
- const NAMES_THE_BODY = /\bbody\b/u
187
-
188
- function projects_away_body(segment: string): boolean {
189
- const projection = FIELD_PROJECTION.exec(segment)
190
-
191
- return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? '')
192
- }
193
-
194
- function is_body_read_segment(segment: string): boolean {
195
- if (projects_away_body(segment)) return false
196
-
197
- if (ISSUE_VIEW_COMMAND.test(segment)) return true
198
-
199
- return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment)
200
- }
201
-
202
- // **An Issue's comments, not just any comments.** Batching pushes a run to fetch the body and the
203
- // comments on one line, so the allowance has to reach across segments — but `gh pr view 42 --json
204
- // comments` says nothing about whether *this* Issue was read whole, so the segment doing the
205
- // fetching has to be an Issue read itself.
206
- function fetches_issue_comments(segment: string): boolean {
207
- if (!FETCHES_COMMENTS.test(segment)) return false
208
-
209
- return (
210
- ISSUE_VIEW_COMMAND.test(segment) || (gh_api.is_gh_api(segment) && ISSUES_PATH.test(segment))
211
- )
212
- }
213
-
214
- // **The trigger is the body read, not the start of implementation.** The moment an Issue's body
215
- // reaches a run is the moment the rule binds, and it is one shell call — the test
216
- // `prompts/collaboration-workflow/rule-delivery.md` sets for leaving residency.
217
- function is_body_only_issue_read(command: string): boolean {
218
- const segments = shell_segments.segments_of(command)
219
-
220
- if (segments.some((segment) => fetches_issue_comments(segment))) return false
221
-
222
- return segments.some((segment) => is_body_read_segment(segment))
223
- }
224
-
225
- // **The refusal hands over the command that fixes it**, because reading is not the same as
226
- // obeying: a sentence saying "also read the comments" is prose of exactly the kind
227
- // joshuafolkken/kit#1344 measured as moving nothing, while a refused body read leaves the run
228
- // holding the reissue that makes the comments *present*. The conflict rule ships with it — a
229
- // delivery that said only "read them" would hand back the deciding at the moment nothing else is
230
- // open to read, the mistake joshuafolkken/kit#1518 corrected for the WIP cap.
231
- const ISSUE_COMMENTS_REASON =
232
- "⛔ an Issue's comments are part of the Issue: read them before implementing, not only the body. " +
233
- 'A decision recorded after the body was written lives only in a comment — a corrected diagnosis ' +
234
- '(joshuafolkken/kit#1537), a changed default and an added acceptance criterion ' +
235
- '(joshuafolkken/kit#1520), a scope handed to another Issue (joshuafolkken/kit#1304) — and ' +
236
- 'nothing in the body says it was superseded, so a body-only reader builds the wrong thing and ' +
237
- 'sees no contradiction. Reissue this read with the comments included: ' +
238
- "`gh api repos/{owner}/{repo}/issues/<N>/comments --jq '.[] | {user: .user.login, created_at, " +
239
- "body}'` beside the body read, or `gh issue view <N> --comments` where GraphQL is reachable. " +
240
- 'Then the later text is the agreement in force — a comment supersedes the body it contradicts — ' +
241
- "except for two answers that are not the run's to make: work a comment reassigns to another " +
242
- 'Issue is out of scope and is not implemented, and a comment saying the Issue no longer has a ' +
243
- 'reason to exist stops the run with a `confirmation` Telegram. The procedure is ' +
244
- '`.claude/skills/workflow-commands/issue-comments.md`. ' +
245
- 'Every body-only read is refused until the comments are read.'
246
-
247
143
  // The reading of the call itself — which spellings carry a body inline, and what the shell does to
248
144
  // the value — is `shell-body-trigger.ts`, beside its own cases, as is the refusal text. A row states
249
145
  // its trigger and its text; a model of zsh quoting is more than a row.
250
146
  const { SHELL_BODY_REASON, carries_a_body, is_shell_evaluated_body, keeps_body_safe } =
251
147
  shell_body_trigger
252
148
 
253
- // **Keeping the WIP cap is counting the open Issues**, which is the one act the rule asks for before
254
- // a filing.
255
- //
256
- // **It has to be Issues, open, and a listing — all three, in one segment.** A pattern that took any
257
- // of them alone scored `gh pr list --state open` and `epic:bundle`'s candidate search as the count,
258
- // and those inflate exactly the ratio the retirement decision reads. `gh issue list` alone is not
259
- // enough either: the cap is about *open* Issues, and a listing that does not say so is some other
260
- // question. Segment-wise like the comments predicate, so a spelling quoted inside a filing's body
261
- // is not read as the count that filing skipped.
262
- const ISSUE_LISTING = /^gh\s+(?:-{1,2}[\w-]+(?:[= ]\S+)?\s+)*issue\s+list\b/u
263
- const ISSUES_QUERY = /repos\/[^\s'"]*\/issues\?[^\s'"]*state=open/u
264
- const OPEN_STATE = /--state[= ]open|state=open/u
265
- // **A label filter makes it a different question.** `gh issue list --label epic --state open` and
266
- // `…/issues?labels=epic&state=open` ask which epics are open, which is what `epic:bundle` and the
267
- // Issue template do; counting either as the WIP count would credit the cap as kept by a run that
268
- // never counted the backlog. The residual the pattern cannot separate is named in
269
- // `docs/josh-commands.md`: the inventory command in the `diag` skill is byte-identical to a hand count
270
- // of the backlog, so no pattern can tell those two apart.
271
- const LABEL_FILTER = /--label\b|[?&]labels=/u
272
-
273
- function counts_open_issues(command: string): boolean {
274
- return shell_segments.segments_of(command).some((segment) => {
275
- if (LABEL_FILTER.test(segment)) return false
276
-
277
- return (ISSUE_LISTING.test(segment) && OPEN_STATE.test(segment)) || ISSUES_QUERY.test(segment)
278
- })
279
- }
280
-
281
- // **Keeping the comments rule is fetching them**, in any of the spellings the refusal hands back.
282
- function reads_issue_comments(command: string): boolean {
283
- return shell_segments.segments_of(command).some((segment) => fetches_issue_comments(segment))
284
- }
285
-
286
149
  // **The occasion the pre-gate cut governs, in the one shape that separates a cut run's two halves**
287
150
  // (joshuafolkken/kit#1867). Asking about the cut is what a lane child does whatever it goes on to do;
288
151
  // claiming the working tree is what only the process on the near side of the boundary does, because
@@ -302,12 +165,7 @@ const DELIVERED_RULES: ReadonlyArray<DeliveredRule> = [
302
165
  // `epic:bundle` itself. It claims the direct call only, so the rows below — which trigger on the
303
166
  // `issue:file` call — never overlap it.
304
167
  direct_filing.ROW,
305
- {
306
- id: 'wip-cap',
307
- is_trigger: on_bash_command(is_issue_filing),
308
- reason: WIP_CAP_REASON,
309
- keeps: on_bash_command(counts_open_issues),
310
- },
168
+ wip_cap.ROW,
311
169
  // **Two more rows share the filing trigger** (joshuafolkken/kit#2119, joshuafolkken/kit#2213), listed
312
170
  // after `wip-cap` so the backlog count still speaks first: `filing-cap` refuses every filing past
313
171
  // the per-run ceiling, and `issue-fold` refuses a *second* filing the run has not folded. Each is a
@@ -315,14 +173,8 @@ const DELIVERED_RULES: ReadonlyArray<DeliveredRule> = [
315
173
  // delivery is still correct one reissue later — so an unfolded, over-cap second filing is delivered
316
174
  // `wip-cap`, then `filing-cap`, then `issue-fold` across its reissues.
317
175
  filing_cap.ROW,
318
- issue_fold.ROW,
319
- {
320
- id: 'issue-comments',
321
- is_trigger: on_bash_command(is_body_only_issue_read),
322
- reason: ISSUE_COMMENTS_REASON,
323
- already_satisfied: prior_comment_read.already_read_for_call,
324
- keeps: on_bash_command(reads_issue_comments),
325
- },
176
+ issue_fold_rule.ROW,
177
+ issue_comments.ROW,
326
178
  {
327
179
  id: 'shell-body',
328
180
  is_trigger: on_bash_command(is_shell_evaluated_body),
@@ -377,11 +229,11 @@ const DELIVERED_RULES: ReadonlyArray<DeliveredRule> = [
377
229
  // second inside one `fullrun` when round 2 fixes a finding in place.
378
230
  {
379
231
  id: 'run-tail',
380
- is_trigger: run_tail.is_foreground_push_step,
381
- reason: run_tail.RUN_TAIL_REASON,
382
- decide: run_tail.decide,
383
- keeps: run_tail.is_backgrounded_push_step,
384
- reaches: run_tail.is_push_step_call,
232
+ is_trigger: run_tail_rule.is_foreground_push_step,
233
+ reason: run_tail_rule.RUN_TAIL_REASON,
234
+ decide: run_tail_rule.decide,
235
+ keeps: run_tail_rule.is_backgrounded_push_step,
236
+ reaches: run_tail_rule.is_push_step_call,
385
237
  },
386
238
  // **The one row whose trigger consults the world beside the command**, because "is this a lane that
387
239
  // has not cut" is not readable from the call: it is the working directory's own name and the cut
@@ -424,22 +276,14 @@ const DELIVERED_RULES: ReadonlyArray<DeliveredRule> = [
424
276
  // "is this a dispatched lane child" is the dispatch mark against this checkout's own issue, read
425
277
  // synchronously by `lane_child_marker.is_child_of`, and the command match runs first so an ordinary
426
278
  // run pays nothing for it.
279
+ // Its once-per-run disposition is stated beside the row in `lane-park.ts`.
427
280
  //
428
- // **Once per run rather than `decide`, and no `already_satisfied`.** The stop ends the turn, so it
429
- // is not the recurring act joshuafolkken/kit#1570 wrote `decide` for. It carries no
430
- // `already_satisfied` because the park it asks for is a GitHub label a synchronous guard cannot
431
- // read, and the transcript-tail alternative would read a child that merely *read*
432
- // `backlogrun-park.md` — whose prose carries the `labels[]=needs-decision` command — as compliant
433
- // and fall silent on a real violation. `lane-park.ts` states why one wasted reissue on the compliant
434
- // path is the safe direction. `keeps` reads an actual label-application `Bash` command, never the
435
- // tail, so it is not fooled the same way; `reaches` stays `is_trigger`, since a compliant run issues
436
- // the same confirmation notify and belongs in the denominator.
437
- {
438
- id: 'lane-park',
439
- is_trigger: on_bash_command(lane_park.is_unparked_stop),
440
- reason: lane_park.LANE_PARK_REASON,
441
- keeps: on_bash_command(lane_park.records_the_park),
442
- },
281
+ // **A split child's park is refused first** (joshuafolkken/kit#3296): a lane child that promoted its
282
+ // own Issue to an epic this run has nothing to park, so `lane-split-park` refuses the label write
283
+ // and the confirmation notify onto that epic on every occurrence. It shares the notify with
284
+ // `lane-park` and is listed first; a run that promoted nothing falls through to `lane-park`.
285
+ lane_split_park.ROW,
286
+ lane_park.ROW,
443
287
  // **joshuafolkken/kit#2034's rule, one tool-call earlier** (joshuafolkken/kit#2201). `lane-park`
444
288
  // fires on the `confirmation` notify a routed child reaches; a child that reaches for
445
289
  // `AskUserQuestion` never gets there, because the harness ends its turn at the refused ask. This
@@ -782,8 +626,8 @@ const delivered_rules = {
782
626
  DIRECT_FILING_REASON: direct_filing.DIRECT_FILING_REASON,
783
627
  FILING_CAP_REASON: filing_cap.FILING_CAP_REASON,
784
628
  GIT_FORCE_REASON: git_force.GIT_FORCE_REASON,
785
- ISSUE_COMMENTS_REASON,
786
- ISSUE_FOLD_REASON: issue_fold.ISSUE_FOLD_REASON,
629
+ ISSUE_COMMENTS_REASON: issue_comments.ISSUE_COMMENTS_REASON,
630
+ ISSUE_FOLD_REASON: issue_fold_rule.ISSUE_FOLD_REASON,
787
631
  LANE_INTERACTIVE_ASK_REASON: lane_interactive_ask.LANE_INTERACTIVE_ASK_REASON,
788
632
  LANE_PARK_REASON: lane_park.LANE_PARK_REASON,
789
633
  MEASURED_RULES,
@@ -791,16 +635,16 @@ const delivered_rules = {
791
635
  POLL_LOOP_REASON: poll_loop.POLL_LOOP_REASON,
792
636
  PRE_GATE_CUT_REASON: pre_gate_cut.PRE_GATE_CUT_REASON,
793
637
  RAW_FIELD_BODY_REASON: raw_field_body.RAW_FIELD_BODY_REASON,
794
- RUN_TAIL_REASON: run_tail.RUN_TAIL_REASON,
638
+ RUN_TAIL_REASON: run_tail_rule.RUN_TAIL_REASON,
795
639
  SHELL_BODY_REASON,
796
640
  SWITCH_ENV_KEY,
797
641
  THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
798
- WIP_CAP,
799
- WIP_CAP_REASON,
642
+ WIP_CAP: wip_cap.WIP_CAP,
643
+ WIP_CAP_REASON: wip_cap.WIP_CAP_REASON,
800
644
  WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
801
645
  delivery,
802
646
  delivery_path,
803
- is_body_only_issue_read,
647
+ is_body_only_issue_read: issue_comments.is_body_only_issue_read,
804
648
  is_enabled,
805
649
  is_issue_filing,
806
650
  }
@@ -14,7 +14,7 @@ const DIRECT_FILING_REASON =
14
14
  'lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing ' +
15
15
  'into another repository, and `epic:bundle` after the create — so none of them is a step to remember. ' +
16
16
  'When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,…>` ' +
17
- 'for the ones that are separate deliverables. Reference: `docs/josh-commands-automation.md` → "`josh issue:file`". ' +
17
+ 'for the ones that are separate deliverables. Reference: `docs/josh-commands-backlog.md` → "`josh issue:file`". ' +
18
18
  '**This rule fires on every occurrence, not once per run.**'
19
19
 
20
20
  function is_any_filing(command: string): boolean {
@@ -1,7 +1,7 @@
1
1
  import { hook_decision, type GuardRun } from '#scripts/josh/hook-decision'
2
- import { run_progress } from '#scripts/run/run-progress'
3
- import { run_progress_clock } from '#scripts/run/run-progress-clock'
4
- import { run_progress_config } from '#scripts/run/run-progress-config'
2
+ import { run_progress } from '#scripts/run/progress/run-progress'
3
+ import { run_progress_clock } from '#scripts/run/progress/run-progress-clock'
4
+ import { run_progress_config } from '#scripts/run/progress/run-progress-config'
5
5
  import type { GuardedCall } from '#scripts/time-runtime/time-batch-guard'
6
6
  import { time_shell } from '#scripts/time-runtime/time-shell'
7
7
  import { shell_segments } from './shell-segments'
@@ -11,7 +11,7 @@
11
11
  // :branch`), because none of those put the literal the glob keys on where the glob looks. Parsing the
12
12
  // arguments is what makes the judgement independent of the spelling.
13
13
 
14
- // The wrapper words that may stand in front of `git`, the same ones `run-tail.ts` skips in front of
14
+ // The wrapper words that may stand in front of `git`, the same ones `run-tail-rule.ts` skips in front of
15
15
  // `josh`: environment assignments, the package-manager launchers, `sudo`, `env`, and an opening
16
16
  // subshell parenthesis. Left in, `sudo git push --force`, `env git commit` and `(git push -f)` are
17
17
  // silently not a git command (joshuafolkken/kit#2983).
@@ -15,7 +15,7 @@ import { shell_segments } from './shell-segments'
15
15
  //
16
16
  // **It fires on every occurrence, not once per run.** A force push is destructive every time it is
17
17
  // issued, so refused-once-and-free-after would put the second one back on the run's self-restraint —
18
- // the disposition `run-tail.ts` and `filing-cap.ts` take for the same reason.
18
+ // the disposition `run-tail-rule.ts` and `filing-cap.ts` take for the same reason.
19
19
 
20
20
  // A force push. The long forms all open with `--force` (`--force`, `--force-with-lease[=ref]`,
21
21
  // `--force-if-includes`); `-f` and any cluster carrying `f` are the short forms; a bare `--delete` and
@@ -5,8 +5,8 @@ import { cost_verdict } from '#scripts/cost-runtime/cost-verdict'
5
5
  import type { GuardRun } from '#scripts/josh/hook-decision'
6
6
  import { lane_child_marker, type MarkerSource } from '#scripts/lane/lane-child-marker'
7
7
  import { lane_paths } from '#scripts/lane/lane-paths'
8
- import { run_cut, type RunCut } from '#scripts/run/run-cut'
9
- import { run_cut_handoff } from '#scripts/run/run-cut-handoff'
8
+ import { run_cut, type RunCut } from '#scripts/run/cut/run-cut'
9
+ import { run_cut_handoff } from '#scripts/run/cut/run-cut-handoff'
10
10
  import type { GuardedCall } from '#scripts/time-runtime/time-batch-guard'
11
11
  import { bash_triggers } from './bash-triggers'
12
12
  import { implementation_cut_verdict } from './implementation-cut-verdict'
@@ -52,7 +52,7 @@ function is_index_mutation(command: string): boolean {
52
52
  const INDEX_MUTATION_REASON =
53
53
  '⛔ git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore ' +
54
54
  "--staged` overwrite the user's staged snapshot, which `CLAUDE.md` → Git Rules forbids on your own " +
55
- 'judgement (`prompts/collaboration-workflow/operating-rules.md` → "git index を勝手に変更しない"). The ' +
55
+ 'judgement (`prompts/collaboration-workflow/operating-rules.md` → "no-self-staging"). The ' +
56
56
  '`.claude/settings.json` deny list matches prefix globs, so it misses `git -C . commit`, `git -c ' +
57
57
  'k=v commit` and `env git commit`; this row reads the argv instead, so the spelling does not matter. ' +
58
58
  'A commit goes through `pnpm josh git -y`; any other staging needs explicit current-turn user ' +
@@ -0,0 +1,118 @@
1
+ import { bash_triggers } from './bash-triggers'
2
+ import { gh_api } from './gh-api'
3
+ import { prior_comment_read } from './prior-comment-read'
4
+ import { shell_segments } from './shell-segments'
5
+
6
+ // The trigger and the delivered text behind the `issue-comments` row of `delivered-rules.ts`, moved
7
+ // out of the enumeration so the list stays a list (joshuafolkken/kit#3264).
8
+
9
+ // **A shell line carries several commands, and the subcommand has to be the one being invoked.**
10
+ // Each segment is judged on its own, anchored at its start, so `gh issue comment <N> -b "… gh issue
11
+ // view <N> …"` is read as the write it is rather than as the read it quotes. The cut itself is
12
+ // `shell-segments.ts`, shared with the triggers that need the same one.
13
+ // Global flags may precede the subcommand (`gh --repo o/r issue view 1`), so they are skipped —
14
+ // the same optional-flag prefix `gh-api.ts` skips in front of `api`, shared from there.
15
+ const ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, 'u')
16
+ // `…/issues/<N>` — **one** Issue's body. The number has to end the path, so the listing
17
+ // (`…/issues`) and every sub-resource under it (`…/issues/1319/comments`) are left alone.
18
+ const ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u
19
+ // **A write to that path is not a read of it**, which is why the body read below asks `gh_api.is_read`:
20
+ // `gh api` sends POST as soon as any field flag appears, and `kickoff` PATCHes `…/issues/<N>` to
21
+ // normalize a title and to fill a blank body — so without the read check the delivery would be spent
22
+ // refusing a write, and the genuine body read later in the same run would never be guarded.
23
+ // A segment that fetches comments: the flag, a `comments` field in a `--json` projection, or the
24
+ // comments endpoint. The short `-c` is deliberately absent — it belongs to `wc`, `grep` and `sort`
25
+ // far more often than to `gh`, and reading it as "comments included" silenced the rule on any line
26
+ // that ended in a pipe. A run that types it pays one round trip instead.
27
+ const FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u
28
+ const ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u
29
+
30
+ // A field projection — `--jq` for `gh api`, `--json` for `gh issue view` — whose value never names the
31
+ // body. `gh api …/issues/<N> --jq '{state, labels}'` fetches the Issue only to read its state or its
32
+ // labels, which is a state check and not the body read this rule guards (joshuafolkken/kit#1905); a
33
+ // `--jq '.body'` still names the body and stays a body read. The value is taken quoted or bare, so the
34
+ // comma list `--json state,labels` and the expression `'{state, labels}'` are read the same way.
35
+ const FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u
36
+ const NAMES_THE_BODY = /\bbody\b/u
37
+
38
+ function projects_away_body(segment: string): boolean {
39
+ const projection = FIELD_PROJECTION.exec(segment)
40
+
41
+ return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? '')
42
+ }
43
+
44
+ function is_body_read_segment(segment: string): boolean {
45
+ if (projects_away_body(segment)) return false
46
+
47
+ if (ISSUE_VIEW_COMMAND.test(segment)) return true
48
+
49
+ return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment)
50
+ }
51
+
52
+ // **An Issue's comments, not just any comments.** Batching pushes a run to fetch the body and the
53
+ // comments on one line, so the allowance has to reach across segments — but `gh pr view 42 --json
54
+ // comments` says nothing about whether *this* Issue was read whole, so the segment doing the
55
+ // fetching has to be an Issue read itself.
56
+ function fetches_issue_comments(segment: string): boolean {
57
+ if (!FETCHES_COMMENTS.test(segment)) return false
58
+
59
+ return (
60
+ ISSUE_VIEW_COMMAND.test(segment) || (gh_api.is_gh_api(segment) && ISSUES_PATH.test(segment))
61
+ )
62
+ }
63
+
64
+ // **The trigger is the body read, not the start of implementation.** The moment an Issue's body
65
+ // reaches a run is the moment the rule binds, and it is one shell call — the test
66
+ // `prompts/collaboration-workflow/rule-delivery.md` sets for leaving residency.
67
+ function is_body_only_issue_read(command: string): boolean {
68
+ const segments = shell_segments.segments_of(command)
69
+
70
+ if (segments.some((segment) => fetches_issue_comments(segment))) return false
71
+
72
+ return segments.some((segment) => is_body_read_segment(segment))
73
+ }
74
+
75
+ // **Keeping the comments rule is fetching them**, in any of the spellings the refusal hands back.
76
+ function reads_issue_comments(command: string): boolean {
77
+ return shell_segments.segments_of(command).some((segment) => fetches_issue_comments(segment))
78
+ }
79
+
80
+ // **The refusal hands over the command that fixes it**, because reading is not the same as
81
+ // obeying: a sentence saying "also read the comments" is prose of exactly the kind
82
+ // joshuafolkken/kit#1344 measured as moving nothing, while a refused body read leaves the run
83
+ // holding the reissue that makes the comments *present*. The conflict rule ships with it — a
84
+ // delivery that said only "read them" would hand back the deciding at the moment nothing else is
85
+ // open to read, the mistake joshuafolkken/kit#1518 corrected for the WIP cap.
86
+ const ISSUE_COMMENTS_REASON =
87
+ "⛔ an Issue's comments are part of the Issue: read them before implementing, not only the body. " +
88
+ 'A decision recorded after the body was written lives only in a comment — a corrected diagnosis ' +
89
+ '(joshuafolkken/kit#1537), a changed default and an added acceptance criterion ' +
90
+ '(joshuafolkken/kit#1520), a scope handed to another Issue (joshuafolkken/kit#1304) — and ' +
91
+ 'nothing in the body says it was superseded, so a body-only reader builds the wrong thing and ' +
92
+ 'sees no contradiction. Reissue this read with the comments included: ' +
93
+ "`gh api repos/{owner}/{repo}/issues/<N>/comments --jq '.[] | {user: .user.login, created_at, " +
94
+ "body}'` beside the body read, or `gh issue view <N> --comments` where GraphQL is reachable. " +
95
+ 'Then the later text is the agreement in force — a comment supersedes the body it contradicts — ' +
96
+ "except for two answers that are not the run's to make: work a comment reassigns to another " +
97
+ 'Issue is out of scope and is not implemented, and a comment saying the Issue no longer has a ' +
98
+ 'reason to exist stops the run with a `confirmation` Telegram. The procedure is ' +
99
+ '`.claude/skills/workflow-commands/issue-comments.md`. ' +
100
+ 'Every body-only read is refused until the comments are read.'
101
+
102
+ // The enumeration row itself, so `delivered-rules.ts` spreads one entry. `already_satisfied` stands it
103
+ // down once the comments are on the transcript tail, and until then it refuses every body-only read.
104
+ const ROW = {
105
+ id: 'issue-comments',
106
+ is_trigger: bash_triggers.on_bash_command(is_body_only_issue_read),
107
+ reason: ISSUE_COMMENTS_REASON,
108
+ already_satisfied: prior_comment_read.already_read_for_call,
109
+ keeps: bash_triggers.on_bash_command(reads_issue_comments),
110
+ }
111
+
112
+ const issue_comments = {
113
+ ISSUE_COMMENTS_REASON,
114
+ ROW,
115
+ is_body_only_issue_read,
116
+ }
117
+
118
+ export { issue_comments }
@@ -67,6 +67,6 @@ const ROW = {
67
67
  keeps: bash_triggers.on_bash_command(runs_the_fold),
68
68
  }
69
69
 
70
- const issue_fold = { ISSUE_FOLD_REASON, ROW, already_folded, runs_the_fold }
70
+ const issue_fold_rule = { ISSUE_FOLD_REASON, ROW, already_folded, runs_the_fold }
71
71
 
72
- export { issue_fold }
72
+ export { issue_fold_rule }
@@ -7,7 +7,7 @@ import {
7
7
  time_transcript_line,
8
8
  type Block as TranscriptBlock,
9
9
  } from '#scripts/time-runtime/time-transcript-line'
10
- import { run_tail } from './run-tail'
10
+ import { run_tail_rule } from './run-tail-rule'
11
11
 
12
12
  // A dispatched lane child is stopped from backgrounding a long-running josh command (joshuafolkken/kit#2704).
13
13
  //
@@ -85,7 +85,7 @@ function is_backgrounded_ship(call: GuardedCall): boolean {
85
85
  // detached route on its first refusal rather than two contradictory ones. A backgrounded `ship` alone is
86
86
  // not refused: `foreground_input` turns it into the foreground call the refusal used to ask for.
87
87
  function is_candidate(call: GuardedCall): boolean {
88
- if (run_tail.is_push_step_call(call)) return true
88
+ if (run_tail_rule.is_push_step_call(call)) return true
89
89
 
90
90
  const command = backgrounded_bash_command(call)
91
91
 
@@ -1,6 +1,7 @@
1
1
  import { NEEDS_DECISION_LABEL } from '#scripts/issue/issue-labels'
2
2
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
3
3
  import { lane_handoff } from '#scripts/lane/lane-handoff'
4
+ import { bash_triggers } from './bash-triggers'
4
5
  import { shell_segments } from './shell-segments'
5
6
 
6
7
  // A dispatched lane child records its park before it stops, delivered at the call it binds on
@@ -124,9 +125,26 @@ const LANE_PARK_REASON =
124
125
  'procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` → "A lane child records its park ' +
125
126
  'before it stops", and the park itself is `backlogrun-park.md` → "park and continue".'
126
127
 
128
+ // The row itself, so `delivered-rules.ts` spreads one entry. **Once per run rather than `decide`, and
129
+ // no `already_satisfied`.** The stop ends the turn, so it is not the recurring act joshuafolkken/kit#1570
130
+ // wrote `decide` for. It carries no `already_satisfied` because the park it asks for is a GitHub label a
131
+ // synchronous guard cannot read, and the transcript-tail alternative would read a child that merely
132
+ // *read* `backlogrun-park.md` — whose prose carries the `labels[]=needs-decision` command — as
133
+ // compliant and fall silent on a real violation (the header above). `keeps` reads an actual
134
+ // label-application `Bash` command, never the tail, so it is not fooled the same way; `reaches` stays
135
+ // `is_trigger`, since a compliant run issues the same confirmation notify and belongs in the
136
+ // denominator.
137
+ const ROW = {
138
+ id: 'lane-park',
139
+ is_trigger: bash_triggers.on_bash_command(is_unparked_stop),
140
+ reason: LANE_PARK_REASON,
141
+ keeps: bash_triggers.on_bash_command(records_the_park),
142
+ }
143
+
127
144
  const lane_park = {
128
145
  COMMAND_NOTIFY_MARKER,
129
146
  LANE_PARK_REASON,
147
+ ROW,
130
148
  is_confirmation_notify,
131
149
  is_parking_child,
132
150
  is_unparked_stop,