@joshuafolkken/kit 1.1229.0 → 1.1370.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 (939) hide show
  1. package/.claude/agents/investigator.md +1 -0
  2. package/.claude/settings.json +4 -0
  3. package/.claude/skills/dependency-update/SKILL.md +5 -3
  4. package/.claude/skills/epic-commands/SKILL.md +82 -113
  5. package/.claude/skills/epic-commands/epic-bundle.md +38 -63
  6. package/.claude/skills/epic-commands/execution-waves.md +30 -51
  7. package/.claude/skills/verify-ui/SKILL.md +13 -20
  8. package/.claude/skills/workflow-commands/SKILL.md +10 -10
  9. package/.claude/skills/workflow-commands/background-commands.md +4 -4
  10. package/.claude/skills/workflow-commands/backlogrun-child.md +30 -105
  11. package/.claude/skills/workflow-commands/backlogrun-handoff-report.md +29 -0
  12. package/.claude/skills/workflow-commands/backlogrun-lanes.md +45 -131
  13. package/.claude/skills/workflow-commands/backlogrun-park.md +36 -53
  14. package/.claude/skills/workflow-commands/backlogrun-progress.md +81 -331
  15. package/.claude/skills/workflow-commands/backlogrun-recovery.md +29 -83
  16. package/.claude/skills/workflow-commands/backlogrun-steps.md +105 -424
  17. package/.claude/skills/workflow-commands/backlogrun.md +50 -61
  18. package/.claude/skills/workflow-commands/chain-rule.md +36 -40
  19. package/.claude/skills/workflow-commands/entry-sequence.md +6 -6
  20. package/.claude/skills/workflow-commands/followup.md +24 -20
  21. package/.claude/skills/workflow-commands/fullrun-steps.md +23 -27
  22. package/.claude/skills/workflow-commands/fullrun.md +10 -10
  23. package/.claude/skills/workflow-commands/halfrun.md +9 -8
  24. package/.claude/skills/workflow-commands/into-target.md +12 -18
  25. package/.claude/skills/workflow-commands/issue-comments.md +11 -26
  26. package/.claude/skills/workflow-commands/kickoff.md +27 -16
  27. package/.claude/skills/workflow-commands/latest-gate.md +18 -29
  28. package/.claude/skills/workflow-commands/needs-human-review.md +13 -24
  29. package/.claude/skills/workflow-commands/observation-filing.md +37 -28
  30. package/.claude/skills/workflow-commands/observation-ledger.md +48 -38
  31. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -9
  32. package/.claude/skills/workflow-commands/prerequisite.md +23 -15
  33. package/.claude/skills/workflow-commands/progress-watcher.md +17 -11
  34. package/.claude/skills/workflow-commands/prrun.md +3 -3
  35. package/.claude/skills/workflow-commands/retrospective.md +13 -18
  36. package/.claude/skills/workflow-commands/split-assessment.md +6 -4
  37. package/.claude/skills/workflow-commands/target-repository.md +25 -28
  38. package/.claude/skills/workflow-commands/working-tree-hold.md +11 -17
  39. package/.gitattributes +2 -1
  40. package/.github/workflows/ci.yml +14 -0
  41. package/.github/workflows/publish.yml +2 -0
  42. package/.vscode/tasks.json +14 -0
  43. package/CLAUDE.md +10 -61
  44. package/README.md +38 -57
  45. package/SECURITY.md +1 -1
  46. package/dist/CLAUDE.md +10 -61
  47. package/dist/commands/document-section-cli.js +42 -10
  48. package/dist/config-merge/index.js +11 -2
  49. package/dist/hooks/audit-provision.js +2 -2
  50. package/dist/hooks/batch-guard.js +7 -7
  51. package/dist/hooks/{chunk-FIVDHQGT.js → chunk-43G45XJF.js} +18 -20
  52. package/dist/hooks/{chunk-JUDKDCDP.js → chunk-4TW4MQUL.js} +284 -362
  53. package/dist/hooks/{chunk-5VMKCCC4.js → chunk-76RK6E3H.js} +13 -11
  54. package/dist/hooks/{chunk-RNWHN5BP.js → chunk-DZWZ3DKY.js} +4 -4
  55. package/dist/hooks/{chunk-5VNDSNG3.js → chunk-FZ6FUSBM.js} +1 -1
  56. package/dist/hooks/{chunk-VC3PZPZO.js → chunk-GG72ANKI.js} +1 -1
  57. package/dist/hooks/{chunk-6HJJXRBX.js → chunk-KPM4YIIX.js} +8 -44
  58. package/dist/hooks/chunk-MXVQMLDP.js +3705 -0
  59. package/dist/hooks/{chunk-ELCN5LCH.js → chunk-NEC4AT4Z.js} +34 -12
  60. package/dist/hooks/{chunk-TQNPDZNH.js → chunk-NZBHG6FW.js} +7 -5
  61. package/dist/hooks/chunk-NZN2UFI6.js +2409 -0
  62. package/dist/hooks/{chunk-F56MFS4B.js → chunk-R6CRQEAL.js} +4 -4
  63. package/dist/hooks/chunk-SYSRHRWU.js +1589 -0
  64. package/dist/hooks/{chunk-55HHG5PX.js → chunk-TLJYAGZK.js} +799 -903
  65. package/dist/hooks/chunk-Z5TMJIPW.js +45 -0
  66. package/dist/hooks/codex-hook-adapter.js +26 -21
  67. package/dist/hooks/duplicate-read-guard.js +8 -8
  68. package/dist/hooks/format-edited.js +4 -4
  69. package/dist/hooks/inputs.json +213 -197
  70. package/dist/hooks/investigation-guard.js +8 -8
  71. package/dist/hooks/pretool-guard.js +15 -15
  72. package/dist/hooks/session-lang.js +406 -8
  73. package/dist/hooks/stop-guard.js +122 -201
  74. package/dist/josh.js +540 -137
  75. package/dist/version/index.js +15 -13
  76. package/eslint/base.js +43 -5
  77. package/eslint/rules/english-only-prose.js +3 -3
  78. package/eslint/rules/import.js +12 -0
  79. package/eslint/rules/namespace-object-export.js +2 -2
  80. package/package.json +16 -10
  81. package/pnpm-workspace.yaml +2 -4
  82. package/prompts/coding-standards.md +14 -48
  83. package/prompts/collaboration-workflow/file-edits.md +1 -1
  84. package/prompts/collaboration-workflow/issue-citation.md +1 -1
  85. package/prompts/collaboration-workflow/issue-template.md +4 -10
  86. package/prompts/collaboration-workflow/operating-rules.md +7 -8
  87. package/prompts/collaboration-workflow/output-bounds.md +1 -1
  88. package/prompts/collaboration-workflow/overview.md +1 -1
  89. package/prompts/collaboration-workflow/plan-comment.md +2 -1
  90. package/prompts/collaboration-workflow/principles.md +21 -3
  91. package/prompts/collaboration-workflow/report-format.md +16 -29
  92. package/prompts/collaboration-workflow/residency.md +1 -1
  93. package/prompts/collaboration-workflow/rule-delivery.md +7 -95
  94. package/prompts/collaboration-workflow/shell-body.md +3 -23
  95. package/prompts/collaboration-workflow/turn-batching.md +4 -13
  96. package/prompts/collaboration-workflow/wip-cap.md +5 -11
  97. package/prompts/refactoring.md +1 -1
  98. package/prompts/review-rubric.md +3 -22
  99. package/prompts/review.md +15 -38
  100. package/prompts/testing-guide.md +2 -4
  101. package/scripts/adopt/adopt-logic.ts +5 -5
  102. package/scripts/adopt/adopt-toolkits.ts +4 -5
  103. package/scripts/adopt/adopt.ts +2 -3
  104. package/scripts/agent/agent-argv.ts +7 -7
  105. package/scripts/agent/agent-diagnostics.ts +2 -2
  106. package/scripts/agent/agent-exit-record.ts +1 -1
  107. package/scripts/agent/agent-headless.ts +1 -1
  108. package/scripts/agent/agent-launch-environment.ts +6 -1
  109. package/scripts/agent/agent-role-profile.ts +49 -22
  110. package/scripts/agent/agent-session-role.ts +32 -0
  111. package/scripts/agent/api-outage.ts +1 -1
  112. package/scripts/agent/claude-agent-argv.ts +4 -4
  113. package/scripts/agent/claude-result-event.ts +4 -4
  114. package/scripts/agent/codex-hooks.ts +2 -2
  115. package/scripts/agent/interactive-ask.ts +3 -6
  116. package/scripts/auto-ok/auto-ok-cli.ts +46 -60
  117. package/scripts/backlog/backlog-arrival-added.ts +34 -0
  118. package/scripts/backlog/backlog-arrival.ts +54 -17
  119. package/scripts/backlog/backlog-budget-cli.ts +34 -6
  120. package/scripts/backlog/backlog-budget.ts +17 -24
  121. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  122. package/scripts/backlog/backlog-drive-args.ts +5 -4
  123. package/scripts/backlog/backlog-drive-cli.ts +14 -10
  124. package/scripts/backlog/backlog-drive-epic.ts +85 -0
  125. package/scripts/backlog/backlog-drive-launch.ts +1 -1
  126. package/scripts/backlog/backlog-drive-named-offer.ts +60 -9
  127. package/scripts/backlog/backlog-drive-named.ts +39 -9
  128. package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
  129. package/scripts/backlog/backlog-drive-restore.ts +1 -1
  130. package/scripts/backlog/backlog-drive-retrospective.ts +2 -2
  131. package/scripts/backlog/backlog-drive.ts +43 -28
  132. package/scripts/backlog/backlog-idle.ts +88 -0
  133. package/scripts/backlog/backlog-named.ts +11 -15
  134. package/scripts/backlog/backlog-next.ts +26 -30
  135. package/scripts/backlog/backlog-offer-cli.ts +28 -23
  136. package/scripts/backlog/backlog-offer.ts +7 -7
  137. package/scripts/backlog/backlog-overlap.ts +178 -0
  138. package/scripts/backlog/backlog-plan-cli.ts +14 -90
  139. package/scripts/backlog/backlog-plan-read.ts +111 -0
  140. package/scripts/backlog/backlog-plan.ts +11 -10
  141. package/scripts/backlog/backlog-pool.ts +13 -14
  142. package/scripts/backlog/backlog-rank.ts +11 -11
  143. package/scripts/backlog/backlog-ready.ts +15 -10
  144. package/scripts/backlog/backlog-scope.ts +14 -15
  145. package/scripts/backlog/backlog-stalled-cli.ts +2 -2
  146. package/scripts/backlog/backlog-stalled-detect.ts +4 -4
  147. package/scripts/backlog/backlog-stalled.ts +5 -6
  148. package/scripts/backlog/backlog-waves.ts +4 -4
  149. package/scripts/backlog/backlogrun-args.ts +74 -0
  150. package/scripts/backlog/backlogrun-cli.ts +113 -0
  151. package/scripts/backlog/backlogrun-launch.ts +178 -0
  152. package/scripts/behavior/behavior-assertion.ts +6 -8
  153. package/scripts/behavior/behavior-cli.ts +1 -1
  154. package/scripts/behavior/behavior-rules.ts +3 -3
  155. package/scripts/build/build-claude-md.ts +4 -4
  156. package/scripts/build/build-codex-hooks.ts +2 -2
  157. package/scripts/build/build-commands.ts +1 -1
  158. package/scripts/build/build-hooks.ts +5 -5
  159. package/scripts/build/build.ts +1 -1
  160. package/scripts/build/hook-bundle-stamp.ts +109 -7
  161. package/scripts/build/local-bin.ts +2 -2
  162. package/scripts/cases/cases-cli.ts +1 -1
  163. package/scripts/cases/cases-logic.ts +2 -2
  164. package/scripts/ci/composite-actions.ts +1 -2
  165. package/scripts/ci/pr-classification.ts +2 -2
  166. package/scripts/claude/workflow-destination.ts +1 -1
  167. package/scripts/clone/clone-aggregate.ts +1 -1
  168. package/scripts/clone/clone-fingerprint.ts +1 -1
  169. package/scripts/clone/clone-scan-cli.ts +1 -1
  170. package/scripts/clone/clone-scan.ts +1 -1
  171. package/scripts/config-merge/index.ts +1 -1
  172. package/scripts/config-merge/json-format.ts +1 -3
  173. package/scripts/config-merge/patch-json-array.ts +90 -0
  174. package/scripts/config-merge/patch-json-key.ts +6 -3
  175. package/scripts/config-merge/patch-json-list.ts +1 -1
  176. package/scripts/cost-runtime/codex-usage.ts +1 -1
  177. package/scripts/cost-runtime/context-cut-payback.ts +4 -5
  178. package/scripts/cost-runtime/context-cut-threshold.ts +4 -6
  179. package/scripts/cost-runtime/cost-attribute.ts +3 -3
  180. package/scripts/cost-runtime/cost-blocks.ts +3 -4
  181. package/scripts/cost-runtime/cost-cli.ts +14 -11
  182. package/scripts/cost-runtime/cost-corpus.ts +11 -11
  183. package/scripts/cost-runtime/cost-pricing.ts +4 -4
  184. package/scripts/cost-runtime/cost-tokens.ts +1 -2
  185. package/scripts/cost-runtime/cost-transcript.ts +29 -29
  186. package/scripts/cost-runtime/cost-usage.ts +6 -7
  187. package/scripts/cost-runtime/cost-verdict.ts +10 -11
  188. package/scripts/cost-runtime/own-session.ts +1 -1
  189. package/scripts/cost-runtime/transcript-cwd.ts +2 -2
  190. package/scripts/delegation/delegation-cli.ts +4 -4
  191. package/scripts/delegation/delegation-policy.ts +13 -15
  192. package/scripts/delegation/duplicate-read-guard.ts +9 -10
  193. package/scripts/delegation/fanout-cli.ts +1 -1
  194. package/scripts/delegation/fanout.ts +1 -1
  195. package/scripts/delegation/investigation-guard.ts +18 -22
  196. package/scripts/delegation/investigation-reads.ts +48 -238
  197. package/scripts/discovery/repo-discovery.ts +1 -1
  198. package/scripts/discovery/repo-map-logic.ts +1 -2
  199. package/scripts/discovery/repo-origin.ts +3 -3
  200. package/scripts/discovery/repo-party-cli.ts +2 -2
  201. package/scripts/discovery/repo-party.ts +2 -2
  202. package/scripts/doctor/doctor-consumer.ts +1 -1
  203. package/scripts/doctor/doctor-io.ts +6 -7
  204. package/scripts/doctor/doctor-logic.ts +1 -1
  205. package/scripts/doctor/doctor-ports.ts +1 -1
  206. package/scripts/doctor/doctor.ts +8 -9
  207. package/scripts/document/backlogrun-parent-read-set.ts +5 -6
  208. package/scripts/document/bash-output-cap.ts +5 -7
  209. package/scripts/document/capped-print-part.ts +1 -1
  210. package/scripts/document/capped-print.ts +1 -1
  211. package/scripts/document/document-byte-budget.ts +33 -64
  212. package/scripts/document/document-byte-check.ts +4 -4
  213. package/scripts/document/document-reachability.ts +7 -8
  214. package/scripts/document/document-read-cli.ts +2 -2
  215. package/scripts/document/document-scan.ts +26 -2
  216. package/scripts/document/document-section-cli.ts +6 -8
  217. package/scripts/document/document-section.ts +8 -12
  218. package/scripts/document/edit-files-cli.ts +3 -4
  219. package/scripts/document/entry-read-budget.ts +8 -70
  220. package/scripts/document/entry-read-set.ts +51 -34
  221. package/scripts/document/lane-child-read-set.ts +9 -9
  222. package/scripts/document/read-files-cli.ts +1 -2
  223. package/scripts/document/read-set-cli.ts +16 -19
  224. package/scripts/document/read-set-cost.ts +4 -4
  225. package/scripts/document/read-set-point-of-use.ts +28 -38
  226. package/scripts/document/read-set-pricing.ts +1 -1
  227. package/scripts/document/read-set-trim.ts +3 -3
  228. package/scripts/document/resident-budget.ts +8 -10
  229. package/scripts/epic/epic-add-body.ts +15 -18
  230. package/scripts/epic/epic-add-plan.ts +26 -28
  231. package/scripts/epic/epic-add.ts +32 -25
  232. package/scripts/epic/epic-audit-checks.ts +27 -37
  233. package/scripts/epic/epic-audit-cli.ts +21 -22
  234. package/scripts/epic/epic-audit-orphans.ts +16 -17
  235. package/scripts/epic/epic-audit-rationale.ts +10 -15
  236. package/scripts/epic/epic-audit-report.ts +2 -5
  237. package/scripts/epic/epic-audit.ts +18 -14
  238. package/scripts/epic/epic-body.ts +10 -10
  239. package/scripts/epic/epic-bundle-cli.ts +36 -34
  240. package/scripts/epic/epic-bundle-evidence.ts +5 -5
  241. package/scripts/epic/epic-bundle-gaps.ts +6 -9
  242. package/scripts/epic/epic-bundle-poll.ts +1 -1
  243. package/scripts/epic/epic-bundle-referenced.ts +11 -14
  244. package/scripts/epic/epic-bundle.ts +19 -20
  245. package/scripts/epic/epic-busy.ts +44 -21
  246. package/scripts/epic/epic-candidate-confirm.ts +15 -18
  247. package/scripts/epic/epic-chains.ts +18 -23
  248. package/scripts/epic/epic-classify.ts +11 -12
  249. package/scripts/epic/epic-cli-argv.ts +5 -6
  250. package/scripts/epic/epic-cli-remove.ts +9 -4
  251. package/scripts/epic/epic-cli.ts +38 -26
  252. package/scripts/epic/epic-close-comment.ts +14 -15
  253. package/scripts/epic/epic-close.ts +25 -29
  254. package/scripts/epic/epic-cross-repo.ts +18 -21
  255. package/scripts/epic/epic-decision.ts +14 -18
  256. package/scripts/epic/epic-fetch.ts +25 -31
  257. package/scripts/epic/epic-graph.ts +15 -17
  258. package/scripts/epic/epic-index.ts +14 -16
  259. package/scripts/epic/epic-issue.ts +9 -11
  260. package/scripts/epic/epic-label-age.ts +74 -0
  261. package/scripts/epic/epic-lane-offer.ts +35 -24
  262. package/scripts/epic/epic-nested.ts +1 -1
  263. package/scripts/epic/epic-next-read.ts +8 -8
  264. package/scripts/epic/epic-next-views.ts +12 -12
  265. package/scripts/epic/epic-next.ts +29 -33
  266. package/scripts/epic/epic-outside-blocker.ts +11 -10
  267. package/scripts/epic/epic-parse.ts +22 -24
  268. package/scripts/epic/epic-promote.ts +3 -2
  269. package/scripts/epic/epic-rank.ts +1 -1
  270. package/scripts/epic/epic-read.ts +10 -8
  271. package/scripts/epic/epic-reconcile.ts +8 -6
  272. package/scripts/epic/epic-reference.ts +5 -4
  273. package/scripts/epic/epic-relation-recheck.ts +10 -11
  274. package/scripts/epic/epic-relations.ts +9 -8
  275. package/scripts/epic/epic-remove-plan.ts +4 -5
  276. package/scripts/epic/epic-remove.ts +7 -5
  277. package/scripts/epic/epic-report.ts +18 -20
  278. package/scripts/epic/epic-run.ts +6 -5
  279. package/scripts/epic/epic-sections.ts +2 -2
  280. package/scripts/epic/epic-shape.ts +3 -3
  281. package/scripts/epic/epic-solo-stale.ts +3 -2
  282. package/scripts/epic/epic-solo.ts +8 -8
  283. package/scripts/epic/epic-triage.ts +5 -4
  284. package/scripts/epic/epic-validate.ts +8 -7
  285. package/scripts/epic/epic.ts +5 -5
  286. package/scripts/exports/unused-members-cli.ts +1 -1
  287. package/scripts/followup/followup-issue-number.ts +1 -2
  288. package/scripts/followup/git-followup-cleanup.ts +6 -7
  289. package/scripts/followup/git-followup-finish.ts +11 -13
  290. package/scripts/followup/git-followup-flush.ts +11 -7
  291. package/scripts/followup/git-followup-issue-close.ts +16 -13
  292. package/scripts/followup/git-followup-label.ts +9 -6
  293. package/scripts/followup/git-followup-merged.ts +1 -1
  294. package/scripts/followup/git-followup-pending.ts +7 -9
  295. package/scripts/followup/git-followup-stages.ts +28 -17
  296. package/scripts/followup/git-followup-workflow.ts +19 -21
  297. package/scripts/followup/git-pr-followup-checks.ts +13 -20
  298. package/scripts/followup/git-pr-followup-wrapup.ts +7 -7
  299. package/scripts/followup/git-pr-followup.ts +18 -17
  300. package/scripts/gate/core-admission.ts +162 -0
  301. package/scripts/gate/core-budget.ts +134 -91
  302. package/scripts/gate/gate-ledger.ts +114 -0
  303. package/scripts/gate/gate-log.ts +7 -9
  304. package/scripts/gate/gate-plan.ts +103 -35
  305. package/scripts/gate/gate-report.ts +39 -21
  306. package/scripts/gate/gate-skip.ts +11 -13
  307. package/scripts/gate/gate-steps.ts +5 -1
  308. package/scripts/gate/gate-tree.ts +3 -4
  309. package/scripts/gate/hook-gate-reuse.ts +9 -9
  310. package/scripts/gate/machine-capacity.ts +313 -0
  311. package/scripts/gate/pre-commit-type-check.ts +4 -4
  312. package/scripts/gate/project-checks.ts +12 -4
  313. package/scripts/gate/scoped-green.ts +11 -18
  314. package/scripts/gate/type-check-command.ts +2 -2
  315. package/scripts/gate/type-check-step.ts +4 -4
  316. package/scripts/gate/verification-gate.ts +60 -53
  317. package/scripts/gh/gh-spawn.ts +2 -2
  318. package/scripts/gh/git-ai-review-scan.ts +3 -2
  319. package/scripts/gh/git-gh-api-path.ts +10 -11
  320. package/scripts/gh/git-gh-exec.ts +43 -48
  321. package/scripts/gh/git-gh-failure.ts +6 -9
  322. package/scripts/gh/git-gh-helpers.ts +1 -1
  323. package/scripts/gh/git-gh-issue-list.ts +15 -19
  324. package/scripts/gh/git-gh-issue-read.ts +43 -60
  325. package/scripts/gh/git-gh-issue-rest.ts +14 -15
  326. package/scripts/gh/git-gh-issue-write.ts +52 -40
  327. package/scripts/gh/git-gh-issue.ts +23 -24
  328. package/scripts/gh/git-gh-pr-auto-merge.ts +3 -3
  329. package/scripts/gh/git-gh-pr-read.ts +46 -50
  330. package/scripts/gh/git-gh-pr-rest.ts +23 -26
  331. package/scripts/gh/git-gh-pr-review.ts +2 -2
  332. package/scripts/gh/git-gh-pr-rollup.ts +4 -4
  333. package/scripts/gh/git-gh-pr-snapshot.ts +8 -10
  334. package/scripts/gh/git-gh-pr.ts +16 -16
  335. package/scripts/gh/git-gh-reachability.ts +11 -12
  336. package/scripts/gh/git-gh-repo.ts +2 -3
  337. package/scripts/gh/git-gh-rest-state.ts +1 -1
  338. package/scripts/gh/git-pr-ai-review.ts +6 -5
  339. package/scripts/gh/git-pr-checks-eval.ts +28 -30
  340. package/scripts/gh/git-pr-checks-parse.ts +14 -16
  341. package/scripts/gh/git-pr-checks-watch.ts +17 -17
  342. package/scripts/gh/git-pr-checks.ts +20 -23
  343. package/scripts/gh/git-pr-coderabbit.ts +8 -6
  344. package/scripts/gh/git-pr-confirmation.ts +5 -5
  345. package/scripts/gh/git-pr-managed-config.ts +7 -9
  346. package/scripts/gh/git-pr-messages.ts +1 -1
  347. package/scripts/gh/git-pr.ts +15 -16
  348. package/scripts/gh/github-issue-url.ts +8 -9
  349. package/scripts/git/base-tree.ts +66 -0
  350. package/scripts/git/change-base.ts +42 -2
  351. package/scripts/git/changed-file-scope.ts +5 -5
  352. package/scripts/git/changed-paths.ts +1 -2
  353. package/scripts/git/constants.ts +2 -2
  354. package/scripts/git/git-branch.ts +1 -1
  355. package/scripts/git/git-closes-keyword.ts +3 -3
  356. package/scripts/git/git-command.ts +53 -41
  357. package/scripts/git/git-common-directory.ts +3 -3
  358. package/scripts/git/git-diff-reads.ts +14 -15
  359. package/scripts/git/git-error.ts +1 -1
  360. package/scripts/git/git-execa-error.ts +1 -2
  361. package/scripts/git/git-issue.ts +10 -8
  362. package/scripts/git/git-location-environment.ts +3 -4
  363. package/scripts/git/git-ls-remote.ts +6 -8
  364. package/scripts/git/git-pre-push-hook.ts +11 -10
  365. package/scripts/git/git-preflight.ts +5 -5
  366. package/scripts/git/git-push-transport.ts +2 -2
  367. package/scripts/git/git-remote-branch.ts +1 -2
  368. package/scripts/git/git-schemas.ts +16 -23
  369. package/scripts/git/git-spawn-sync.ts +1 -1
  370. package/scripts/git/git-spawn.ts +37 -14
  371. package/scripts/git/git-ssh-keepalive.ts +12 -5
  372. package/scripts/git/git-staging.ts +7 -8
  373. package/scripts/git/git-workflow.ts +1 -1
  374. package/scripts/git/git-worktree.ts +35 -27
  375. package/scripts/git/gone-branch.ts +2 -2
  376. package/scripts/git/listing-cutoff.ts +1 -2
  377. package/scripts/git/lockfile-sync.ts +65 -0
  378. package/scripts/git/main-merge-guard.ts +1 -2
  379. package/scripts/git/main-merge.ts +7 -7
  380. package/scripts/git/main-sync.ts +23 -8
  381. package/scripts/git/parse-json-array.ts +18 -23
  382. package/scripts/git/repository-lock.ts +34 -2
  383. package/scripts/git/stash/git-stash.ts +4 -5
  384. package/scripts/git/stash/stash-orphans.ts +5 -3
  385. package/scripts/git/stash/stash-pop-args.ts +1 -1
  386. package/scripts/git/stash/stash-pop-cli.ts +3 -3
  387. package/scripts/git/stash/stash-sweep-lock.ts +2 -2
  388. package/scripts/hooks/batch-guard.ts +11 -13
  389. package/scripts/hooks/check-commit-message.ts +12 -9
  390. package/scripts/hooks/codex-hook-adapter.ts +16 -10
  391. package/scripts/hooks/edited-cspell.ts +3 -4
  392. package/scripts/hooks/format-edited-cli.ts +1 -1
  393. package/scripts/hooks/format-edited-file.ts +11 -17
  394. package/scripts/hooks/format-rewrite-notice.ts +19 -17
  395. package/scripts/hooks/pre-push-unit.ts +10 -14
  396. package/scripts/hooks/pretool-guard-cli.ts +4 -4
  397. package/scripts/hooks/pretool-guard.ts +93 -18
  398. package/scripts/hooks/reserved-run.ts +2 -2
  399. package/scripts/hooks/step-zero-notice.ts +1 -1
  400. package/scripts/hooks/stop-guard.ts +21 -10
  401. package/scripts/init/basic-path-migration.ts +2 -2
  402. package/scripts/init/claude-kit-only-settings.ts +2 -2
  403. package/scripts/init/claude-plugin-config.ts +2 -2
  404. package/scripts/init/distributed-paths.ts +4 -4
  405. package/scripts/init/hook-command-rewrite.ts +3 -3
  406. package/scripts/init/hook-launch.ts +5 -5
  407. package/scripts/init/init-actions.ts +13 -0
  408. package/scripts/init/init-ai-copy.ts +11 -11
  409. package/scripts/init/init-bootstrap.ts +3 -3
  410. package/scripts/init/init-copy-content.ts +4 -4
  411. package/scripts/init/init-logic-json-merge.ts +7 -9
  412. package/scripts/init/init-logic-migrate.ts +1 -1
  413. package/scripts/init/init-logic-templates.ts +1 -1
  414. package/scripts/init/init-logic-workspace.ts +4 -4
  415. package/scripts/init/init-logic-yaml-merge.ts +2 -2
  416. package/scripts/init/init-logic.ts +19 -22
  417. package/scripts/init/init.ts +12 -8
  418. package/scripts/init/kit-base-preset.ts +1 -1
  419. package/scripts/init/kit-setup-state.ts +1 -1
  420. package/scripts/init/kit-written-paths.ts +1 -2
  421. package/scripts/init/package-manager-pin.ts +35 -0
  422. package/scripts/init/project-profile-cli.ts +1 -1
  423. package/scripts/init/project-profile.ts +3 -3
  424. package/scripts/init/session-language-environment.ts +1 -1
  425. package/scripts/init/start-exec.ts +3 -4
  426. package/scripts/init/start-plan.ts +2 -2
  427. package/scripts/init/start-setup-pr.ts +3 -2
  428. package/scripts/init/start-steps.ts +2 -2
  429. package/scripts/init/tsconfig-preset-migration.ts +1 -1
  430. package/scripts/init/vscode-tasks.ts +62 -0
  431. package/scripts/issue/baseline-measure.ts +2 -2
  432. package/scripts/issue/behavior-change-lint.ts +2 -2
  433. package/scripts/issue/defect-rate-cli.ts +6 -7
  434. package/scripts/issue/defect-rate.ts +37 -27
  435. package/scripts/issue/git-next-issues.ts +9 -8
  436. package/scripts/issue/issue-auto-ok.ts +49 -11
  437. package/scripts/issue/issue-backlinks-cli.ts +5 -5
  438. package/scripts/issue/issue-backlinks.ts +6 -6
  439. package/scripts/issue/issue-cite-cli.ts +20 -18
  440. package/scripts/issue/issue-cite.ts +18 -8
  441. package/scripts/issue/issue-closing-pr.ts +4 -4
  442. package/scripts/issue/issue-comment-cli.ts +6 -4
  443. package/scripts/issue/issue-file-cli.ts +75 -15
  444. package/scripts/issue/issue-file-fold.ts +155 -0
  445. package/scripts/issue/issue-file.ts +14 -6
  446. package/scripts/issue/issue-fold-cli.ts +14 -9
  447. package/scripts/issue/issue-fold.ts +23 -8
  448. package/scripts/issue/issue-labels.ts +46 -37
  449. package/scripts/issue/issue-lint-cli.ts +2 -2
  450. package/scripts/issue/issue-lint.ts +1 -1
  451. package/scripts/issue/issue-merged.ts +10 -4
  452. package/scripts/issue/issue-number-shape.ts +27 -0
  453. package/scripts/issue/issue-rank.ts +1 -1
  454. package/scripts/issue/issue-read-cli.ts +4 -5
  455. package/scripts/issue/issue-read.ts +4 -5
  456. package/scripts/issue/issue-reference.ts +5 -5
  457. package/scripts/issue/issue-release-cli.ts +165 -0
  458. package/scripts/issue/issue-release.ts +58 -0
  459. package/scripts/issue/issue-report-failures.ts +7 -4
  460. package/scripts/issue/issue-scout-cli.ts +11 -11
  461. package/scripts/issue/issue-scout-closed.ts +4 -4
  462. package/scripts/issue/issue-scout.ts +4 -5
  463. package/scripts/issue/issue-state-cli.ts +17 -10
  464. package/scripts/issue/issue-state.ts +12 -13
  465. package/scripts/issue/issue-wip.ts +7 -7
  466. package/scripts/issue/markdown-section.ts +3 -3
  467. package/scripts/issue/measure-rerun-cli.ts +10 -9
  468. package/scripts/issue/reproduction-measure.ts +3 -3
  469. package/scripts/issue/session-cite.ts +56 -0
  470. package/scripts/josh/agent-session-environment.ts +10 -8
  471. package/scripts/josh/cli-body.ts +2 -2
  472. package/scripts/josh/command-suggest.ts +1 -1
  473. package/scripts/josh/file-map-stamp.ts +20 -23
  474. package/scripts/josh/hook-decision.ts +24 -28
  475. package/scripts/josh/josh-command-map.ts +4 -1
  476. package/scripts/josh/josh-command-types.ts +32 -34
  477. package/scripts/josh/josh-commands-ai.ts +6 -6
  478. package/scripts/josh/josh-commands-backlog.ts +15 -3
  479. package/scripts/josh/josh-commands-clone.ts +1 -1
  480. package/scripts/josh/josh-commands-development.ts +33 -4
  481. package/scripts/josh/josh-commands-document.ts +3 -3
  482. package/scripts/josh/josh-commands-guard.ts +3 -3
  483. package/scripts/josh/josh-commands-issue.ts +11 -5
  484. package/scripts/josh/josh-commands-lane.ts +16 -5
  485. package/scripts/josh/josh-commands-lint.ts +1 -1
  486. package/scripts/josh/josh-commands-maintenance.ts +2 -2
  487. package/scripts/josh/josh-commands-run.ts +20 -6
  488. package/scripts/josh/josh-commands-split.ts +1 -2
  489. package/scripts/josh/josh-commands-versioning.ts +5 -5
  490. package/scripts/josh/josh-commands-workflow.ts +30 -22
  491. package/scripts/josh/josh-environment-file.ts +1 -1
  492. package/scripts/josh/josh-in-process.ts +3 -3
  493. package/scripts/josh/josh-logic.ts +10 -10
  494. package/scripts/josh/josh-run.ts +3 -3
  495. package/scripts/josh/josh-verdict.ts +9 -12
  496. package/scripts/josh/josh.ts +3 -3
  497. package/scripts/josh/kit-only.ts +2 -2
  498. package/scripts/josh/path-decision.ts +3 -3
  499. package/scripts/josh/platform-temporary.ts +7 -5
  500. package/scripts/josh/process-identity.ts +24 -6
  501. package/scripts/josh/process-owner.ts +4 -4
  502. package/scripts/josh/session-language-cli.ts +53 -11
  503. package/scripts/josh/session-language.ts +1 -1
  504. package/scripts/josh/stamp-file.ts +48 -29
  505. package/scripts/josh/stamp-record.ts +57 -0
  506. package/scripts/lane/lane-await.ts +35 -11
  507. package/scripts/lane/lane-cache.ts +6 -6
  508. package/scripts/lane/lane-capacity.ts +32 -17
  509. package/scripts/lane/lane-child-invocation.ts +17 -15
  510. package/scripts/lane/lane-child-marker.ts +12 -12
  511. package/scripts/lane/lane-cli.ts +20 -13
  512. package/scripts/lane/lane-close.ts +6 -6
  513. package/scripts/lane/lane-dispatch.ts +61 -72
  514. package/scripts/lane/lane-environment.ts +5 -5
  515. package/scripts/lane/lane-guard-policy.ts +7 -21
  516. package/scripts/lane/lane-handoff.ts +2 -2
  517. package/scripts/lane/lane-install.ts +9 -10
  518. package/scripts/lane/lane-launch-cli.ts +13 -8
  519. package/scripts/lane/lane-ledger.ts +73 -11
  520. package/scripts/lane/lane-leftover.ts +44 -30
  521. package/scripts/lane/lane-limit-cli.ts +160 -0
  522. package/scripts/lane/lane-limit-override.ts +74 -0
  523. package/scripts/lane/lane-load.ts +19 -68
  524. package/scripts/lane/lane-measure-cli.ts +15 -25
  525. package/scripts/lane/lane-occupancy.ts +6 -4
  526. package/scripts/lane/lane-open.ts +25 -24
  527. package/scripts/lane/lane-output.ts +4 -3
  528. package/scripts/lane/lane-paths.ts +22 -8
  529. package/scripts/lane/lane-phase.ts +33 -0
  530. package/scripts/lane/lane-reap.ts +2 -2
  531. package/scripts/lane/lane-registry.ts +27 -15
  532. package/scripts/lane/lane-relaunch.ts +9 -9
  533. package/scripts/lane/lane-report.ts +5 -4
  534. package/scripts/lane/lane-resume.ts +2 -2
  535. package/scripts/lane/lane-sampler.ts +55 -0
  536. package/scripts/lane/lane-seed.ts +7 -7
  537. package/scripts/lane/lane-stage-stats.ts +135 -0
  538. package/scripts/lane/lane-start-point.ts +8 -11
  539. package/scripts/lane/lane-stats.ts +60 -15
  540. package/scripts/lane/lane-stray.ts +140 -0
  541. package/scripts/lane/lane-vacant.ts +1 -1
  542. package/scripts/lane/openai-lane-supervisor-decision.ts +3 -7
  543. package/scripts/lane/openai-lane-supervisor-owner.ts +3 -9
  544. package/scripts/lane/openai-review-broker.ts +2 -4
  545. package/scripts/lib/ancestor-directories.ts +1 -1
  546. package/scripts/lib/bounded-pool.ts +2 -2
  547. package/scripts/lib/buffered-process.ts +4 -6
  548. package/scripts/lib/caddy-logs.sh +0 -0
  549. package/scripts/lib/cli-flags.ts +39 -6
  550. package/scripts/lib/error-message.ts +1 -1
  551. package/scripts/lib/json-value.ts +17 -3
  552. package/scripts/lib/poll.ts +1 -1
  553. package/scripts/lib/status-icons.ts +3 -3
  554. package/scripts/lib/timed-fetch.ts +1 -1
  555. package/scripts/lib/timeouts.ts +1 -1
  556. package/scripts/lines/bytes-command.ts +3 -4
  557. package/scripts/lines/effective-limit.ts +25 -12
  558. package/scripts/lines/line-budget.ts +47 -55
  559. package/scripts/lines/line-targets.ts +6 -2
  560. package/scripts/lines/lines-command.ts +3 -3
  561. package/scripts/lint/cspell-cached.ts +1 -1
  562. package/scripts/lint/cycle-recheck.ts +122 -0
  563. package/scripts/lint/format.ts +1 -2
  564. package/scripts/lint/lint-parallel.ts +34 -19
  565. package/scripts/lint/lint-related-scope.ts +25 -15
  566. package/scripts/lint/lint-related.ts +6 -7
  567. package/scripts/managed-marker/index.ts +1 -1
  568. package/scripts/managed-marker/managed-marker-logic.ts +1 -2
  569. package/scripts/notify/git-notify.ts +9 -7
  570. package/scripts/notify/telegram-notify.ts +53 -37
  571. package/scripts/notify/telegram-test-logic.ts +3 -3
  572. package/scripts/notify/telegram-test.ts +45 -30
  573. package/scripts/observations/observation-ledger-home.ts +14 -8
  574. package/scripts/observations/observation-ledger-line.ts +3 -2
  575. package/scripts/observations/observation-ledger-migrate.ts +3 -4
  576. package/scripts/observations/observation-ledger-prepare.ts +4 -4
  577. package/scripts/observations/observation-ledger.ts +14 -15
  578. package/scripts/observations/observation-record-cli.ts +83 -0
  579. package/scripts/observations/observation-record.ts +91 -0
  580. package/scripts/observations/observations-flush-cli.ts +1 -1
  581. package/scripts/observations/observations-flush-landing.ts +7 -6
  582. package/scripts/observations/observations-flush.ts +26 -27
  583. package/scripts/overrides/overrides-check.ts +18 -8
  584. package/scripts/package/package-scout-cli.ts +1 -1
  585. package/scripts/package/package-scout-format.ts +2 -2
  586. package/scripts/package/package-scout.ts +1 -1
  587. package/scripts/propagate/propagate-git-failure.ts +1 -1
  588. package/scripts/propagate/propagate-git.ts +7 -7
  589. package/scripts/propagate/propagate-plan.ts +2 -2
  590. package/scripts/propagate/propagate-publish.ts +2 -3
  591. package/scripts/propagate/propagate-run.ts +5 -7
  592. package/scripts/propagate/propagate-select.ts +1 -1
  593. package/scripts/propagate/propagate-steps.ts +19 -20
  594. package/scripts/propagate/propagate-targets.ts +6 -10
  595. package/scripts/propagate/propagate.ts +5 -5
  596. package/scripts/refactor/import-graph.ts +1 -1
  597. package/scripts/refactor/refactor-lint.ts +4 -10
  598. package/scripts/refactor/refactor-scan-cli.ts +1 -1
  599. package/scripts/refactor/refactor-scan.ts +2 -2
  600. package/scripts/refactor/refactor-targets.ts +1 -1
  601. package/scripts/release/github-release-cli.ts +25 -3
  602. package/scripts/release/github-release-environment.ts +1 -0
  603. package/scripts/release/github-release-runs.ts +115 -0
  604. package/scripts/release/github-release.ts +13 -67
  605. package/scripts/release/release-cli.ts +21 -4
  606. package/scripts/release/release-history.ts +6 -10
  607. package/scripts/release/release-plan.ts +3 -3
  608. package/scripts/release/release-progress.ts +3 -4
  609. package/scripts/release/release-publish.ts +8 -9
  610. package/scripts/release/release-scope-cli.ts +8 -10
  611. package/scripts/release/release-tag.ts +6 -8
  612. package/scripts/release/release-worktree.ts +5 -5
  613. package/scripts/repo/auto-merge-setting-logic.ts +7 -9
  614. package/scripts/repo/auto-merge-setting.ts +1 -1
  615. package/scripts/repo/repo-setting.ts +4 -4
  616. package/scripts/repo/repository-labels.ts +1 -1
  617. package/scripts/repo/required-checks-logic.ts +3 -3
  618. package/scripts/repo/required-checks-report.ts +1 -1
  619. package/scripts/repo/ruleset-check-cli.ts +1 -1
  620. package/scripts/repo/ruleset-check.ts +1 -1
  621. package/scripts/report/report-format-reference.ts +26 -0
  622. package/scripts/report/report-lint-cli.ts +2 -2
  623. package/scripts/report/report-lint.ts +16 -2
  624. package/scripts/review/disposition-cli.ts +2 -2
  625. package/scripts/review/disposition-logic.ts +1 -1
  626. package/scripts/review/live-evidence.ts +9 -6
  627. package/scripts/review/review-attest-cli.ts +1 -2
  628. package/scripts/review/review-attest.ts +3 -3
  629. package/scripts/review/review-brief-cli.ts +19 -20
  630. package/scripts/review/review-brief.ts +29 -32
  631. package/scripts/review/review-checkout.ts +2 -2
  632. package/scripts/review/review-diff-parts.ts +3 -5
  633. package/scripts/review/review-diff-write.ts +1 -1
  634. package/scripts/review/review-finding-ledger.ts +7 -4
  635. package/scripts/review/review-findings-cli.ts +2 -2
  636. package/scripts/review/review-level.ts +4 -7
  637. package/scripts/review/review-record-cli.ts +73 -16
  638. package/scripts/review/review-record.ts +5 -4
  639. package/scripts/review/review-round2-cli.ts +2 -3
  640. package/scripts/review/review-round2.ts +13 -21
  641. package/scripts/review/review-stamps.ts +9 -71
  642. package/scripts/review/review-tree.ts +4 -5
  643. package/scripts/rules/bash-triggers.ts +4 -4
  644. package/scripts/rules/decision-oracle-batch.ts +5 -6
  645. package/scripts/rules/decision-oracle-stage.ts +1 -1
  646. package/scripts/rules/decision-oracle-tokens.ts +1 -2
  647. package/scripts/rules/decision-oracle.ts +2 -2
  648. package/scripts/rules/delivered-rules.ts +61 -406
  649. package/scripts/rules/destructive-command.ts +2 -2
  650. package/scripts/rules/direct-filing.ts +4 -4
  651. package/scripts/rules/direct-pr-create.ts +2 -2
  652. package/scripts/rules/early-heartbeat.ts +15 -19
  653. package/scripts/rules/file-body.ts +5 -6
  654. package/scripts/rules/filing-cap.ts +6 -7
  655. package/scripts/rules/filing-offer.ts +1 -1
  656. package/scripts/rules/firing-point.ts +6 -6
  657. package/scripts/rules/gh-api.ts +2 -2
  658. package/scripts/rules/git-argv.ts +3 -3
  659. package/scripts/rules/git-force.ts +2 -2
  660. package/scripts/rules/hook-context-line.ts +31 -0
  661. package/scripts/rules/implementation-cut-verdict.ts +5 -8
  662. package/scripts/rules/implementation-cut.ts +35 -32
  663. package/scripts/rules/index-guard.ts +2 -2
  664. package/scripts/rules/issue-citation.ts +7 -7
  665. package/scripts/rules/issue-comments.ts +21 -13
  666. package/scripts/rules/josh-git-bare.ts +2 -2
  667. package/scripts/rules/lane-background.ts +14 -16
  668. package/scripts/rules/lane-carry-conflict.ts +41 -5
  669. package/scripts/rules/lane-interactive-ask.ts +6 -9
  670. package/scripts/rules/lane-park.ts +12 -16
  671. package/scripts/rules/lane-split-park.ts +1 -2
  672. package/scripts/rules/lane-switch-main.ts +10 -13
  673. package/scripts/rules/last-prompt.ts +1 -1
  674. package/scripts/rules/oracle-consulted.ts +5 -5
  675. package/scripts/rules/oracle-firing.ts +6 -9
  676. package/scripts/rules/oracle-list-cli.ts +2 -2
  677. package/scripts/rules/permission-guards.ts +2 -2
  678. package/scripts/rules/piped-verification.ts +79 -9
  679. package/scripts/rules/poll-loop.ts +9 -11
  680. package/scripts/rules/pre-gate-cut.ts +33 -30
  681. package/scripts/rules/prior-comment-read.ts +3 -3
  682. package/scripts/rules/protected-files.ts +25 -13
  683. package/scripts/rules/raw-field-body.ts +3 -4
  684. package/scripts/rules/reply-language.ts +1 -1
  685. package/scripts/rules/rule-body-guard.ts +10 -12
  686. package/scripts/rules/rule-guard.ts +1 -1
  687. package/scripts/rules/rule-list.ts +51 -0
  688. package/scripts/rules/rule-registry-rows.ts +282 -0
  689. package/scripts/rules/rule-registry.ts +109 -0
  690. package/scripts/rules/rule-value-cache.ts +2 -2
  691. package/scripts/rules/rule-value-cli.ts +6 -4
  692. package/scripts/rules/rule-value.ts +61 -22
  693. package/scripts/rules/run-tail-rule.ts +11 -15
  694. package/scripts/rules/shell-body-trigger.ts +7 -7
  695. package/scripts/rules/shell-segments.ts +2 -4
  696. package/scripts/rules/stop-rules.ts +54 -41
  697. package/scripts/rules/tail-commands.ts +1 -1
  698. package/scripts/rules/test-declared-commit.ts +2 -2
  699. package/scripts/rules/third-party-write.ts +3 -3
  700. package/scripts/rules/worktree-guard.ts +12 -5
  701. package/scripts/run/add/run-add-cli.ts +165 -0
  702. package/scripts/run/add/run-add.ts +124 -0
  703. package/scripts/run/board/run-board-cli.ts +207 -0
  704. package/scripts/run/board/run-board-closed.ts +95 -0
  705. package/scripts/run/board/run-board-every.ts +60 -0
  706. package/scripts/run/board/run-board-fetch.ts +38 -0
  707. package/scripts/run/board/run-board-fit.ts +219 -0
  708. package/scripts/run/board/run-board-github.ts +162 -0
  709. package/scripts/run/board/run-board-header.ts +275 -0
  710. package/scripts/run/board/run-board-kind.ts +35 -0
  711. package/scripts/run/board/run-board-labels.ts +299 -0
  712. package/scripts/run/board/run-board-layout.ts +222 -0
  713. package/scripts/run/board/run-board-link.ts +31 -0
  714. package/scripts/run/board/run-board-machine.ts +198 -0
  715. package/scripts/run/board/run-board-notes.ts +103 -0
  716. package/scripts/run/board/run-board-phase.ts +136 -0
  717. package/scripts/run/board/run-board-plan.ts +129 -0
  718. package/scripts/run/board/run-board-read.ts +62 -0
  719. package/scripts/run/board/run-board-render-legend.ts +82 -0
  720. package/scripts/run/board/run-board-render-notes.ts +77 -0
  721. package/scripts/run/board/run-board-render-slot.ts +50 -0
  722. package/scripts/run/board/run-board-render.ts +407 -0
  723. package/scripts/run/board/run-board-scope.ts +17 -0
  724. package/scripts/run/board/run-board-screen.ts +43 -0
  725. package/scripts/run/board/run-board-span.ts +66 -0
  726. package/scripts/run/board/run-board-spin.ts +115 -0
  727. package/scripts/run/board/run-board-state.ts +113 -0
  728. package/scripts/run/board/run-board-status.ts +279 -0
  729. package/scripts/run/board/run-board-tick.ts +210 -0
  730. package/scripts/run/board/run-board-track.ts +22 -0
  731. package/scripts/run/board/run-board-usage-read.ts +255 -0
  732. package/scripts/run/board/run-board-usage-rusage.ts +124 -0
  733. package/scripts/run/board/run-board-usage-text.ts +80 -0
  734. package/scripts/run/board/run-board-usage.ts +165 -0
  735. package/scripts/run/carry/run-carry-added.ts +62 -0
  736. package/scripts/run/carry/run-carry-args.ts +25 -22
  737. package/scripts/run/carry/run-carry-change.ts +1 -1
  738. package/scripts/run/carry/run-carry-cli.ts +71 -48
  739. package/scripts/run/carry/run-carry-conversation.ts +6 -7
  740. package/scripts/run/carry/run-carry-ended.ts +85 -0
  741. package/scripts/run/carry/run-carry-reclaim.ts +1 -1
  742. package/scripts/run/carry/run-carry-stash.ts +3 -2
  743. package/scripts/run/carry/run-carry-streak.ts +8 -10
  744. package/scripts/run/carry/run-carry.ts +85 -89
  745. package/scripts/run/cut/run-cut-args.ts +7 -7
  746. package/scripts/run/cut/run-cut-cli.ts +68 -37
  747. package/scripts/run/cut/run-cut-handoff.ts +15 -15
  748. package/scripts/run/cut/run-cut-report.ts +37 -16
  749. package/scripts/run/cut/run-cut.ts +63 -77
  750. package/scripts/run/detached-launch.ts +35 -50
  751. package/scripts/run/entry/run-entry-cli.ts +48 -27
  752. package/scripts/run/entry/run-entry-stop.ts +7 -9
  753. package/scripts/run/entry/run-entry.ts +5 -6
  754. package/scripts/run/event/run-event-cli.ts +5 -6
  755. package/scripts/run/event/run-event-filed.ts +69 -0
  756. package/scripts/run/event/run-event-follow.ts +3 -3
  757. package/scripts/run/event/run-event-plan.ts +38 -0
  758. package/scripts/run/event/run-event-render.ts +10 -4
  759. package/scripts/run/event/run-event-scope.ts +47 -18
  760. package/scripts/run/event/run-event-stream-emit.ts +64 -11
  761. package/scripts/run/event/run-event-stream.ts +172 -43
  762. package/scripts/run/event/run-event-watch.ts +4 -6
  763. package/scripts/run/hold/run-hold-cli.ts +65 -62
  764. package/scripts/run/hold/run-hold.ts +88 -46
  765. package/scripts/run/merge/run-merge-cli.ts +116 -77
  766. package/scripts/run/merge/run-merge-collect.ts +107 -0
  767. package/scripts/run/merge/run-merge-steps.ts +112 -65
  768. package/scripts/run/merge/run-merge-token.ts +30 -0
  769. package/scripts/run/merge/run-merge.ts +31 -22
  770. package/scripts/run/progress/run-progress-args.ts +4 -4
  771. package/scripts/run/progress/run-progress-cli.ts +39 -50
  772. package/scripts/run/progress/run-progress-clock.ts +25 -32
  773. package/scripts/run/progress/run-progress-config.ts +6 -9
  774. package/scripts/run/progress/run-progress-read.ts +9 -10
  775. package/scripts/run/progress/run-progress.ts +48 -53
  776. package/scripts/run/review/run-review-cli.ts +6 -6
  777. package/scripts/run/review/run-review-steps.ts +6 -6
  778. package/scripts/run/review/run-review.ts +9 -11
  779. package/scripts/run/run-cli-fault.ts +29 -0
  780. package/scripts/run/run-ending-cli.ts +3 -3
  781. package/scripts/run/run-ending.ts +10 -14
  782. package/scripts/run/run-halfrun-resume.ts +6 -8
  783. package/scripts/run/run-headless.ts +25 -12
  784. package/scripts/run/run-invocation.ts +17 -22
  785. package/scripts/run/run-label.ts +7 -5
  786. package/scripts/run/run-liveness-cli.ts +19 -6
  787. package/scripts/run/run-liveness.ts +27 -38
  788. package/scripts/run/run-next-cli.ts +5 -9
  789. package/scripts/run/run-next.ts +4 -5
  790. package/scripts/run/run-parent-cut-hook.ts +10 -12
  791. package/scripts/run/run-preflight.ts +25 -29
  792. package/scripts/run/run-prep-cli.ts +17 -13
  793. package/scripts/run/run-prep-locate.ts +4 -5
  794. package/scripts/run/run-prep.ts +7 -10
  795. package/scripts/run/run-prrun-resume.ts +2 -2
  796. package/scripts/run/run-report-cli.ts +4 -4
  797. package/scripts/run/run-report.ts +9 -14
  798. package/scripts/run/run-retrospective.ts +3 -3
  799. package/scripts/run/run-section.ts +42 -0
  800. package/scripts/run/run-stage-read.ts +2 -2
  801. package/scripts/run/run-stage.ts +6 -4
  802. package/scripts/run/run-status-cli.ts +7 -5
  803. package/scripts/run/run-status.ts +6 -7
  804. package/scripts/run/run-step-cli.ts +13 -15
  805. package/scripts/run/run-step.ts +62 -48
  806. package/scripts/run/run-stop-notify.ts +7 -8
  807. package/scripts/run/run-stranded-cli.ts +1 -1
  808. package/scripts/run/run-stranded-detect.ts +4 -4
  809. package/scripts/run/run-stranded.ts +11 -13
  810. package/scripts/run/run-tail-cli.ts +15 -16
  811. package/scripts/run/run-tail.ts +14 -33
  812. package/scripts/run/run-watcher-guard-cli.ts +3 -4
  813. package/scripts/run/run-watcher-guard.ts +11 -12
  814. package/scripts/run/run-watcher-hook.ts +6 -12
  815. package/scripts/run/ship/run-ship-cli.ts +67 -33
  816. package/scripts/run/ship/run-ship-detach.ts +15 -13
  817. package/scripts/run/ship/run-ship-lock.ts +1 -1
  818. package/scripts/run/ship/run-ship-next.ts +7 -8
  819. package/scripts/run/ship/run-ship-pre-detach.ts +27 -11
  820. package/scripts/run/ship/run-ship-preflight.ts +10 -11
  821. package/scripts/run/ship/run-ship-probe.ts +4 -4
  822. package/scripts/run/ship/run-ship-return.ts +1 -1
  823. package/scripts/run/ship/run-ship-review-steps.ts +43 -24
  824. package/scripts/run/ship/run-ship-review.ts +12 -14
  825. package/scripts/run/ship/run-ship-scoped.ts +15 -6
  826. package/scripts/run/ship/run-ship-stage.ts +80 -26
  827. package/scripts/run/ship/run-ship-steps.ts +26 -16
  828. package/scripts/run/ship/run-ship-stop-text.ts +4 -2
  829. package/scripts/run/ship/run-ship-sync.ts +9 -8
  830. package/scripts/run/ship/run-ship.ts +17 -32
  831. package/scripts/run/tidy/run-tidy-cli.ts +9 -5
  832. package/scripts/run/tidy/run-tidy-lanes.ts +18 -7
  833. package/scripts/run/tidy/run-tidy-stashes.ts +8 -8
  834. package/scripts/run/tidy/run-tidy.ts +4 -3
  835. package/scripts/run/wake/run-wake-cli.ts +27 -33
  836. package/scripts/run/wake/run-wake-describe.ts +76 -29
  837. package/scripts/run/wake/run-wake-driver.ts +14 -14
  838. package/scripts/run/wake/run-wake-handoff.ts +6 -6
  839. package/scripts/run/wake/run-wake-loop.ts +13 -21
  840. package/scripts/run/wake/run-wake-restart.ts +4 -5
  841. package/scripts/run/wake/run-wake-session.ts +23 -27
  842. package/scripts/run/wake/run-wake.ts +57 -328
  843. package/scripts/safe-chain/ci-installer-pin.ts +3 -3
  844. package/scripts/safe-chain/pack-hook.ts +2 -2
  845. package/scripts/safe-chain/preinstall-command.ts +2 -2
  846. package/scripts/security/security-audit-logic.ts +1 -1
  847. package/scripts/security/security-audit-provision-logic.ts +1 -1
  848. package/scripts/security/security-audit-provision.ts +2 -2
  849. package/scripts/security/security-audit.ts +5 -5
  850. package/scripts/security/security-updates-logic.ts +2 -3
  851. package/scripts/security/security-updates.ts +2 -4
  852. package/scripts/self-sync-guard/index.ts +1 -1
  853. package/scripts/self-sync-guard/self-sync-guard-logic.ts +2 -2
  854. package/scripts/self-sync-guard/self-sync-refusal.ts +1 -1
  855. package/scripts/sonar/sonar-hotspots-cli.ts +1 -1
  856. package/scripts/sonar/sonar-hotspots.ts +1 -1
  857. package/scripts/sonar/sonar-new-code-cli.ts +1 -1
  858. package/scripts/sonar/sonar-new-code.ts +1 -1
  859. package/scripts/sonar/sonar-project.ts +1 -1
  860. package/scripts/split/split-assess-cli.ts +1 -1
  861. package/scripts/split/split-assess.ts +3 -3
  862. package/scripts/sync/directory-copy-guard.ts +2 -3
  863. package/scripts/sync/managed-config-scope-cli.ts +1 -2
  864. package/scripts/sync/managed-config-scope.ts +4 -4
  865. package/scripts/sync/plugin-skill-directories.ts +2 -2
  866. package/scripts/sync/reconcile-templates.ts +17 -8
  867. package/scripts/sync/removed-skill-manifest.ts +2 -3
  868. package/scripts/sync/sync-ai-files.ts +7 -7
  869. package/scripts/sync/sync-configs.ts +30 -3
  870. package/scripts/sync/sync-hook-safety.ts +2 -2
  871. package/scripts/sync/sync.ts +23 -10
  872. package/scripts/sync/synced-paths.ts +2 -1
  873. package/scripts/sync/workflow-pin-logic.ts +1 -1
  874. package/scripts/test/test-declared-changed.ts +4 -4
  875. package/scripts/test/test-declared-logic.ts +2 -3
  876. package/scripts/test/test-declared-match.ts +25 -4
  877. package/scripts/test/test-declared.ts +26 -9
  878. package/scripts/test/test-red-commit.ts +3 -2
  879. package/scripts/test/test-red-logic.ts +7 -14
  880. package/scripts/test/test-red.ts +12 -47
  881. package/scripts/test/test-related-scope.ts +2 -3
  882. package/scripts/test/test-related.ts +3 -3
  883. package/scripts/test/test-type-logic.ts +1 -2
  884. package/scripts/test/test-unit-guard.ts +13 -17
  885. package/scripts/test/unit-worker-share.ts +7 -8
  886. package/scripts/time-runtime/time-agent-bundles.ts +2 -4
  887. package/scripts/time-runtime/time-background.ts +17 -21
  888. package/scripts/time-runtime/time-batch-guard.ts +67 -342
  889. package/scripts/time-runtime/time-bundle-call.ts +26 -28
  890. package/scripts/time-runtime/time-bundles.ts +11 -12
  891. package/scripts/time-runtime/time-call-identity.ts +1 -1
  892. package/scripts/time-runtime/time-command-key.ts +2 -2
  893. package/scripts/time-runtime/time-density-hook.ts +8 -11
  894. package/scripts/time-runtime/time-density.ts +5 -5
  895. package/scripts/time-runtime/time-followup-stage.ts +7 -7
  896. package/scripts/time-runtime/time-format.ts +5 -5
  897. package/scripts/time-runtime/time-hook-transcript.ts +3 -3
  898. package/scripts/time-runtime/time-instant.ts +1 -1
  899. package/scripts/time-runtime/time-markers.ts +15 -19
  900. package/scripts/time-runtime/time-phase-names.ts +3 -3
  901. package/scripts/time-runtime/time-reported-failure.ts +15 -19
  902. package/scripts/time-runtime/time-round-trips.ts +20 -23
  903. package/scripts/time-runtime/time-shell.ts +8 -8
  904. package/scripts/time-runtime/time-single-check.ts +1 -2
  905. package/scripts/time-runtime/time-spans.ts +35 -40
  906. package/scripts/time-runtime/time-tool-call.ts +15 -16
  907. package/scripts/time-runtime/time-transcript-line.ts +53 -34
  908. package/scripts/time-runtime/time-writes.ts +5 -5
  909. package/scripts/ui/ui-routes-cli.ts +1 -2
  910. package/scripts/ui/ui-routes.ts +1 -2
  911. package/scripts/version/bump-version.ts +1 -1
  912. package/scripts/version/fresh-metadata-cache.ts +2 -2
  913. package/scripts/version/latest-corepack.ts +129 -98
  914. package/scripts/version/latest-lane-guard.ts +1 -1
  915. package/scripts/version/latest-scope-cli.ts +2 -2
  916. package/scripts/version/latest-stamp.ts +1 -2
  917. package/scripts/version/latest-update.ts +2 -2
  918. package/scripts/version/npm-registry.ts +2 -2
  919. package/scripts/version/package-manager-version.ts +18 -0
  920. package/scripts/version/publishable-range-check.ts +2 -1
  921. package/scripts/version/publishable-range.ts +1 -1
  922. package/scripts/version/release-age.ts +11 -11
  923. package/scripts/version/release-hold.ts +2 -2
  924. package/scripts/version/safe-chain-version-update.ts +3 -3
  925. package/scripts/version/version-check-logic.ts +2 -3
  926. package/scripts/version/version-check.ts +1 -2
  927. package/scripts/version/version-command-config.ts +2 -2
  928. package/scripts/version/version-commands.ts +3 -3
  929. package/scripts/version/version-remote.ts +4 -4
  930. package/scripts/version/version-targets.ts +2 -2
  931. package/templates/gitignore +7 -0
  932. package/templates/workflows/github-release.yml +4 -0
  933. package/dist/hooks/chunk-NV2JRZZO.js +0 -6385
  934. package/dist/hooks/chunk-RKUAD3I6.js +0 -90
  935. package/dist/hooks/chunk-WPALEIRX.js +0 -18
  936. package/scripts/backlog/backlog-restructure.ts +0 -152
  937. package/scripts/rules/issue-fold-rule.ts +0 -72
  938. package/scripts/rules/wip-cap.ts +0 -63
  939. package/scripts/run/run-issue-number.ts +0 -19
@@ -1,84 +1,123 @@
1
1
  import {
2
2
  duplicate_read_outcome
3
- } from "./chunk-TQNPDZNH.js";
3
+ } from "./chunk-NZBHG6FW.js";
4
4
  import {
5
5
  ALREADY_DONE_LABEL,
6
6
  CONTEXT_CUT_THRESHOLD,
7
7
  IN_PROGRESS_LABEL,
8
8
  NEEDS_DECISION_LABEL,
9
9
  NEEDS_HUMAN_REVIEW_LABEL,
10
- PORCELAIN_FLAG,
11
- agent_role_profile,
12
10
  bash_triggers,
13
- cli_flags,
14
11
  filing_cap,
15
- git_command,
16
- git_common_directory,
17
12
  git_gh_command,
18
13
  git_gh_issue_read,
19
14
  git_gh_pr_read,
20
- git_spawn_sync,
21
15
  has_any_label,
22
- issue_cite,
23
16
  lane_background,
24
17
  lane_park,
25
- lane_registry,
26
- own_session,
27
- parse_json_object_safe,
18
+ parse_json_object_or_undefined,
28
19
  read_json_listing,
29
- repo_origin,
30
20
  repo_party,
31
- run_carry,
32
21
  run_cut,
33
22
  run_cut_handoff,
34
- run_headless,
23
+ run_event_scope,
24
+ run_event_stream,
25
+ run_event_stream_emit,
35
26
  run_hold,
36
27
  run_tail_rule,
28
+ session_cite,
37
29
  shell_segments,
38
30
  tail_commands
39
- } from "./chunk-NV2JRZZO.js";
31
+ } from "./chunk-MXVQMLDP.js";
40
32
  import {
41
33
  ancestor_directories,
42
34
  find_local_bin_upwards
43
35
  } from "./chunk-JBBMAPC4.js";
36
+ import {
37
+ agent_role_profile,
38
+ agent_session_role,
39
+ git_common_directory,
40
+ issue_cite,
41
+ lane_registry,
42
+ repo_origin,
43
+ run_headless
44
+ } from "./chunk-SYSRHRWU.js";
44
45
  import {
45
46
  error_text
46
47
  } from "./chunk-CDY4NFQG.js";
47
48
  import {
48
49
  batch_outcome
49
- } from "./chunk-RNWHN5BP.js";
50
+ } from "./chunk-DZWZ3DKY.js";
50
51
  import {
51
52
  investigation_refusal
52
- } from "./chunk-F56MFS4B.js";
53
+ } from "./chunk-R6CRQEAL.js";
53
54
  import {
54
55
  investigation_reads
55
- } from "./chunk-5VMKCCC4.js";
56
+ } from "./chunk-76RK6E3H.js";
56
57
  import {
57
58
  lane_guard_policy
58
- } from "./chunk-VC3PZPZO.js";
59
+ } from "./chunk-GG72ANKI.js";
59
60
  import {
60
- hook_decision,
61
- lane_child_marker,
62
- run_issue_number
63
- } from "./chunk-6HJJXRBX.js";
61
+ hook_decision
62
+ } from "./chunk-KPM4YIIX.js";
64
63
  import {
65
64
  cost_attribute,
66
65
  cost_blocks,
66
+ open_issue_schema,
67
+ time_batch_guard,
68
+ time_density_hook,
69
+ time_shell
70
+ } from "./chunk-TLJYAGZK.js";
71
+ import {
72
+ lane_child_marker
73
+ } from "./chunk-Z5TMJIPW.js";
74
+ import {
75
+ PORCELAIN_FLAG,
76
+ cli_flags,
67
77
  cost_transcript,
68
78
  cost_usage,
69
79
  file_reader,
80
+ git_command,
81
+ git_spawn_sync,
82
+ issue_number_shape,
70
83
  json_value,
71
84
  lane_paths,
72
- open_issue_schema,
73
- time_batch_guard,
74
- time_density_hook,
75
- time_shell
76
- } from "./chunk-55HHG5PX.js";
85
+ own_session,
86
+ run_carry
87
+ } from "./chunk-NZN2UFI6.js";
77
88
  import {
78
89
  PLATFORM_TEMP_ROOT,
79
90
  PROBE_TIMEOUT_MS,
91
+ platform_temporary,
80
92
  stamp_file
81
- } from "./chunk-ELCN5LCH.js";
93
+ } from "./chunk-NEC4AT4Z.js";
94
+
95
+ // scripts/run/event/run-event-plan.ts
96
+ var KIND = run_event_stream.EVENT_KIND.PLAN;
97
+ var PLAN_VERB = "planned";
98
+ function plan_text(issue) {
99
+ return `${PLAN_VERB} ${issue_cite.plain(issue)}`;
100
+ }
101
+ function has_plan(events, issue) {
102
+ return run_event_scope.since_own_launch(events, issue).some((event) => event.kind === KIND && run_event_scope.issue_named(event) === issue);
103
+ }
104
+ async function emit_plan(issue) {
105
+ if (has_plan(await run_event_stream_emit.invocation_or_all_events(), issue)) return;
106
+ await run_event_stream_emit.emit(KIND, plan_text(issue));
107
+ }
108
+ var run_event_plan = { emit_plan, plan_text };
109
+
110
+ // scripts/lane/lane-phase.ts
111
+ async function mark_implement(directory = process.cwd(), source = process.env) {
112
+ const issue = lane_child_marker.marked_issue(source);
113
+ if (issue === void 0 || !lane_child_marker.is_child_of(directory, source)) return;
114
+ await run_event_plan.emit_plan(issue);
115
+ await run_event_stream_emit.emit(
116
+ run_event_stream.EVENT_KIND.LANE_PHASE,
117
+ `${issue_cite.plain(issue)} implement`
118
+ );
119
+ }
120
+ var lane_phase = { mark_implement };
82
121
 
83
122
  // scripts/rules/direct-filing.ts
84
123
  var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands-backlog.md` \u2192 "`josh issue:file`". **This rule fires on every occurrence, not once per run.**';
@@ -101,6 +140,8 @@ var INTERVAL_KEY = "JOSH_PROGRESS_INTERVAL_MINUTES";
101
140
  var MS_PER_MINUTE = 6e4;
102
141
  var MS_PER_SECOND = 1e3;
103
142
  var DEFAULT_INTERVAL_MS = DEFAULT_INTERVAL_MINUTES * MS_PER_MINUTE;
143
+ var DISABLED_KEY = "JOSH_PROGRESS";
144
+ var DISABLED_VALUE = "0";
104
145
  var NO_LANES = "none";
105
146
  var NO_CHILD_YET = "no in-progress child yet";
106
147
  var NO_NEXT = "none";
@@ -193,9 +234,14 @@ function minutes_from(raw) {
193
234
  }
194
235
  return minutes;
195
236
  }
237
+ function is_disabled() {
238
+ return process.env[DISABLED_KEY] === DISABLED_VALUE;
239
+ }
196
240
  var run_progress = {
197
241
  DEFAULT_INTERVAL_MINUTES,
198
242
  DEFAULT_INTERVAL_MS,
243
+ DISABLED_KEY,
244
+ DISABLED_VALUE,
199
245
  INTERVAL_KEY,
200
246
  MS_PER_MINUTE,
201
247
  MS_PER_SECOND,
@@ -208,6 +254,7 @@ var run_progress = {
208
254
  format_next_line,
209
255
  format_next_report,
210
256
  format_report,
257
+ is_disabled,
211
258
  is_due,
212
259
  minutes_from,
213
260
  next_state,
@@ -234,11 +281,7 @@ function parse_stamp(raw) {
234
281
  function read_last_line(target) {
235
282
  const raw = stamp_file.read_stamp_text(target);
236
283
  if (raw === void 0) return void 0;
237
- try {
238
- return report_stamp_schema.safeParse(JSON.parse(raw)).data?.line;
239
- } catch {
240
- return void 0;
241
- }
284
+ return json_value.parse_with(raw, report_stamp_schema)?.line;
242
285
  }
243
286
  function read_last_report(target) {
244
287
  const raw = stamp_file.read_stamp_text(target);
@@ -351,8 +394,7 @@ var package_schema = z2.object({
351
394
  function read_declared_minutes(directory) {
352
395
  try {
353
396
  const raw = readFileSync(path.join(directory, PACKAGE_FILE), "utf8");
354
- const parsed = package_schema.safeParse(JSON.parse(raw));
355
- return parsed.success ? parsed.data.josh?.progress_interval_minutes : void 0;
397
+ return json_value.parse_with(raw, package_schema)?.josh?.progress_interval_minutes;
356
398
  } catch {
357
399
  return void 0;
358
400
  }
@@ -437,7 +479,7 @@ function decide(call, run2, can_record) {
437
479
  if (can_record) ARM_RECORD.record(target, lands_ms);
438
480
  return false;
439
481
  }
440
- var EARLY_HEARTBEAT_REASON = "\u26D4 early heartbeat: this call arms a wait timer of its own, and the progress clock is not yours to keep. `pnpm josh run:progress --wait` keeps it for you (`JOSH_PROGRESS_INTERVAL_MINUTES`, or `josh.progress_interval_minutes` in `package.json`, default 20 minutes): start it once in the background and it reports the five labelled lines to the run's event stream on its own, without waking you \u2014 relay nothing, and start it again only after it exits, which it does when newly runnable work arrives. Two timers armed at once is what produced reports 3\u20135 minutes apart on a 15-minute setting (joshuafolkken/kit#1570): a new one was armed on the turn a timer fired and again on the turn a child's completion woke the run, and `--mark` recorded each report without anything refusing the early ones. **An explicit ask is not a heartbeat**: if the person asked for progress now, run `pnpm josh run:progress --once`, which prints the five labelled lines whatever the clock says. If you are waiting on something rather than on a clock, wait on the thing itself \u2014 `pnpm josh followup` waits for CI. The procedure is `.claude/skills/workflow-commands/backlogrun.md` \u2192 \"Progress while the run is quiet\". **This rule fires on every early arm, not once per run**, so reissuing the same sleep will be refused again.";
482
+ var EARLY_HEARTBEAT_REASON = "\u26D4 early heartbeat: this call arms a wait timer of its own, and the progress clock is not yours to keep. `pnpm josh run:progress --wait` keeps it for you (`JOSH_PROGRESS_INTERVAL_MINUTES`, or `josh.progress_interval_minutes` in `package.json`, default 20 minutes): start it once in the background and it reports the five labelled lines to the run's event stream on its own, without waking you \u2014 relay nothing, and start it again only after it exits, which it does when newly runnable work arrives. Two timers armed at once is what produced reports 3\u20135 minutes apart on a 15-minute setting (joshuafolkken/kit#1570): a new one was armed on the turn a timer fired and again on the turn a child's completion woke the run, and `--mark` recorded each report without anything refusing the early ones. **An explicit ask is not a heartbeat**: if the person asked for progress now, run `pnpm josh run:progress --once`, which prints the five labelled lines whatever the clock says \u2014 under `backlogrun`, `pnpm josh run:board --chat` instead. If you are waiting on something rather than on a clock, wait on the thing itself \u2014 `pnpm josh followup` waits for CI. The procedure is `.claude/skills/workflow-commands/backlogrun.md` \u2192 \"Progress while the run is quiet\". **This rule fires on every early arm, not once per run**, so reissuing the same sleep will be refused again.";
441
483
  var PROGRESS_NAMES = /* @__PURE__ */ new Set(["run:progress"]);
442
484
  var WATCH_FLAG = /(?:^|\s)--(?:wait|once)(?=\s|$)/u;
443
485
  function is_progress_watch_segment(segment) {
@@ -931,7 +973,8 @@ function report_empty(cwd, session_id) {
931
973
  }
932
974
  function provider_of(environment) {
933
975
  const resolved = agent_role_profile.resolve_provider(environment);
934
- return resolved.kind === "provider" ? resolved.provider : void 0;
976
+ if (resolved.kind !== "provider" || resolved.is_default === true) return void 0;
977
+ return resolved.provider;
935
978
  }
936
979
  function measurement_of(session) {
937
980
  return {
@@ -1060,12 +1103,9 @@ function cache_path(directory) {
1060
1103
  function cached_verdict(target, now_ms) {
1061
1104
  const raw = stamp_file.read_stamp_text(target);
1062
1105
  if (raw === void 0) return void 0;
1063
- try {
1064
- const { read_at_ms, verdict } = cache_schema.parse(JSON.parse(raw));
1065
- return now_ms - read_at_ms < VERDICT_REUSE_MS ? verdict : void 0;
1066
- } catch {
1067
- return void 0;
1068
- }
1106
+ const cached = json_value.parse_with(raw, cache_schema);
1107
+ if (cached === void 0) return void 0;
1108
+ return now_ms - cached.read_at_ms < VERDICT_REUSE_MS ? cached.verdict : void 0;
1069
1109
  }
1070
1110
  function reused_verdict(read2, directory = process.cwd(), now_ms = Date.now()) {
1071
1111
  const target = cache_path(directory);
@@ -1100,7 +1140,7 @@ function current_state() {
1100
1140
  source: process.env,
1101
1141
  carried: run_cut.carried_cut_sync,
1102
1142
  held_issue: run_cut.held_issue_sync,
1103
- // **Read through the short reuse window** (joshuafolkken/kit#2385). The row now fires per
1143
+ // **Read through the short reuse window**. The row now fires per
1104
1144
  // threshold crossing rather than once per run, so this predicate is a candidate on every edit;
1105
1145
  // the window collapses one turn's burst of edits to a single whole-transcript price.
1106
1146
  verdict: () => implementation_cut_verdict.reused_verdict(() => cost_cli.session_verdict())
@@ -1114,8 +1154,12 @@ function held_run_issue(state) {
1114
1154
  if (lane_paths.lane_issue_of(state.directory, { ...state.source }) !== void 0) return void 0;
1115
1155
  return state.held_issue();
1116
1156
  }
1157
+ function implementing_run_issue(state) {
1158
+ if (agent_session_role.is_reviewer(state.source)) return void 0;
1159
+ return lane_child_issue(state) ?? held_run_issue(state);
1160
+ }
1117
1161
  function uncut_run_issue(state) {
1118
- const issue = lane_child_issue(state) ?? held_run_issue(state);
1162
+ const issue = implementing_run_issue(state);
1119
1163
  if (issue === void 0) return void 0;
1120
1164
  return state.carried()?.issue === issue ? void 0 : issue;
1121
1165
  }
@@ -1129,7 +1173,7 @@ function is_over_threshold_edit(call, state = current_state()) {
1129
1173
  return warrants_the_cut(state);
1130
1174
  }
1131
1175
  var THRESHOLD_TEXT = `${cost_format.format_tokens(CONTEXT_CUT_THRESHOLD)}-token`;
1132
- var IMPLEMENTATION_CUT_REASON = `\u26D4 implementation-phase cut: this checkout is a lane dispatched for this issue, or a run holds it for this issue (joshuafolkken/kit#2760), and its recent-context cost has crossed the shared ${THRESHOLD_TEXT} threshold mid-implementation, so the thinking accumulated so far is now re-read on every later request. Take the cut before this edit. First write a handoff file with the Write tool \u2014 the user\u2019s instruction verbatim, what you have completed, what remains, and what you deliberately did not touch, as ${run_cut_handoff.HANDOFF_FORMAT} (Markdown is refused) \u2014 and pass it as \`--handoff <path>\`, so the fresh process resumes on the original instruction rather than the working tree alone (joshuafolkken/kit#2354); a resume that finds no instruction is refused \`incomplete\` rather than continuing blind. \`pnpm josh run:cut --impl <N> --handoff <path>\` ends this process and relaunches a fresh one that resumes back into implementation (\`resume-impl\`), dropping the accumulated context rather than carrying it \u2014 joshuafolkken/kit#1933 built the boundary and joshuafolkken/kit#2310 measured it firing 0 times because the verdict was only ever read at session entry, where the context has not yet grown. The verdict is \`pnpm josh cost --cut\`'s exactly \u2014 the same per-request billed-input statistic against the same threshold, never a second measurement. Issue \`pnpm josh run:cut --impl <N> --handoff <path>\` now and read the verdict: on \`cut\`, **end the turn immediately** \u2014 in a lane the fresh process owns the run and continues implementing, so it must not be waited for; outside a lane nothing is relaunched, so first send the \`confirmation\` Telegram naming the resume command \`fullrun #<N>\` and keep the hold; on \`not-a-lane\`, \`unready\`, \`busy\`, \`failed\` or \`unknown\`, this process carries the run on and the edit is simply the next call. Never relaunch a second process after \`busy\`. This fired because the dispatch mark names this lane or the run hold names this issue; a person working here carries no mark and holds no run, so sees no refusal, so there is no human-or-child judgement left to make. The procedure is \`.claude/skills/workflow-commands/pre-gate-cut.md\`. Reissue this edit once the cut has answered \u2014 an edit reissued right after this refusal passes, so \`busy\` / \`failed\` cannot wedge the run edit after edit; this fires again on the next threshold crossing rather than once per run.`;
1176
+ var IMPLEMENTATION_CUT_REASON = `\u26D4 implementation-phase cut: this checkout is a lane dispatched for this issue, or a run holds it for this issue (joshuafolkken/kit#2760), and its recent-context cost has crossed the shared ${THRESHOLD_TEXT} threshold mid-implementation, so the thinking accumulated so far is now re-read on every later request. Take the cut before this edit. First write a handoff file at \`${run_cut_handoff.HANDOFF_PATH}\` (ignored, so it is never committed) with the Write tool \u2014 the user\u2019s instruction verbatim, what you have completed, what remains, and what you deliberately did not touch, as ${run_cut_handoff.HANDOFF_FORMAT} (Markdown is refused) \u2014 and pass it as \`--handoff <path>\`, so the fresh process resumes on the original instruction rather than the working tree alone (joshuafolkken/kit#2354); a resume that finds no instruction is refused \`incomplete\` rather than continuing blind. \`pnpm josh run:cut --impl <N> --handoff <path>\` ends this process and relaunches a fresh one that resumes back into implementation (\`resume-impl\`), dropping the accumulated context rather than carrying it \u2014 joshuafolkken/kit#1933 built the boundary and joshuafolkken/kit#2310 measured it firing 0 times because the verdict was only ever read at session entry, where the context has not yet grown. The verdict is \`pnpm josh cost --cut\`'s exactly \u2014 the same per-request billed-input statistic against the same threshold, never a second measurement. Issue \`pnpm josh run:cut --impl <N> --handoff <path>\` now and read the verdict: on \`cut\`, **end the turn immediately** \u2014 in a lane the fresh process owns the run and continues implementing, so it must not be waited for; outside a lane nothing is relaunched, so first send the \`confirmation\` Telegram naming the resume command \`fullrun #<N>\` and keep the hold; on \`not-a-lane\`, \`unready\`, \`busy\`, \`failed\` or \`unknown\`, this process carries the run on and the edit is simply the next call. Never relaunch a second process after \`busy\`. This fired because the dispatch mark names this lane or the run hold names this issue; a person working here carries no mark and holds no run, so sees no refusal, so there is no human-or-child judgement left to make. The procedure is \`.claude/skills/workflow-commands/pre-gate-cut.md\`. Reissue this edit once the cut has answered \u2014 an edit reissued right after this refusal passes, so \`busy\` / \`failed\` cannot wedge the run edit after edit; this fires again on the next threshold crossing rather than once per run.`;
1133
1177
  var REISSUE_WINDOW_MS = 9e4;
1134
1178
  function is_reissued_refusal(now_ms, delivered_at_ms) {
1135
1179
  return now_ms - delivered_at_ms < REISSUE_WINDOW_MS;
@@ -1201,7 +1245,6 @@ var gh_api = {
1201
1245
  var ISSUE_PATH_NUMBER = /issues\/(\d+)/gu;
1202
1246
  var ISSUE_VIEW_NUMBER = /\bissue\s+view\s+(?:-{1,2}\S+\s+)*(\d+)/u;
1203
1247
  var ISSUE_READ_ARGUMENTS = /\bissue:read\b([\s\d]*)/u;
1204
- var A_NUMBER = /^\d+$/u;
1205
1248
  var COMMENTS_ENDPOINT = /issues\/\d+\/comments\b/u;
1206
1249
  var VIEW_WITH_COMMENTS = /\bissue\s+view\b[^\n]*--comments\b/u;
1207
1250
  var ISSUE_READ_COMMAND = /\bissue:read\b/u;
@@ -1210,7 +1253,7 @@ function add_issue_read_numbers(command, numbers) {
1210
1253
  if (match === null) return;
1211
1254
  const tokens = (match[1] ?? "").split(/\s+/u);
1212
1255
  for (const token of tokens) {
1213
- if (A_NUMBER.test(token)) numbers.add(Number(token));
1256
+ if (issue_number_shape.ISSUE_NUMBER_PATTERN.test(token)) numbers.add(Number(token));
1214
1257
  }
1215
1258
  }
1216
1259
  function issue_numbers_of(command) {
@@ -1275,13 +1318,15 @@ function is_body_only_issue_read(command) {
1275
1318
  function reads_issue_comments(command) {
1276
1319
  return shell_segments.segments_of(command).some((segment) => fetches_issue_comments(segment));
1277
1320
  }
1278
- var ISSUE_COMMENTS_REASON = "\u26D4 an Issue's comments are part of the Issue: read them before implementing, not only the body. A decision recorded after the body was written lives only in a comment \u2014 a corrected diagnosis (joshuafolkken/kit#1537), a changed default and an added acceptance criterion (joshuafolkken/kit#1520), a scope handed to another Issue (joshuafolkken/kit#1304) \u2014 and nothing in the body says it was superseded, so a body-only reader builds the wrong thing and sees no contradiction. Reissue this read with the comments included: `gh api repos/{owner}/{repo}/issues/<N>/comments --jq '.[] | {user: .user.login, created_at, body}'` beside the body read, or `gh issue view <N> --comments` where GraphQL is reachable. Then the later text is the agreement in force \u2014 a comment supersedes the body it contradicts \u2014 except for two answers that are not the run's to make: work a comment reassigns to another Issue is out of scope and is not implemented, and a comment saying the Issue no longer has a reason to exist stops the run with a `confirmation` Telegram. The procedure is `.claude/skills/workflow-commands/issue-comments.md`. Every body-only read is refused until the comments are read.";
1321
+ var ISSUE_COMMENTS_REASON = "\u26D4 issue comments: pnpm josh issue:read <N>\nRun that in place of this read: <N> is the Issue it names, and an Issue's comments are part of the Issue, so the body and every comment come back together. Without josh, add `gh api repos/{owner}/{repo}/issues/<N>/comments --jq '.[] | {user: .user.login, created_at, body}'` beside the body read, or `gh issue view <N> --comments` where GraphQL is reachable.\nWhy: a decision recorded after the body was written lives only in a comment \u2014 a corrected diagnosis (joshuafolkken/kit#1537), a changed default and an added acceptance criterion (joshuafolkken/kit#1520), a scope handed to another Issue (joshuafolkken/kit#1304) \u2014 and nothing in the body says it was superseded, so a body-only reader builds the wrong thing and sees no contradiction. Then the later text is the agreement in force \u2014 a comment supersedes the body it contradicts \u2014 except for two answers that are not the run's to make: work a comment reassigns to another Issue is out of scope and is not implemented, and a comment saying the Issue no longer has a reason to exist stops the run with a `confirmation` Telegram. The procedure is `.claude/skills/workflow-commands/issue-comments.md`. Every body-only read is refused until the comments are read.";
1279
1322
  var ROW5 = {
1280
1323
  id: "issue-comments",
1281
1324
  is_trigger: bash_triggers.on_bash_command(is_body_only_issue_read),
1282
1325
  reason: ISSUE_COMMENTS_REASON,
1283
1326
  already_satisfied: prior_comment_read.already_read_for_call,
1284
- keeps: bash_triggers.on_bash_command(reads_issue_comments)
1327
+ keeps: bash_triggers.on_bash_command(reads_issue_comments),
1328
+ // The opening the reason carried before it led with the command.
1329
+ former_reasons: ["\u26D4 an Issue's comments are part of the Issue: read them before implementing"]
1285
1330
  };
1286
1331
  var issue_comments = {
1287
1332
  ISSUE_COMMENTS_REASON,
@@ -1289,25 +1334,6 @@ var issue_comments = {
1289
1334
  is_body_only_issue_read
1290
1335
  };
1291
1336
 
1292
- // scripts/rules/issue-fold-rule.ts
1293
- var FOLD_NAMES = /* @__PURE__ */ new Set(["issue:fold"]);
1294
- function runs_the_fold(command) {
1295
- return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, FOLD_NAMES));
1296
- }
1297
- function already_folded(tail, _call) {
1298
- if (filing_cap.prior_filing_count(tail) === 0) return true;
1299
- return tail_commands.prior_bash_commands(tail).some((command) => runs_the_fold(command));
1300
- }
1301
- var ISSUE_FOLD_REASON = '\u26D4 issue fold: this run has filed an Issue before and is filing another with no `pnpm josh issue:fold` between them. Several findings from one session fold into one Issue by default \u2014 the filing-time counterpart to the split assessment, whose two questions it reuses: are the findings separable, and does the whole clearly exceed one verification gate (the size half is `split:assess`, called not recomputed). Reissue after folding: `pnpm josh issue:fold "<title>" "<title>" \u2026` \u2014 `fold` means file one Issue covering them, `separate` means file them apart (separable findings whose combined size clears the split guide), and the default is `fold`. Add `--not-separable` when they are really one deliverable. The procedure is `.claude/skills/workflow-commands/split-assessment.md` \u2192 "The question". Every later filing is refused until the fold is on the transcript.';
1302
- var ROW6 = {
1303
- id: "issue-fold",
1304
- is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
1305
- reason: ISSUE_FOLD_REASON,
1306
- already_satisfied: already_folded,
1307
- keeps: bash_triggers.on_bash_command(runs_the_fold)
1308
- };
1309
- var issue_fold_rule = { ISSUE_FOLD_REASON, ROW: ROW6, already_folded, runs_the_fold };
1310
-
1311
1337
  // scripts/rules/josh-git-bare.ts
1312
1338
  var GIT_COMMAND2 = /* @__PURE__ */ new Set(["git"]);
1313
1339
  var CONFIRM_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes"]);
@@ -1322,7 +1348,7 @@ function is_bare_josh_git(command) {
1322
1348
  return shell_segments.segments_of(command).some((segment) => is_bare_segment(segment));
1323
1349
  }
1324
1350
  var JOSH_GIT_BARE_REASON = '\u26D4 bare `pnpm josh git`: this prompts to confirm the staging, and with no TTY the prompt falls to its cancel branch \u2014 `Checking unstaged files... Found` then `Operation cancelled.` \u2014 so the call throws away the time it takes to fail (about 31 seconds in one measured lane) and you reissue it with `-y` anyway (joshuafolkken/kit#2297). Run it unattended from the start: `pnpm josh git -y "<title> #<N>"`. The recovery form that opens the PR for a push that already landed is `pnpm josh git -y --skip-commit --skip-push`. This rule fires on every occurrence, not once per run.';
1325
- var ROW7 = {
1351
+ var ROW6 = {
1326
1352
  id: "josh-git-bare",
1327
1353
  is_trigger: bash_triggers.on_bash_command(is_bare_josh_git),
1328
1354
  reason: JOSH_GIT_BARE_REASON,
@@ -1330,21 +1356,32 @@ var ROW7 = {
1330
1356
  };
1331
1357
  var josh_git_bare = {
1332
1358
  JOSH_GIT_BARE_REASON,
1333
- ROW: ROW7,
1359
+ ROW: ROW6,
1334
1360
  is_bare_josh_git
1335
1361
  };
1336
1362
 
1337
1363
  // scripts/rules/lane-carry-conflict.ts
1338
1364
  var RUN_BUDGET_COMMANDS = /* @__PURE__ */ new Set(["run:merge", "run:carry"]);
1365
+ var LEADING_ASSIGNMENT = String.raw`[A-Za-z_]\w*=(?:"[^"]*"|'[^']*'|\$\((?:[^()]|\([^()]*\))*\)|[^\s"'])*\s+`;
1366
+ var REDIRECTED_PREFIX = new RegExp(
1367
+ String.raw`^(?:${LEADING_ASSIGNMENT})*${platform_temporary.TEMP_ROOT_KEY}=(?!""|''|\s|$|"?\$\{?[A-Za-z_])`,
1368
+ "u"
1369
+ );
1370
+ var SHARED_STATE_COMMANDS = /* @__PURE__ */ new Set(["run:merge"]);
1371
+ var LEADING_ASSIGNMENTS = new RegExp(`^(?:${LEADING_ASSIGNMENT})*`, "u");
1372
+ function is_budget_segment(segment) {
1373
+ const commands = REDIRECTED_PREFIX.test(segment) ? SHARED_STATE_COMMANDS : RUN_BUDGET_COMMANDS;
1374
+ return shell_segments.is_josh_command(segment.replace(LEADING_ASSIGNMENTS, ""), commands);
1375
+ }
1339
1376
  function invokes_budget_command(command) {
1340
- return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, RUN_BUDGET_COMMANDS));
1377
+ return shell_segments.segments_of(command).some((segment) => is_budget_segment(segment));
1341
1378
  }
1342
1379
  function is_carry_conflict(command, is_lane_child = lane_child_marker.is_child_of(process.cwd())) {
1343
1380
  if (!invokes_budget_command(command)) return false;
1344
1381
  return is_lane_child;
1345
1382
  }
1346
- var LANE_CARRY_CONFLICT_REASON = '\u26D4 lane child carry conflict: this session is a dispatched lane child, and `pnpm josh run:merge` / `pnpm josh run:carry` are the parent backlogrun budget commands \u2014 a child must never run them. The live carry record here belongs to the parent that launched this child, not to a competing run: the parent holds the one run budget for the repository, and every lane shares the common git directory it is keyed on, so a live owner a lane child reads is always its parent (joshuafolkken/kit#2267, measured 2026-09-21 in backlogrun #2252 \u2014 a child read run:merge `busy` as a competitor and parked #2258 unimplemented). Do not run this command, and do not treat it as a conflict: no `needs-decision`, no `confirmation` Telegram, no question to a person \u2014 a dispatched child cannot ask, and there is nothing to decide. Continue implementing this Issue; the parent owns the budget and counts the outcome at the return. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` and `backlogrun-progress.md` \u2192 "The hand-off". This rule fires on every occurrence, not once per run.';
1347
- var ROW8 = {
1383
+ var LANE_CARRY_CONFLICT_REASON = '\u26D4 lane child carry conflict: this session is a dispatched lane child, and `pnpm josh run:merge` / `pnpm josh run:carry` are the parent backlogrun budget commands \u2014 a child must never run them. The live carry record here belongs to the parent that launched this child, not to a competing run: the parent holds the one run budget for the repository, and every lane shares the common git directory it is keyed on, so a live owner a lane child reads is always its parent (joshuafolkken/kit#2267, measured 2026-09-21 in backlogrun #2252 \u2014 a child read run:merge `busy` as a competitor and parked #2258 unimplemented). Do not run this command, and do not treat it as a conflict: no `needs-decision`, no `confirmation` Telegram, no question to a person \u2014 a dispatched child cannot ask, and there is nothing to decide. Continue implementing this Issue; the parent owns the budget and counts the outcome at the return. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` and `backlogrun-progress.md` \u2192 "The hand-off". Live evidence of `run:carry` is taken with a fresh `JOSH_TEMP_ROOT` prefixed to the call, which this rule lets through (joshuafolkken/kit#3458); `run:merge` is refused under any root, because it also syncs main, closes lanes and writes to GitHub. This rule fires on every occurrence, not once per run.';
1384
+ var ROW7 = {
1348
1385
  id: "lane-carry-conflict",
1349
1386
  is_trigger: bash_triggers.on_bash_command(is_carry_conflict),
1350
1387
  reason: LANE_CARRY_CONFLICT_REASON,
@@ -1352,7 +1389,7 @@ var ROW8 = {
1352
1389
  };
1353
1390
  var lane_carry_conflict = {
1354
1391
  LANE_CARRY_CONFLICT_REASON,
1355
- ROW: ROW8,
1392
+ ROW: ROW7,
1356
1393
  invokes_budget_command,
1357
1394
  is_carry_conflict
1358
1395
  };
@@ -1405,13 +1442,13 @@ function is_interactive_ask(call, is_lane_child = lane_child_marker.is_child_of(
1405
1442
  return is_lane_child;
1406
1443
  }
1407
1444
  var LANE_INTERACTIVE_ASK_REASON = '\u26D4 lane child interactive ask: this session is a dispatched lane child and cannot ask a person, so this call is refused. Left to run it would end the turn with nothing on the Issue \u2014 the parent then finds an OPEN Issue with `in-progress` and no question (joshuafolkken/kit#2201; joshuafolkken/kit#2034 placed the same rule one tool-call too late). Do not ask: park the decision instead. Apply `needs-decision` and post a comment carrying the question, the options and whether work was stashed \u2014 `gh api repos/{owner}/{repo}/issues/<N>/labels -f \'labels[]=needs-decision\'` then `pnpm josh issue:comment <N> --body-file <path>` \u2014 then send the `confirmation` Telegram and stop. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` \u2192 "A lane child records its park before it stops", and the park itself is `backlogrun-park.md` \u2192 "park and continue". This rule fires on every occurrence, not once per run.';
1408
- var ROW9 = {
1445
+ var ROW8 = {
1409
1446
  id: "lane-interactive-ask",
1410
1447
  is_trigger: is_interactive_ask,
1411
1448
  reason: LANE_INTERACTIVE_ASK_REASON,
1412
1449
  decide: () => true
1413
1450
  };
1414
- var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW: ROW9, is_interactive_ask };
1451
+ var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW: ROW8, is_interactive_ask };
1415
1452
 
1416
1453
  // scripts/rules/lane-split-park.ts
1417
1454
  var EPIC_COMMANDS = /* @__PURE__ */ new Set(["epic"]);
@@ -1461,7 +1498,7 @@ function decide3(_call, run2) {
1461
1498
  return issue !== void 0 && promoted_issues(commands).has(issue);
1462
1499
  }
1463
1500
  var LANE_SPLIT_PARK_REASON = '\u26D4 lane split park: this session is a dispatched lane child that promoted its own Issue to an epic this run, and a split is not a park \u2014 do not apply `needs-decision` to the promoted epic and do not send a `confirmation` Telegram. The parent reads an open Issue carrying `epic` as a split and continues through its children; a question here has nobody to answer it (joshuafolkken/kit#3296, measured 2026-10-06 when the child for #3255 parked its promoted epic). If what remains of the original work needs a person, park that child issue instead, then end the turn. The procedure is `.claude/skills/workflow-commands/backlogrun-park.md` \u2192 "Splitting a child mid-run". This rule fires on every occurrence, not once per run.';
1464
- var ROW10 = {
1501
+ var ROW9 = {
1465
1502
  id: "lane-split-park",
1466
1503
  is_trigger: bash_triggers.on_bash_command(is_candidate),
1467
1504
  reason: LANE_SPLIT_PARK_REASON,
@@ -1469,7 +1506,7 @@ var ROW10 = {
1469
1506
  };
1470
1507
  var lane_split_park = {
1471
1508
  LANE_SPLIT_PARK_REASON,
1472
- ROW: ROW10,
1509
+ ROW: ROW9,
1473
1510
  promoted_issues
1474
1511
  };
1475
1512
 
@@ -1501,8 +1538,8 @@ function is_switch_away_from_lane(command, is_lane_child = lane_child_marker.is_
1501
1538
  if (!is_lane_child || own_branch === void 0) return false;
1502
1539
  return switch_targets(command).some((target) => target !== own_branch);
1503
1540
  }
1504
- var LANE_SWITCH_MAIN_REASON = "\u26D4 lane child on `git switch main`: this session is a dispatched lane child, and switching to the default branch here always fails \u2014 a lane is a linked work tree, the primary checkout holds the default branch, and git allows one branch in one work tree at a time (`fatal: main is already used by worktree at <the primary checkout>`, measured 11 times in the 2026-09-21 backlogrun). Do not switch: the lane is already on its own issue branch, branched from a fresh default before it opened, and the latest default is brought in during the gate by `pnpm josh main:merge`. So skip this step and keep implementing on the current branch \u2014 no `git switch`, no `git pull`. `pnpm josh main:sync` (`josh ms`) is refused in a lane for the same reason, and a lane is finished with `pnpm josh lane:close <issue-number>`. The parent runs `git switch main && git pull` in the primary checkout before each lane opens, never the child \u2014 `backlogrun-lanes.md` and `backlogrun-child.md` are the single sources. This rule fires on every occurrence, not once per run.";
1505
- var ROW11 = {
1541
+ var LANE_SWITCH_MAIN_REASON = "\u26D4 lane child on `git switch main`: this session is a dispatched lane child, and switching to the default branch here always fails \u2014 a lane is a linked work tree, the primary checkout holds the default branch, and git allows one branch in one work tree at a time (`fatal: main is already used by worktree at <the primary checkout>`, measured 11 times in the 2026-09-21 backlogrun). Do not switch: the lane is already on its own issue branch, branched from a fresh default before it opened, and the latest default is brought in during the gate by `pnpm josh main:merge`. So skip this step and keep implementing on the current branch \u2014 no `git switch`, no `git pull`. `pnpm josh main:sync` (`josh ms`) is refused in a lane for the same reason, and a lane is finished with `pnpm josh lane:close <issue-number>`. The parent runs `pnpm josh ms` in the primary checkout before each lane opens, never the child \u2014 `backlogrun-lanes.md` and `backlogrun-child.md` are the single sources. This rule fires on every occurrence, not once per run.";
1542
+ var ROW10 = {
1506
1543
  id: "lane-switch-main",
1507
1544
  is_trigger: bash_triggers.on_bash_command(is_switch_away_from_lane),
1508
1545
  reason: LANE_SWITCH_MAIN_REASON,
@@ -1510,7 +1547,7 @@ var ROW11 = {
1510
1547
  };
1511
1548
  var lane_switch_main = {
1512
1549
  LANE_SWITCH_MAIN_REASON,
1513
- ROW: ROW11,
1550
+ ROW: ROW10,
1514
1551
  is_switch_away_from_lane,
1515
1552
  switch_target,
1516
1553
  switch_targets
@@ -1693,12 +1730,12 @@ function next_commands(command) {
1693
1730
  return COMMANDS.slice(COMMANDS.indexOf(command) + 1);
1694
1731
  }
1695
1732
  function next_line(command, issue) {
1696
- const commands = next_commands(command).map((next) => `${next} #${issue}`);
1733
+ const commands = next_commands(command).map((next) => `${next} ${issue_cite.plain(issue)}`);
1697
1734
  return `${NEXT_PREFIX}${commands.join(NEXT_SEPARATOR)}`;
1698
1735
  }
1699
1736
  function format_decision(issue, decision) {
1700
1737
  const fields = [`at: ${decision.state}`, `to: ${decision.command}`, `start: ${decision.start}`];
1701
- return `stage #${issue} \u2014 ${fields.join(" \xB7 ")}`;
1738
+ return `stage ${issue_cite.plain(issue)} \u2014 ${fields.join(" \xB7 ")}`;
1702
1739
  }
1703
1740
  function cell(state, command) {
1704
1741
  const decision = decide4(state, command);
@@ -1838,7 +1875,7 @@ var DECISION_ORACLES = [
1838
1875
  name: "issue:fold",
1839
1876
  decision: "Whether findings filed from one session fold into one issue or stay separate",
1840
1877
  args: "<title...>",
1841
- vocabulary: ["fold", "separate", "no-fold-needed"],
1878
+ vocabulary: ["fold", "separate", "no-fold-needed", "undetermined"],
1842
1879
  single_source: SPLIT_ASSESSMENT_QUESTION
1843
1880
  },
1844
1881
  {
@@ -1960,10 +1997,7 @@ var NOT_NAMED = /* @__PURE__ */ new Map([
1960
1997
  ["epic:bundle", FILING_STEP_REASON],
1961
1998
  ["issue:scout", FILING_STEP_REASON],
1962
1999
  ["issue:lint", FILING_STEP_REASON],
1963
- [
1964
- "issue:fold",
1965
- "the second filing it governs is already gated by the `issue-fold` delivered rule"
1966
- ],
2000
+ ["issue:fold", "`issue:file` asks the fold question itself on the second filing it governs"],
1967
2001
  ["cases", READ],
1968
2002
  ["issue:state", READ],
1969
2003
  ["run:cut:resume", SELF],
@@ -2083,13 +2117,13 @@ function is_destructive_command(command) {
2083
2117
  return shell_segments.segments_of(command).some((segment) => is_destructive_segment2(segment));
2084
2118
  }
2085
2119
  var DESTRUCTIVE_COMMAND_REASON = "\u26D4 destructive command: a recursive forced `rm` (`rm -rf`, `rm -fr`, `rm -r -f`, `rm --recursive --force`), `gh repo delete` / `archive`, `gh repo edit --visibility`, `gh pr close`, or a `gh api -X DELETE repos/\u2026` call deletes or closes something that cannot be restored, which is Tier C and needs explicit current-turn user instruction (`prompts/collaboration-workflow/operating-rules.md`). The `.claude/settings.json` deny list matches prefix globs, so it misses these spellings; this row reads the argv instead. Remove a file or directory one path at a time without `-f`, or ask the user to run the command in their own terminal. `gh issue close` and removing a label from an Issue (`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`) are not refused. **This rule fires on every occurrence, not once per run.**";
2086
- var ROW12 = {
2120
+ var ROW11 = {
2087
2121
  id: "destructive-command",
2088
2122
  is_trigger: bash_triggers.on_bash_command(is_destructive_command),
2089
2123
  reason: DESTRUCTIVE_COMMAND_REASON,
2090
2124
  decide: () => true
2091
2125
  };
2092
- var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW12, is_destructive_command };
2126
+ var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW11, is_destructive_command };
2093
2127
 
2094
2128
  // scripts/rules/direct-pr-create.ts
2095
2129
  var GH_COMMAND2 = "gh";
@@ -2144,13 +2178,13 @@ function is_direct_pr_create(command) {
2144
2178
  return shell_segments.segments_of(command).some((segment) => is_pr_create_segment(segment));
2145
2179
  }
2146
2180
  var DIRECT_PR_CREATE_REASON = "\u26D4 direct PR creation: `gh pr create` (or a `gh api` POST to `repos/<o>/<r>/pulls`) bypasses `build_body`, which writes the `closes #N` line \u2014 the Issue would stay open after the merge (`CLAUDE.md` \u2192 Git Rules). Open the PR with `pnpm josh pr` instead; after a failed push, fix, push, then run `pnpm josh pr`. **This rule fires on every occurrence, not once per run.**";
2147
- var ROW13 = {
2181
+ var ROW12 = {
2148
2182
  id: "direct-pr-create",
2149
2183
  is_trigger: bash_triggers.on_bash_command(is_direct_pr_create),
2150
2184
  reason: DIRECT_PR_CREATE_REASON,
2151
2185
  decide: () => true
2152
2186
  };
2153
- var direct_pr_create = { DIRECT_PR_CREATE_REASON, ROW: ROW13, is_direct_pr_create };
2187
+ var direct_pr_create = { DIRECT_PR_CREATE_REASON, ROW: ROW12, is_direct_pr_create };
2154
2188
 
2155
2189
  // scripts/rules/worktree-guard.ts
2156
2190
  var CHECKOUT_SUBCOMMAND = "checkout";
@@ -2211,15 +2245,15 @@ function is_unauthorized_segment(segment) {
2211
2245
  function is_unauthorized_worktree_change(command) {
2212
2246
  return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
2213
2247
  }
2214
- var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>`, a forced `git clean` and an unauthorized `git stash` discard work or mutate the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. **This rule fires on every occurrence, not once per run.**';
2215
- var ROW14 = {
2248
+ var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>`, a forced `git clean` and an unauthorized `git stash` discard work or mutate the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. A plan that restores a handed-over stash with `git stash apply` / `git stash pop` is read as `pnpm josh stash:pop "<message>"`: apply it that way, continue, and record the substitution on the Issue \u2014 Tier A, never a `needs-decision` park unless the plan says why the owner keeps the stash: then stop and confirm (`operating-rules.md` \u2192 "no-self-staging"). **This rule fires on every occurrence, not once per run.**';
2249
+ var ROW13 = {
2216
2250
  id: "worktree-mutation",
2217
2251
  is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
2218
2252
  reason: WORKTREE_MUTATION_REASON,
2219
2253
  decide: () => true
2220
2254
  };
2221
2255
  var worktree_guard = {
2222
- ROW: ROW14,
2256
+ ROW: ROW13,
2223
2257
  WORKTREE_MUTATION_REASON,
2224
2258
  is_unauthorized_worktree_change,
2225
2259
  touches_worktree
@@ -2248,13 +2282,13 @@ function is_index_mutation(command) {
2248
2282
  return shell_segments.segments_of(command).some((segment) => is_index_segment(segment));
2249
2283
  }
2250
2284
  var INDEX_MUTATION_REASON = '\u26D4 git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore --staged` overwrite the user\'s staged snapshot, which `CLAUDE.md` \u2192 Git Rules forbids on your own judgement (`prompts/collaboration-workflow/operating-rules.md` \u2192 "no-self-staging"). The `.claude/settings.json` deny list matches prefix globs, so it misses `git -C . commit`, `git -c k=v commit` and `env git commit`; this row reads the argv instead, so the spelling does not matter. A commit goes through `pnpm josh git -y`; any other staging needs explicit current-turn user instruction, run by the user in their own terminal. **This rule fires on every occurrence, not once per run.**';
2251
- var ROW15 = {
2285
+ var ROW14 = {
2252
2286
  id: "index-mutation",
2253
2287
  is_trigger: bash_triggers.on_bash_command(is_index_mutation),
2254
2288
  reason: INDEX_MUTATION_REASON,
2255
2289
  decide: () => true
2256
2290
  };
2257
- var index_guard = { INDEX_MUTATION_REASON, ROW: ROW15, is_index_mutation };
2291
+ var index_guard = { INDEX_MUTATION_REASON, ROW: ROW14, is_index_mutation };
2258
2292
 
2259
2293
  // scripts/rules/protected-files.ts
2260
2294
  import path5 from "node:path";
@@ -2382,7 +2416,9 @@ var doctor_consumer = {
2382
2416
  // scripts/rules/protected-files.ts
2383
2417
  var READ_TOOL = "Read";
2384
2418
  var EDIT_TOOLS2 = /* @__PURE__ */ new Set(["Edit", "Write"]);
2385
- var ENVIRONMENT_FILE = ".env";
2419
+ var SECRET_FILES = /* @__PURE__ */ new Set([".env", ".dev.vars"]);
2420
+ var ENVIRONMENT_VARIANT_PREFIX = ".env.";
2421
+ var TRACKED_VARIANTS = /* @__PURE__ */ new Set([".env.example", ".env.test"]);
2386
2422
  var SETTINGS_FILE = path5.join(".claude", "settings.json");
2387
2423
  var WHITESPACE3 = /\s+/u;
2388
2424
  var TOKEN_QUOTES = /["']/gu;
@@ -2413,7 +2449,9 @@ function file_path_of(call) {
2413
2449
  return typeof value === "string" ? value : "";
2414
2450
  }
2415
2451
  function is_environment_path(token) {
2416
- return path5.basename(token.replaceAll(TOKEN_QUOTES, "")) === ENVIRONMENT_FILE;
2452
+ const name = path5.basename(token.replaceAll(TOKEN_QUOTES, ""));
2453
+ if (TRACKED_VARIANTS.has(name)) return false;
2454
+ return SECRET_FILES.has(name) || name.startsWith(ENVIRONMENT_VARIANT_PREFIX);
2417
2455
  }
2418
2456
  function is_environment_read_segment(segment) {
2419
2457
  const words = segment.replaceAll(INPUT_REDIRECT, " < ").split(WHITESPACE3).filter((word) => word !== "");
@@ -2439,14 +2477,14 @@ function is_consumer_settings_edit(call, is_kit_consumer2) {
2439
2477
  function is_protected_file_call(call, is_kit_consumer2 = doctor_consumer.is_kit_consumer) {
2440
2478
  return is_environment_read(call) || is_consumer_settings_edit(call, is_kit_consumer2);
2441
2479
  }
2442
- var PROTECTED_FILE_REASON = '\u26D4 protected file: `.env` holds the Telegram bot token, so reading it \u2014 with the Read tool or a shell reader such as `cat` / `grep` / `source` \u2014 puts a secret in the transcript \u2014 the scripts that need it load it in their own process. `.claude/settings.json` in a consumer repository is the deny list and the hooks every other rule rests on, so a run must not rewrite it; `josh sync` owns it, and a change belongs upstream in kit (`CLAUDE.md` \u2192 "Route distributed-doc / config changes upstream to kit"). Leave both files alone, or ask the user. **This rule fires on every occurrence, not once per run.**';
2443
- var ROW16 = {
2480
+ var PROTECTED_FILE_REASON = '\u26D4 protected file: `.env` holds the Telegram bot token, and a `.env.*` variant or `.dev.vars` holds the same kind of secret, so reading one \u2014 with the Read tool or a shell reader such as `cat` / `grep` / `source` \u2014 puts a secret in the transcript \u2014 the scripts that need it load it in their own process (the tracked `.env.example` and `.env.test` stay readable). `.claude/settings.json` in a consumer repository is the deny list and the hooks every other rule rests on, so a run must not rewrite it; `josh sync` owns it, and a change belongs upstream in kit (`CLAUDE.md` \u2192 "Route distributed-doc / config changes upstream to kit"). Leave these files alone, or ask the user. **This rule fires on every occurrence, not once per run.**';
2481
+ var ROW15 = {
2444
2482
  id: "protected-file",
2445
2483
  is_trigger: is_protected_file_call,
2446
2484
  reason: PROTECTED_FILE_REASON,
2447
2485
  decide: () => true
2448
2486
  };
2449
- var protected_files = { ROW: ROW16, is_protected_file_call };
2487
+ var protected_files = { ROW: ROW15, is_protected_file_call };
2450
2488
 
2451
2489
  // scripts/rules/permission-guards.ts
2452
2490
  var ROWS2 = [
@@ -2482,8 +2520,11 @@ function is_masked_verification(command) {
2482
2520
  return time_shell.discarded_commands(command).some((segment) => is_verification_command(segment));
2483
2521
  }
2484
2522
  var PIPED_VERIFICATION_REASON = "\u26D4 piped verification: a pipeline exits with its last command's status, so `pnpm josh gate | tail` reports success on a gate that failed, and the verdict is discarded before anything reads it. Run the check without the pipe \u2014 josh prints its verdict line last, so the harness output cap keeps it even when the middle is elided. Where the output genuinely has to be narrowed, redirect it to a file and read ranges from that file, or prefix `set -o pipefail` so the pipeline carries the check's status. Read the printed verdict either way, never the exit code alone. Read-only listings are untouched \u2014 this fires only on a command whose result means pass or fail. The rule is in `prompts/collaboration-workflow/output-bounds.md`. Reissue this call with no pipe \u2014 it fires once per run and cannot repeat on the call in hand.";
2523
+ function pipelines_of(command) {
2524
+ return shell_segments.segments_of(time_shell.unquoted(command)).map((segment) => segment.split("|").map((piece) => piece.trim()));
2525
+ }
2485
2526
  function command_pieces(command) {
2486
- return shell_segments.segments_of(time_shell.unquoted(command)).flatMap((segment) => segment.split("|")).map((piece) => piece.trim());
2527
+ return pipelines_of(command).flat();
2487
2528
  }
2488
2529
  function runs_verification(command) {
2489
2530
  return command_pieces(command).some((piece) => is_verification_command(piece));
@@ -2491,11 +2532,38 @@ function runs_verification(command) {
2491
2532
  function keeps_verdict_intact(command) {
2492
2533
  return runs_verification(command) && !is_masked_verification(command);
2493
2534
  }
2535
+ var PIPEFAIL_PREFIX = "set -o pipefail; ";
2536
+ var FULL_READING_FILTER = /^(?:tail|grep)\b/u;
2537
+ var EARLY_EXIT_GREP = /^grep\b.*\s(?:-[a-zA-Z]*[lLmq]|--(?:quiet|silent|max-count|files-with(?:out)?-match))/u;
2538
+ var ALLOWED_OPERATORS = /2>&1|&&/gu;
2539
+ var SIDE_EFFECT_SYNTAX = /[&<>`]|\$\(/u;
2540
+ var PIPEFAIL_NOTE = "\u21BB piped verification: the josh check was piped, so `set -o pipefail;` was prefixed and the call ran with the check's status carried to the exit code. Read the printed verdict, never the exit code alone \u2014 `prompts/collaboration-workflow/output-bounds.md` (joshuafolkken/kit#3570).";
2541
+ function reads_to_the_end(piece) {
2542
+ return FULL_READING_FILTER.test(piece) && !EARLY_EXIT_GREP.test(piece);
2543
+ }
2544
+ function is_filtered_check(pipeline) {
2545
+ const [head = "", ...filters] = pipeline;
2546
+ return is_verification_command(head) && filters.every((piece) => reads_to_the_end(piece));
2547
+ }
2548
+ function has_side_effect_syntax(command) {
2549
+ return SIDE_EFFECT_SYNTAX.test(command.replaceAll(ALLOWED_OPERATORS, " "));
2550
+ }
2551
+ function is_pipefail_rewritable(command) {
2552
+ return is_masked_verification(command) && !has_side_effect_syntax(command) && pipelines_of(command).every((pipeline) => is_filtered_check(pipeline));
2553
+ }
2554
+ function pipefail_input(call) {
2555
+ if (call.name !== cost_blocks.BASH_TOOL || !json_value.is_record(call.input)) return void 0;
2556
+ const command = time_shell.bash_command(call.input);
2557
+ if (!is_pipefail_rewritable(command)) return void 0;
2558
+ return { ...call.input, command: `${PIPEFAIL_PREFIX}${command}` };
2559
+ }
2494
2560
  var piped_verification = {
2495
2561
  PIPED_VERIFICATION_REASON,
2562
+ PIPEFAIL_NOTE,
2496
2563
  VERIFICATION_COMMANDS,
2497
2564
  is_masked_verification,
2498
2565
  keeps_verdict_intact,
2566
+ pipefail_input,
2499
2567
  runs_verification
2500
2568
  };
2501
2569
 
@@ -2525,7 +2593,7 @@ function is_poll_loop(command) {
2525
2593
  return LOOP_KEYWORD2.test(plain) && SLEEP_COMMAND.test(plain);
2526
2594
  }
2527
2595
  var POLL_LOOP_REASON = '\u26D4 output poll loop: this hand-writes a `while`/`until` wait loop over a command\'s output, a marker file or a process \u2014 with a `sleep` or, worse, without one \u2014 and the loop does not know when the command finished. A regex that never matches waits to the harness block limit; one that matches a mid-run line exits too early \u2014 either way the command exit is not what ends the wait, and each pass spends a full-context round trip (joshuafolkken/kit#2371 measured about 45 minutes, 13% of six lanes, lost to exactly this). Do not poll the output file. A command known to take minutes \u2014 `pnpm josh gate`, `pnpm josh followup`, `pnpm josh format` \u2014 is started detached from the first with `run_in_background` set, and the harness re-invokes you when it exits: its completion notification is what ends the wait, on the task exit rather than a regex or the block limit. So issue the long command in the background and end the turn \u2014 do not arm a loop; the wake-up brings you back. To glance at interim output, `Read` the output file once, never in a sleep loop. If you are waiting on CI, `pnpm josh followup` waits on it for you. The procedure is `.claude/skills/workflow-commands/background-commands.md`, "A command that can take minutes is issued in the background". **This rule fires on every occurrence, not once per run.**';
2528
- var ROW17 = {
2596
+ var ROW16 = {
2529
2597
  id: "poll-loop",
2530
2598
  is_trigger: bash_triggers.on_bash_command(is_poll_loop),
2531
2599
  reason: POLL_LOOP_REASON,
@@ -2533,7 +2601,7 @@ var ROW17 = {
2533
2601
  };
2534
2602
  var poll_loop = {
2535
2603
  POLL_LOOP_REASON,
2536
- ROW: ROW17,
2604
+ ROW: ROW16,
2537
2605
  is_poll_loop
2538
2606
  };
2539
2607
 
@@ -2565,13 +2633,17 @@ function current_state2() {
2565
2633
  directory: process.cwd(),
2566
2634
  carried: run_cut.carried_cut_sync,
2567
2635
  marked_issue: lane_child_marker.marked_issue(),
2636
+ is_reviewer: agent_session_role.is_reviewer(),
2568
2637
  context_verdict: cost_cli.session_verdict
2569
2638
  };
2570
2639
  }
2640
+ function dispatched_issue(state) {
2641
+ return state.is_reviewer ? void 0 : state.marked_issue;
2642
+ }
2571
2643
  function uncut_lane_issue(state) {
2572
2644
  const issue = lane_paths.lane_issue_of(state.directory);
2573
2645
  if (issue === void 0) return void 0;
2574
- if (state.marked_issue !== issue) return void 0;
2646
+ if (dispatched_issue(state) !== issue) return void 0;
2575
2647
  return state.carried()?.issue === issue ? void 0 : issue;
2576
2648
  }
2577
2649
  function warrants_the_cut2(verdict) {
@@ -2603,13 +2675,13 @@ function posts_at_path_literally(command) {
2603
2675
  return RAW_FIELD_BODY_AT.test(command);
2604
2676
  }
2605
2677
  var RAW_FIELD_BODY_REASON = "\u26D4 literal @path body: this passes `body=@<path>` to `gh api` with a raw field flag (`-f` / `--raw-field`), which sends the value verbatim \u2014 the string `@<path>` is posted as the comment, not the file it points at. `-F` / `--field` is the flag that reads the file, and the two spellings differ by one character while `gh` exits 0 and returns a comment URL either way, so the break is invisible until a person reads the issue (joshuafolkken/kit#2304 lost two park comments this way). Post an issue or PR comment with `pnpm josh issue:comment <N> --body-file <path>` \u2014 the one spelling, which cannot take the wrong flag. The rule and the safe forms are in `prompts/collaboration-workflow/shell-body.md`. This rule fires on every occurrence, not once per run.";
2606
- var ROW18 = {
2678
+ var ROW17 = {
2607
2679
  id: "raw-field-body",
2608
2680
  is_trigger: bash_triggers.on_bash_command(posts_at_path_literally),
2609
2681
  reason: RAW_FIELD_BODY_REASON,
2610
2682
  decide: () => true
2611
2683
  };
2612
- var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW18, posts_at_path_literally };
2684
+ var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW17, posts_at_path_literally };
2613
2685
 
2614
2686
  // scripts/rules/rule-body-guard.ts
2615
2687
  var EDIT_TOOL = "Edit";
@@ -2680,7 +2752,7 @@ function runs_placement_oracle(command) {
2680
2752
  (names) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
2681
2753
  );
2682
2754
  }
2683
- var ROW19 = {
2755
+ var ROW18 = {
2684
2756
  id: "rule-body",
2685
2757
  is_trigger: writes_rule_prose,
2686
2758
  reason: RULE_BODY_REASON,
@@ -2689,7 +2761,7 @@ var ROW19 = {
2689
2761
  };
2690
2762
  var rule_body_guard = {
2691
2763
  RULE_BODY_REASON,
2692
- ROW: ROW19,
2764
+ ROW: ROW18,
2693
2765
  answered_placement,
2694
2766
  is_rule_document,
2695
2767
  writes_rule_prose
@@ -2925,6 +2997,9 @@ function project_root(directory) {
2925
2997
  (candidate) => existsSync4(path7.join(candidate, PACKAGE_JSON))
2926
2998
  ) ?? directory;
2927
2999
  }
3000
+ function is_kit_repository(directory) {
3001
+ return !doctor_consumer.is_kit_consumer(project_root(directory));
3002
+ }
2928
3003
  function is_ignored(entry) {
2929
3004
  return entry.split(/[/\\]/u).some((part) => IGNORED_DIRS.has(part));
2930
3005
  }
@@ -2998,6 +3073,7 @@ var project_checks = {
2998
3073
  has_config,
2999
3074
  has_files,
3000
3075
  is_basic,
3076
+ is_kit_repository,
3001
3077
  prettier_skip_reason,
3002
3078
  project_root,
3003
3079
  skip_notice,
@@ -3121,8 +3197,8 @@ function is_untested_commit(call) {
3121
3197
  return test_declared_changed.current_verdict() === "required";
3122
3198
  }
3123
3199
  var REASON = '\u26D4 test declared: this commits a runtime code change with no test beside it \u2014 `pnpm josh test:declared` reads the working tree as `required` (a changed file that is neither a test \u2014 `*.test.ts` / `*.e2e.ts` \u2014 nor a mechanically-exempt non-runtime path). `CLAUDE.md` \u2192 Code Change Rules requires a test for ALL code changes: a bug fix gets a regression test, a refactor gets tests pinning behavior first, logic gets a unit test, an observable UI/timing change gets an E2E. Add the declared test and reissue this commit. **If the change genuinely touches no executable runtime path** it is the pre-approved non-runtime exception \u2014 declare it in your Step 0 work summary with the reason it is exempt (non-executable config, a cosmetic asset swap) and reissue; the exception is a presentation, not a stop. Run `pnpm josh test:declared` to see the files it names. The procedure is `prompts/collaboration-workflow/rule-delivery.md` \u2192 "\u914D\u9001\u3055\u308C\u3066\u3044\u308B\u898F\u5247". This fires once per run, so reissuing will not repeat on the call in hand.';
3124
- var ROW20 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
3125
- var test_declared_commit = { REASON, ROW: ROW20 };
3200
+ var ROW19 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
3201
+ var test_declared_commit = { REASON, ROW: ROW19 };
3126
3202
 
3127
3203
  // scripts/rules/third-party-write.ts
3128
3204
  function is_third_party_write_segment(segment, session_owner, resolve_placeholder_owners) {
@@ -3160,67 +3236,30 @@ function writes_third_party(command, resolve_owner = repo_party.current_owner, r
3160
3236
  );
3161
3237
  }
3162
3238
  var THIRD_PARTY_WRITE_REASON = '\u26D4 third-party write: this `gh api` call writes to a repository whose owner is not this session \u2014 a tracker we do not own is Tier C, and every write there (Issue, comment, PR, Discussion, review) needs explicit current-turn user instruction; typing the target, or holding a correct diagnosis, is not that instruction (`CLAUDE.md` \u2192 "Third-party repositories are Tier C", `prompts/collaboration-workflow/upstream-interrupt.md`). Confirm the party with `pnpm josh repo:party <owner/repo>` \u2014 owner equality, computed not judged. A read (`-X GET`, or no field flag) passes untouched; only a write (`-f`/`-F`/`--field`/`--raw-field`/`--input`, or `-X POST|PATCH|PUT|DELETE`) is refused. Record the finding on our own Issue and draft the artifact for a person to post \u2014 do not publish it yourself. **This rule fires on every occurrence, not once per run.**';
3163
- var ROW21 = {
3239
+ var ROW20 = {
3164
3240
  id: "third-party-write",
3165
3241
  is_trigger: bash_triggers.on_bash_command((command) => writes_third_party(command)),
3166
3242
  reason: THIRD_PARTY_WRITE_REASON,
3167
3243
  decide: () => true
3168
3244
  };
3169
3245
  var third_party_write = {
3170
- ROW: ROW21,
3246
+ ROW: ROW20,
3171
3247
  THIRD_PARTY_WRITE_REASON,
3172
3248
  writes_third_party
3173
3249
  };
3174
3250
 
3175
- // scripts/rules/wip-cap.ts
3176
- var WIP_CAP = 30;
3177
- var WIP_CAP_REASON = `\u26D4 backlog WIP cap: \`pnpm josh issue:file\` counts the target repository's open Issues before filing. With more than ${String(WIP_CAP)} open, close one first; nothing honestly closable means do not file \u2014 the command holds such a filing. Two filings are exempt and proceed while stating the overage \u2014 one the run is blocked by (\`--over-cap\`, or the \`tier-a\` / \`split\` route), and an interrupt (\`--route interrupt\`), decided by three tests rather than judgement: a verification answers wrongly, a documented workflow cannot complete, or data is lost or written outside the repository. Meeting none of the three, the finding is discretionary and waits. Both procedures are in \`prompts/collaboration-workflow/wip-cap.md\`. Reissue this call and let the command count \u2014 it fires once per run and cannot repeat on the call in hand.`;
3178
- var ISSUE_LISTING = /^gh\s+(?:-{1,2}[\w-]+(?:[= ]\S+)?\s+)*issue\s+list\b/u;
3179
- var ISSUES_QUERY = /repos\/[^\s'"]*\/issues\?[^\s'"]*state=open/u;
3180
- var OPEN_STATE = /--state[= ]open|state=open/u;
3181
- var LABEL_FILTER = /--label\b|[?&]labels=/u;
3182
- function counts_open_issues(command) {
3183
- return shell_segments.segments_of(command).some((segment) => {
3184
- if (LABEL_FILTER.test(segment)) return false;
3185
- return ISSUE_LISTING.test(segment) && OPEN_STATE.test(segment) || ISSUES_QUERY.test(segment);
3186
- });
3187
- }
3188
- var ROW22 = {
3189
- id: "wip-cap",
3190
- is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
3191
- reason: WIP_CAP_REASON,
3192
- keeps: bash_triggers.on_bash_command(counts_open_issues)
3193
- };
3194
- var wip_cap = { ROW: ROW22, WIP_CAP, WIP_CAP_REASON };
3195
-
3196
3251
  // scripts/rules/delivered-rules.ts
3197
3252
  var SWITCH_ENV_KEY = "JOSH_RULE_GUARD";
3198
3253
  var STAMP_PREFIX = "josh-rule-guard-";
3199
3254
  var { is_issue_filing, on_bash_command } = bash_triggers;
3200
3255
  var { SHELL_BODY_REASON: SHELL_BODY_REASON2, carries_a_body: carries_a_body2, is_shell_evaluated_body: is_shell_evaluated_body2, keeps_body_safe: keeps_body_safe2 } = shell_body_trigger;
3201
3256
  var DELIVERED_RULES = [
3202
- // **Listed first, ahead of the filing trigger it overlaps** (joshuafolkken/kit#2122). A `gh api`
3203
- // that files an Issue into a repository we do not own trips both this row and `wip-cap` /
3204
- // `issue-scout` / `filing-cap` — but the backlog count, the scout and the per-run cap are all about
3205
- // filing into *our own* backlog, so on a third-party target the right answer is to stop the write
3206
- // entirely rather than to count anything. Placing it first means a third-party write is refused
3207
- // before any of those speak; on a first-party filing it is silent (the owners match), so `wip-cap`
3208
- // still speaks first, exactly as before. It fires on every occurrence (`git-force.ts`).
3257
+ // First: a third-party write is stopped before the first-party filing rows count anything.
3209
3258
  third_party_write.ROW,
3210
- // **A hand-built filing is refused on every occurrence** (joshuafolkken/kit#2808) and pointed at
3211
- // `josh issue:file`, which runs the scout, the lint, the labels, the `## Origin` check and
3212
- // `epic:bundle` itself. It claims the direct call only, so the rows below — which trigger on the
3213
- // `issue:file` call — never overlap it.
3259
+ // The direct call only; `filing-cap` below triggers on `issue:file`. The WIP cap and the fold
3260
+ // question are `issue:file`'s own steps, so no row gates them.
3214
3261
  direct_filing.ROW,
3215
- wip_cap.ROW,
3216
- // **Two more rows share the filing trigger** (joshuafolkken/kit#2119, joshuafolkken/kit#2213), listed
3217
- // after `wip-cap` so the backlog count still speaks first: `filing-cap` refuses every filing past
3218
- // the per-run ceiling, and `issue-fold` refuses a *second* filing the run has not folded. Each is a
3219
- // real instance of the admissible overlap the comment on `delivery` describes — the losing rule's
3220
- // delivery is still correct one reissue later — so an unfolded, over-cap second filing is delivered
3221
- // `wip-cap`, then `filing-cap`, then `issue-fold` across its reissues.
3222
3262
  filing_cap.ROW,
3223
- issue_fold_rule.ROW,
3224
3263
  issue_comments.ROW,
3225
3264
  {
3226
3265
  id: "shell-body",
@@ -3229,19 +3268,15 @@ var DELIVERED_RULES = [
3229
3268
  keeps: on_bash_command(keeps_body_safe2),
3230
3269
  reaches: on_bash_command(carries_a_body2)
3231
3270
  },
3232
- // **The sibling of `shell-body`, one call further along** (joshuafolkken/kit#2304). `shell-body`
3233
- // refuses a body passed inline so the shell evaluates it; this refuses a body passed by path with
3234
- // the raw field flag (`-f` / `--raw-field body=@<path>`), which posts the literal `@<path>`. It
3235
- // claims no command any row above does — `shell-body` triggers on a backtick or `$`, which the
3236
- // `@<path>` misfire carries neither, and `is_issue_filing` leaves the comment endpoint alone — so
3237
- // the exactly-one-rule invariant over its fixtures holds. It fires on every occurrence (`git-force.ts`).
3238
3271
  raw_field_body.ROW,
3239
3272
  {
3240
3273
  id: "piped-verification",
3241
3274
  is_trigger: on_bash_command(piped_verification.is_masked_verification),
3242
3275
  reason: piped_verification.PIPED_VERIFICATION_REASON,
3243
3276
  keeps: on_bash_command(piped_verification.keeps_verdict_intact),
3244
- reaches: on_bash_command(piped_verification.runs_verification)
3277
+ reaches: on_bash_command(piped_verification.runs_verification),
3278
+ // A `| tail` / `| grep` masking is rewritten under `pipefail` by `pretool-guard.ts`, never refused.
3279
+ rewrite_note: piped_verification.PIPEFAIL_NOTE
3245
3280
  },
3246
3281
  {
3247
3282
  id: "early-heartbeat",
@@ -3251,29 +3286,11 @@ var DELIVERED_RULES = [
3251
3286
  keeps: on_bash_command(early_heartbeat.is_progress_watch),
3252
3287
  reaches: on_bash_command(early_heartbeat.waits_for_progress)
3253
3288
  },
3254
- // **Listed before `run-tail`, the one row it deliberately overlaps** (joshuafolkken/kit#2118). A
3255
- // foreground `pnpm josh git -y` with no test beside the change is claimed by both: `test-declared`
3256
- // decides whether the commit should happen at all, so it is listed first, exactly as `wip-cap`
3257
- // precedes `shell-body` for deciding whether an Issue should exist. Nothing is lost by `run-tail`
3258
- // losing the race — the stamps are keyed per `id`, so the reissued push is delivered `run-tail`,
3259
- // which is asserted rather than assumed. The overlap only arises in a real checkout where the
3260
- // verdict is `required`; in the hermetic non-repo suite the git read fails to `exempt`, so
3261
- // `test-declared` claims nothing and the "exactly one rule" invariant over `run-tail`'s fixture holds.
3289
+ // Before `run-tail`: whether to commit at all is decided before how to push.
3262
3290
  test_declared_commit.ROW,
3263
- // **The long-running josh command a lane child backgrounds and loses** (joshuafolkken/kit#2704). A
3264
- // headless child's background tasks die with its turn, so a backgrounded `josh gate` / `git` /
3265
- // `followup` is killed before it reports; this refuses the launch and hands back `josh ship
3266
- // --detach`, whose supervisor outlives the turn. **Listed before `run-tail`, whose push it claims in
3267
- // a child**: that row answers a foreground push with "reissue it backgrounded", the reissue this row
3268
- // refuses, so a child is sent to the detached ship on its first refusal instead of through both.
3269
- // Outside a child it claims nothing. A backgrounded bare `josh git` also trips `josh-git-bare`
3270
- // below; this row wins, and `ship --detach` satisfies both. It fires on every occurrence.
3291
+ // Before `run-tail`, so a lane child goes straight to `ship --detach` instead of through both.
3271
3292
  lane_background.ROW,
3272
- // **The one row whose trigger reads a field of the input beside the command**, so it supplies its
3273
- // own tool-name check rather than going through `on_bash_command`: a push step already issued with
3274
- // `run_in_background` is the rule obeyed, and refusing it would charge a run for doing the right
3275
- // thing. It supplies `decide` because a push is a recurring act — one per child in a batch, and a
3276
- // second inside one `fullrun` when round 2 fixes a finding in place.
3293
+ // Reads `run_in_background` beside the command; `decide`, since a push recurs.
3277
3294
  {
3278
3295
  id: "run-tail",
3279
3296
  is_trigger: run_tail_rule.is_foreground_push_step,
@@ -3282,36 +3299,10 @@ var DELIVERED_RULES = [
3282
3299
  keeps: run_tail_rule.is_backgrounded_push_step,
3283
3300
  reaches: run_tail_rule.is_push_step_call
3284
3301
  },
3285
- // **The one row whose trigger consults the world beside the command**, because "is this a lane that
3286
- // has not cut" is not readable from the call: it is the working directory's own name and the cut
3287
- // record on disk. Both reads are synchronous, and the command match runs first, so a run that never
3288
- // types `pnpm josh gate` pays nothing for it.
3289
- //
3290
- // **Once per run rather than `decide`, and the direction of the error is why.** Taking the cut ends
3291
- // the process, so this is not the recurring act joshuafolkken/kit#1570 wrote `decide` for — and the
3292
- // five verdicts that legitimately leave a run at the gate (`not-a-lane`, `unready`, `busy`,
3293
- // `failed`, `unknown`) all need the reissue to go through. A row that refused every time would wedge exactly
3294
- // those runs; a row that refuses once cannot.
3295
- //
3296
- // **`pnpm josh rule:value` read this row with a ceiling of about 50%, and joshuafolkken/kit#1867 is
3297
- // what removed it** (the ceiling was recorded here by joshuafolkken/kit#1864's review round 2). A
3298
- // cut relaunches a *new session*, which the measurement groups as a run of its own, and that
3299
- // resumed run issues the entry check — so scored on the asking alone it joined the denominator
3300
- // while never being able to satisfy `keeps`, which only the cutting run can, and ten perfectly
3301
- // obedient children read as ten kept out of twenty. **The two halves are separable, just not from
3302
- // one call**: both processes issue byte-identical `--resume` strings, but the `fresh` verdict sends
3303
- // a run on to claim the working-tree hold while the `resume` verdict tells its counterpart to skip
3304
- // that claim. `reaches_the_pre_gate_boundary` is that conjunction, and it is why `CallTest` is
3305
- // handed the run's calls beside the turn's rather than a second predicate slot of its own.
3306
- // Dropping `reaches` altogether is still strictly worse — the trigger evaluates against the
3307
- // *measuring* process's working directory, so it is always false from the main checkout and the row
3308
- // would read as no runs at all.
3309
- //
3310
- // **It overlaps `piped-verification` on a piped gate call, and the order is safe in both
3311
- // directions.** `pnpm josh gate | tail` in an uncut lane is a real instance of both defects; the
3312
- // earlier row speaks, its own reason says to reissue, and this one delivers on the reissue — the
3313
- // reading "the losing rule's delivery is still correct one call later" that the comment below
3314
- // requires of any admissible overlap.
3302
+ // Reads the lane name and the cut record. Once per run, not `decide`: taking the cut ends the
3303
+ // process, and the verdicts that leave a run at the gate all need the reissue through. `reaches`
3304
+ // tells the cutting run from the resumed one by the run's calls. Overlaps `piped-verification`
3305
+ // safely: that row speaks first and this one delivers on the reissue.
3315
3306
  {
3316
3307
  id: "pre-gate-cut",
3317
3308
  is_trigger: on_bash_command(pre_gate_cut.is_uncut_gate),
@@ -3319,125 +3310,25 @@ var DELIVERED_RULES = [
3319
3310
  keeps: on_bash_command(pre_gate_cut.takes_the_cut),
3320
3311
  reaches: pre_gate_cut.reaches_the_pre_gate_boundary
3321
3312
  },
3322
- // **The second row whose trigger consults the world beside the command** (joshuafolkken/kit#2034):
3323
- // "is this a dispatched lane child" is the dispatch mark against this checkout's own issue, read
3324
- // synchronously by `lane_child_marker.is_child_of`, and the command match runs first so an ordinary
3325
- // run pays nothing for it.
3326
- // Its once-per-run disposition is stated beside the row in `lane-park.ts`.
3327
- //
3328
- // **A split child's park is refused first** (joshuafolkken/kit#3296): a lane child that promoted its
3329
- // own Issue to an epic this run has nothing to park, so `lane-split-park` refuses the label write
3330
- // and the confirmation notify onto that epic on every occurrence. It shares the notify with
3331
- // `lane-park` and is listed first; a run that promoted nothing falls through to `lane-park`.
3313
+ // A split child's park is refused before `lane-park` sees it.
3332
3314
  lane_split_park.ROW,
3333
3315
  lane_park.ROW,
3334
- // **joshuafolkken/kit#2034's rule, one tool-call earlier** (joshuafolkken/kit#2201). `lane-park`
3335
- // fires on the `confirmation` notify a routed child reaches; a child that reaches for
3336
- // `AskUserQuestion` never gets there, because the harness ends its turn at the refused ask. This
3337
- // row's trigger is that ask, so the refusal becomes an instruction — park the question — instead of
3338
- // the child's death. Its trigger reads the input's tool name and consults the dispatch mark, like
3339
- // `lane-park`, and shares no command with any Bash row, so it claims no `gh` / `git` / `josh` call.
3340
- // It fires on every occurrence (`decide` returns true), the disposition `git-force.ts` takes: an
3341
- // interactive ask must never succeed in a child, and the route is always the park, never a reissue.
3342
3316
  lane_interactive_ask.ROW,
3343
- // **The parent budget commands a lane child must never run** (joshuafolkken/kit#2267). A dispatched
3344
- // child that ran `pnpm josh run:merge` read its `busy` as a competing session and parked its Issue
3345
- // unimplemented — but the live carry record is the parent that launched it, never a competitor. This
3346
- // row refuses `run:merge` / `run:carry` in a lane child and hands back the mechanical branch: the
3347
- // record is the parent's, so continue implementing rather than park or ask. Its trigger claims a
3348
- // `josh` subcommand no other row does (the filing rows are `gh`, `run-tail` is `git push`), so it
3349
- // overlaps nothing. It fires on every occurrence (`decide` returns true): a child must never run
3350
- // these, and the route is always "continue", never a reissue (`git-force.ts`).
3351
3317
  lane_carry_conflict.ROW,
3352
- // **The `git switch main` a lane child can never succeed at** (joshuafolkken/kit#2313), the fifth of
3353
- // the "always fails" misfires joshuafolkken/kit#2297 catalogued. A lane is a linked work tree and the
3354
- // primary checkout holds the default branch, so a child's `git switch main` fails structurally
3355
- // (`already used by worktree`); this refuses it before it fails and hands back the next command —
3356
- // skip the step, `pnpm josh main:merge` brings the default in during the gate. Its trigger reads the
3357
- // dispatch mark and the lane branch like `lane-carry-conflict`, and claims a `git switch` no row
3358
- // above matches — `git-force` is `git push` / `git branch`, `worktree-mutation` is `git checkout --`
3359
- // / `restore` / `stash` — so no two rows claim one command. It fires on every occurrence (`decide`
3360
- // returns true): a child must never run this, and the route is always "continue" (`git-force.ts`).
3361
3318
  lane_switch_main.ROW,
3362
- // **A `pnpm josh git` with no `-y` cannot succeed from an agent** (joshuafolkken/kit#2297). It
3363
- // prompts to confirm the staging, the no-TTY prompt cancels, and the run reissues with `-y` after
3364
- // throwing away the time it took to fail. This row refuses the bare call and hands back the `-y`
3365
- // form. It is disjoint from `run-tail` by the flag — that row requires `-y`, this refuses its
3366
- // absence — so no two rows claim one command. It fires on every occurrence (`decide` returns true):
3367
- // a bare call wastes the same time each time, so the route is always the `-y` reissue
3368
- // (`git-force.ts`).
3319
+ // Disjoint from `run-tail` by the `-y` flag.
3369
3320
  josh_git_bare.ROW,
3370
- // **The three Bash-string gaps the deny glob cannot express** (joshuafolkken/kit#2120), each reading
3371
- // the command's argv rather than a literal a glob keys on. They share no trigger with any row above —
3372
- // force/delete is `git push` / `git branch`, the worktree change is `git checkout` / `restore` /
3373
- // `stash`, and the file body is a heredoc / `node -e` / `perl -i` — so no two rows claim one command,
3374
- // which `delivered-rules-bash.test.ts` pins. Each declares `decide` returning true: a destructive or
3375
- // billed act is refused on every occurrence, never once per run (`git-force.ts`).
3321
+ // The Bash-string gaps the deny glob cannot express, read by argv; `delivered-rules-bash.test.ts`
3322
+ // pins that no two rows claim one command.
3376
3323
  git_force.ROW,
3377
3324
  worktree_guard.ROW,
3378
3325
  file_body.ROW,
3379
- // **The deny-list bypasses closed by argv and path rather than glob** (joshuafolkken/kit#2983) — an
3380
- // index mutation, a recursive forced `rm` or destructive `gh` call, and a protected file. Each
3381
- // claims a command or file no row above does and refuses every occurrence (`permission-guards.ts`).
3382
3326
  ...permission_guards.ROWS,
3383
- // **The hand-written wait loop a lane child improvises over a backgrounded command's output**
3384
- // (joshuafolkken/kit#2371). `early-heartbeat` refuses a bare `sleep` and stands down on any loop
3385
- // keyword; this is the loop it left — a `while`/`until` that sleeps between probes of an output
3386
- // file. Its trigger claims a command no row above matches: the loop-plus-`sleep` shape carries no
3387
- // `gh` / `git` / `josh` subcommand any other trigger keys on, and a poll loop is never a bare
3388
- // `sleep` (the loop keyword makes `early-heartbeat.is_wait_timer` false), so the Bash
3389
- // `rules_claiming` invariant holds. It fires on every occurrence (`decide` returns true): a poll
3390
- // loop wastes the same round trips each time, so the route is always "background it and end the
3391
- // turn", never a reissue (`git-force.ts`).
3392
3327
  poll_loop.ROW,
3393
- // **The second row whose trigger is an `Edit` / `Write` rather than a shell call**
3394
- // (joshuafolkken/kit#2310), and the third that consults the world beside the call: "is this a lane
3395
- // child whose recent-context cost has crossed the threshold" reads the dispatch mark, the cut record
3396
- // on disk and the synchronous `pnpm josh cost --cut` verdict — the same measurement the parent
3397
- // hand-off uses, never a second one (joshuafolkken/kit#1933). The `Edit` / `Write` name match runs
3398
- // first, then the lane read, then the transcript-priced verdict, so a run that is not a dispatched
3399
- // child in an uncut lane pays nothing for it. It self-gates on the tool name, so it claims no `Bash`
3400
- // command any row above matches — the Bash `rules_claiming` invariant is untouched.
3401
- //
3402
- // **Listed before `rule-body`, the one row it can overlap.** Both trigger on an `Edit` / `Write`; a
3403
- // rule-document edit made in a lane child that is already over threshold trips both. The cut is the
3404
- // right thing to do first — end the process and drop the accumulated context — and on the reissue in
3405
- // the fresh, under-threshold process this row is silent and `rule-body` delivers, the "losing rule's
3406
- // delivery still correct one reissue later" reading any admissible overlap needs.
3407
- //
3408
- // **It carries `decide`, and the inversion of the pre-gate cut's reasoning is why** (joshuafolkken/kit#2385).
3409
- // `pre-gate-cut` and `lane-park` are once per run because taking the cut, or the park, *ends the
3410
- // process* — the one act is not a recurring one, and the five verdicts that leave a run holding it
3411
- // all need the one reissue to go through. This cut ends the process too, but its *trigger* does not:
3412
- // crossing the threshold mid-implementation is a recurring state that once-per-run silenced for the
3413
- // rest of a process the moment its first refusal landed — so a `busy` / `failed` / `unready` verdict,
3414
- // or an edit the model reissued unchanged, left the context to grow unwatched (joshuafolkken/kit#2382
3415
- // ran to 282,747 tokens with the cut never taken). `decide` re-asks on every over-threshold edit, and
3416
- // its `delivered_at_ms` lets an edit reissued right after a refusal through, so `busy` / `failed`
3417
- // cannot wedge the run edit after edit — the reissue path once-per-run gave for free, now made
3418
- // explicit. The fresh process after a successful cut reads its own transcript under threshold, so it
3419
- // is silent without needing a carried record to quiet it.
3420
- //
3421
- // **No `reaches`, because the occasion is live-only.** `pre-gate-cut` gives a `reaches` because both
3422
- // its halves leave a command-string trace (`run:cut --resume` at entry); this row's occasion —
3423
- // crossing the threshold mid-implementation — is the working directory, the cut record and the cost
3424
- // verdict, none of which a transcript records. `pnpm josh rule:value` therefore reads it as no runs
3425
- // rather than as 0% kept (`keeps` stays, so the compliance act is named for a future measurement
3426
- // redesign), the same honest-unmeasured stance the batching and investigation rows take for the same
3427
- // reason: a trigger no transcript can reconstruct. The row itself lives in `implementation-cut.ts`.
3328
+ // Before `rule-body`, its one overlap: the cut is the right first act, and `rule-body` delivers on
3329
+ // the reissue in the fresh process.
3428
3330
  implementation_cut.ROW,
3429
- // **The first row whose trigger is an `Edit` / `Write` rather than a shell call**
3430
- // (joshuafolkken/kit#2272). It delivers the residency questions at the edit that writes a rule into
3431
- // prose, and self-gates on the tool name and the file path, so it claims no `Bash` command any row
3432
- // above matches — the `rules_claiming` invariant over Bash fixtures is untouched. It carries no
3433
- // `decide`, so it is once per run and takes the batching stand-aside `is_first_delivery` gives every
3434
- // default row: the batching guard treats an `Edit` as a candidate, exactly as it treats an Issue
3435
- // read, so this row stands aside on a batched turn and delivers on the reissue.
3436
3331
  rule_body_guard.ROW,
3437
- // **The generic oracle-consulted rows, one per oracle that declared a firing point**
3438
- // (joshuafolkken/kit#2324). Each refuses the action the oracle governs when the run has not run that
3439
- // oracle's command first, stood down once it has. Listed last; the one firing point left
3440
- // (`pkg:scout` on a package add) claims a command no row above matches.
3441
3332
  ...oracle_consulted.ROWS
3442
3333
  ];
3443
3334
  var ALONE_IN_TURN = 1;
@@ -3511,7 +3402,6 @@ var delivered_rules = {
3511
3402
  FILING_CAP_REASON: filing_cap.FILING_CAP_REASON,
3512
3403
  GIT_FORCE_REASON: git_force.GIT_FORCE_REASON,
3513
3404
  ISSUE_COMMENTS_REASON: issue_comments.ISSUE_COMMENTS_REASON,
3514
- ISSUE_FOLD_REASON: issue_fold_rule.ISSUE_FOLD_REASON,
3515
3405
  LANE_INTERACTIVE_ASK_REASON: lane_interactive_ask.LANE_INTERACTIVE_ASK_REASON,
3516
3406
  LANE_PARK_REASON: lane_park.LANE_PARK_REASON,
3517
3407
  MEASURED_RULES,
@@ -3523,8 +3413,6 @@ var delivered_rules = {
3523
3413
  SHELL_BODY_REASON: SHELL_BODY_REASON2,
3524
3414
  SWITCH_ENV_KEY,
3525
3415
  THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
3526
- WIP_CAP: wip_cap.WIP_CAP,
3527
- WIP_CAP_REASON: wip_cap.WIP_CAP_REASON,
3528
3416
  WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
3529
3417
  delivery,
3530
3418
  delivery_path,
@@ -3539,17 +3427,23 @@ import { loadavg } from "node:os";
3539
3427
  // scripts/epic/epic-busy.ts
3540
3428
  var LISTING_LIMIT = 100;
3541
3429
  var PARKED_LABELS = /* @__PURE__ */ new Set([NEEDS_DECISION_LABEL, ALREADY_DONE_LABEL]);
3542
- function format_holders(issues) {
3543
- return issues.map((issue) => `#${String(issue.number)} ${issue.title}`).join(", ");
3430
+ var NO_AGES = /* @__PURE__ */ new Map();
3431
+ function format_holder(issue, repo, ages) {
3432
+ const cite = session_cite.issue(issue.number, issue.title, repo);
3433
+ const age = ages.get(issue.number);
3434
+ return age === void 0 ? cite : `${cite} (${age})`;
3435
+ }
3436
+ function format_holders(issues, repo, ages) {
3437
+ return issues.map((issue) => format_holder(issue, repo, ages)).join(", ");
3544
3438
  }
3545
3439
  function occupied_lanes(read2) {
3546
3440
  return read2.kind === "busy" ? read2.issues.length : 0;
3547
3441
  }
3548
- function occupancy_message(issues, repo, limit) {
3549
- return `${String(issues.length)} of ${String(limit)} lanes in use in ${repo}: ${format_holders(issues)}.`;
3442
+ function occupancy_message(issues, repo, limit, ages = NO_AGES) {
3443
+ return `${String(issues.length)} of ${String(limit)} lanes in use in ${repo}: ${format_holders(issues, repo, ages)}.`;
3550
3444
  }
3551
- function lanes_full_message(issues, repo, limit) {
3552
- return `${occupancy_message(issues, repo, limit)} No lane is free, so nothing is offered here. A lane is released when its child merges or is parked; if a label is stale, remove it and ask again, and \`JOSH_LANE_LIMIT\` is what raises the ceiling.`;
3445
+ function lanes_full_message(issues, repo, limit, ages = NO_AGES) {
3446
+ return `${occupancy_message(issues, repo, limit, ages)} No lane is free, so nothing is offered here. A lane is released when its child merges or is parked; if a label is stale, remove it and ask again, and \`JOSH_LANE_LIMIT\` is what raises the ceiling.`;
3553
3447
  }
3554
3448
  function unreadable_message(repo) {
3555
3449
  return `Could not read the \`${IN_PROGRESS_LABEL}\` listing for ${repo}. That is not "nothing is running" \u2014 check \`gh auth status\` and ask again.`;
@@ -3561,8 +3455,8 @@ var BUSY_REASONS = {
3561
3455
  unreadable: unreadable_message,
3562
3456
  truncated: truncated_message
3563
3457
  };
3564
- function busy_reason(read2, repo, limit) {
3565
- if (read2.kind === "busy") return lanes_full_message(read2.issues, repo, limit);
3458
+ function busy_reason(read2, repo, limit, ages = NO_AGES) {
3459
+ if (read2.kind === "busy") return lanes_full_message(read2.issues, repo, limit, ages);
3566
3460
  if (read2.kind === "idle") return "";
3567
3461
  return BUSY_REASONS[read2.kind](repo);
3568
3462
  }
@@ -3792,7 +3686,7 @@ var issue_state_schema = z10.looseObject({
3792
3686
  });
3793
3687
  function parse_issue_state(json) {
3794
3688
  try {
3795
- const parsed = parse_json_object_safe(json, issue_state_schema);
3689
+ const parsed = parse_json_object_or_undefined(json, issue_state_schema);
3796
3690
  if (parsed === void 0) return void 0;
3797
3691
  const labels = parsed.labels ?? [];
3798
3692
  return {
@@ -3835,7 +3729,7 @@ var DEFAULT_SILENT_MINUTES = 30;
3835
3729
  var DEFAULT_GAP_SECONDS = 5;
3836
3730
  var MS_PER_MINUTE3 = 6e4;
3837
3731
  var MS_PER_SECOND2 = 1e3;
3838
- var OPEN_STATE2 = "OPEN";
3732
+ var OPEN_STATE = "OPEN";
3839
3733
  var STATE_FIELDS = "state,labels";
3840
3734
  var REASONS = {
3841
3735
  [ALIVE_VERDICT]: "The unit is still working: its output moved, or a process of the child is alive.",
@@ -3847,7 +3741,7 @@ var ALIVE_ADVICE = "Keep polling; do not touch the child.";
3847
3741
  var SETTLED_ADVICE = "Read the child with `pnpm josh issue:state <N>` and take the branch its state says.";
3848
3742
  var STOPPED_CLEAN_ADVICE = 'The unit ended \u2014 classify how before booking it. Run `pnpm josh run:merge <N> --output <path>` (add `--epic <E> --repo <owner/repo> --owner "$PPID"` for a named epic): it reads the child\'s exit record without waiting for the unit to return, re-dispatches a child that could not reach the API (counting the outage; a run of them trips the environment guard and stops the run), and parks an abandoned child with `needs-decision` against the consecutive-failure guard. The checkout is clean, so there is nothing to stash.';
3849
3743
  var STOPPED_DIRTY_ADVICE = 'Stash the half-finished work with `git stash push -u -m "backlogrun: stopped unit for #<N>"` and record it on the Issue, then classify how the unit ended with `pnpm josh run:merge <N> --output <path>` (add `--epic <E> --repo <owner/repo> --owner "$PPID"` for a named epic): it re-dispatches an API-outage child (counting the outage; a run of them trips the environment guard) and parks an abandoned child with `needs-decision` against the consecutive-failure guard, without waiting for the unit to return.';
3850
- var UNDETERMINED_ADVICE = "Read the trace that failed and ask again. Where the process trace was never given, run the `pgrep` against the checkout the unit was given and pass `--process alive` or `--process none`. Two of these in a row is a fault in the check itself rather than a slow unit: stop polling and report it.";
3744
+ var UNDETERMINED_ADVICE = "Read the trace that failed and ask again. Where the process trace was given as `unknown`, leave `--process` out so the command reads it. Two of these in a row is a fault in the check itself rather than a slow unit: stop polling and report it.";
3851
3745
  function is_output_moving(traces) {
3852
3746
  return traces.is_output_frozen === false;
3853
3747
  }
@@ -3954,7 +3848,7 @@ async function read_child_settled(issue, repo) {
3954
3848
  if (json === void 0) return void 0;
3955
3849
  const state = issue_state.parse_issue_state(json);
3956
3850
  if (state === void 0) return void 0;
3957
- return state.state !== OPEN_STATE2 || carries_settled_label(state.labels);
3851
+ return state.state !== OPEN_STATE || carries_settled_label(state.labels);
3958
3852
  }
3959
3853
  async function read_traces(request) {
3960
3854
  const [is_initial_child_settled, is_output_frozen, is_tree_dirty] = await Promise.all([
@@ -3979,7 +3873,7 @@ async function read_traces(request) {
3979
3873
  };
3980
3874
  }
3981
3875
  async function check(request) {
3982
- run_issue_number.require_issue_number(request.issue);
3876
+ issue_number_shape.require_issue_number(request.issue);
3983
3877
  return decide5(await read_traces(request));
3984
3878
  }
3985
3879
  var run_liveness = {
@@ -3994,14 +3888,14 @@ var run_liveness = {
3994
3888
 
3995
3889
  // scripts/lane/lane-report.ts
3996
3890
  var NO_LANES2 = "No lanes are open.";
3997
- var OPEN_STATE3 = "open";
3891
+ var OPEN_STATE2 = "open";
3998
3892
  var STRANDED_STATE = "stranded";
3999
3893
  var UNREADABLE_STATE = "unreadable";
4000
3894
  var UNKNOWN_VALUE = "-";
4001
3895
  var COLUMN_SEPARATOR = " ";
4002
3896
  function lane_state(lane) {
4003
3897
  if (lane.is_stranded) return STRANDED_STATE;
4004
- return lane.seat === void 0 ? UNREADABLE_STATE : OPEN_STATE3;
3898
+ return lane.seat === void 0 ? UNREADABLE_STATE : OPEN_STATE2;
4005
3899
  }
4006
3900
  function value_or_unknown(value) {
4007
3901
  return value === void 0 ? UNKNOWN_VALUE : String(value);
@@ -4041,11 +3935,11 @@ function describe_ports(lane) {
4041
3935
  return `seat ${String(lane.seat)}, dev ${value_or_unknown(lane.development_port)}, preview ${value_or_unknown(lane.preview_port)}`;
4042
3936
  }
4043
3937
  function describe_opened(lane) {
4044
- return `Opened a lane for #${lane.issue} on ${lane.branch}: ${describe_ports(lane)}.`;
3938
+ return `Opened a lane for ${session_cite.issue(lane.issue)} on ${lane.branch}: ${describe_ports(lane)}.`;
4045
3939
  }
4046
3940
  var lane_report = {
4047
3941
  NO_LANES: NO_LANES2,
4048
- OPEN_STATE: OPEN_STATE3,
3942
+ OPEN_STATE: OPEN_STATE2,
4049
3943
  STRANDED_STATE,
4050
3944
  UNREADABLE_STATE,
4051
3945
  describe_lane,
@@ -4076,7 +3970,7 @@ var CLEAN_ADVICE = "Start the child.";
4076
3970
  var RESUME_ADVICE = "Reuse the branch and run the whole verification gate from the start \u2014 refactor, `pnpm josh gate`, `/code-review` \u2014 never only the part the interrupted run had not reached. Nobody verified what it already committed.";
4077
3971
  var PARK_ADVICE = "Park the child: add `needs-decision` and comment what was found. Do not delete the branch, do not reopen the pull request, and do not commit on top of it.";
4078
3972
  var STASH_LABEL_PREFIX = "run:hold reclaimed before #";
4079
- var { require_issue_number } = run_issue_number;
3973
+ var { require_issue_number } = issue_number_shape;
4080
3974
  var UNREADABLE_PR_MESSAGE = "The pull request could not be read for branch ";
4081
3975
  function needs_reclaim(tree) {
4082
3976
  return tree.is_dirty || tree.branch !== tree.default_branch;
@@ -4118,13 +4012,13 @@ function reclaim_steps(tree, issue) {
4118
4012
  if (!tree.is_dirty) return [];
4119
4013
  return [
4120
4014
  `git stash push -u -m "${STASH_LABEL_PREFIX}${issue}"`,
4121
- `Record the stash on #${issue} \u2014 the comment is what gets it popped; nothing pops it for you.`
4015
+ `Record the stash on ${session_cite.issue(issue)} \u2014 the comment is what gets it popped; nothing pops it for you.`
4122
4016
  ];
4123
4017
  }
4124
4018
  function reclaim_advice(tree, issue) {
4125
4019
  return [
4126
4020
  ...reclaim_steps(tree, issue),
4127
- `git switch ${tree.default_branch} && git pull`,
4021
+ "pnpm josh ms",
4128
4022
  "Then ask this command again: it is re-askable, and the clean tree gets its own answer."
4129
4023
  ].join("\n");
4130
4024
  }
@@ -4143,11 +4037,7 @@ function decide6(tree, child, issue) {
4143
4037
  };
4144
4038
  }
4145
4039
  function read_pr_field(raw) {
4146
- try {
4147
- return pr_info_schema.parse(JSON.parse(raw)).state;
4148
- } catch {
4149
- return void 0;
4150
- }
4040
+ return json_value.parse_with(raw, pr_info_schema)?.state;
4151
4041
  }
4152
4042
  function to_pr_state(raw) {
4153
4043
  const state = read_pr_field(raw);
@@ -4372,12 +4262,7 @@ var SWITCH_ENV_KEY2 = "JOSH_WATCHER_GUARD";
4372
4262
  var STAMP_PREFIX2 = "josh-watcher-guard-";
4373
4263
  var payload_schema = z12.object({ transcript_path: z12.string().min(1) });
4374
4264
  function transcript_of(raw_payload) {
4375
- try {
4376
- const parsed = payload_schema.safeParse(JSON.parse(raw_payload));
4377
- return parsed.success ? parsed.data.transcript_path : void 0;
4378
- } catch {
4379
- return void 0;
4380
- }
4265
+ return json_value.parse_with(raw_payload, payload_schema)?.transcript_path;
4381
4266
  }
4382
4267
  function fires_once(transcript, now_ms) {
4383
4268
  const stamp = hook_decision.create_refusal_stamp(STAMP_PREFIX2);
@@ -4415,7 +4300,7 @@ var THRESHOLD_TEXT2 = `${cost_format.format_tokens(CONTEXT_CUT_THRESHOLD)}-token
4415
4300
  var PARENT_CUT_REASON = `\u26D4 backlogrun parent hand-off: this session drives a \`backlogrun\` (it owns the live carry record) and its recent-context cost has crossed the shared ${THRESHOLD_TEXT2} threshold between merges, so every later request re-reads the accumulated context (joshuafolkken/kit#2947). Take the hand-off now rather than at the next merge: follow \`.claude/skills/workflow-commands/backlogrun-progress.md\` \u2192 "The hand-off" from its \`over\` branch \u2014 open no new lane and take no new child, read \`pnpm josh lane:list\`, make sure every lane in flight records an output path, then run \`pnpm josh run:carry --cut --owner "$PPID"\` and, unless it answers \`capped\`, \`pnpm josh run:wake\`, post the progress comment and end the turn. A lane nobody can poll means the cut does not happen; carry on and this asks again later. Reissue this call when the hand-off is done or cannot be taken \u2014 the guard stays quiet for a while after it refuses, so the hand-off calls themselves are not refused.`;
4416
4301
  async function is_due2(state) {
4417
4302
  const carry = await state.carry();
4418
- if (carry === void 0 || run_carry.is_at_cut_cap(carry)) return false;
4303
+ if (carry === void 0 || run_headless.is_cut_capped(carry)) return false;
4419
4304
  return state.verdict() === cost_verdict.OVER_VERDICT;
4420
4305
  }
4421
4306
  function is_quiet(target, now_ms) {
@@ -4522,6 +4407,9 @@ function guard_outcome(raw_payload) {
4522
4407
  function pretool_outcome(raw_payload) {
4523
4408
  return with_step_zero_notice(guard_outcome(raw_payload), raw_payload);
4524
4409
  }
4410
+ async function mark_phase(outcome) {
4411
+ if (outcome.notice === step_zero_notice.NOTICE) await lane_phase.mark_implement();
4412
+ }
4525
4413
  async function async_reason(raw_payload) {
4526
4414
  const watcher = await run_watcher_hook.watcher_hook_reason(raw_payload);
4527
4415
  return watcher ?? await run_parent_cut_hook.parent_cut_reason(raw_payload);
@@ -4531,7 +4419,9 @@ async function pretool_outcome_async(raw_payload) {
4531
4419
  if (!is_clear(base)) return base;
4532
4420
  const reason = await async_reason(raw_payload);
4533
4421
  if (reason !== void 0) return { reason, notice: void 0, fault: void 0 };
4534
- return with_step_zero_notice(base, raw_payload);
4422
+ const outcome = with_step_zero_notice(base, raw_payload);
4423
+ await mark_phase(outcome);
4424
+ return outcome;
4535
4425
  }
4536
4426
  function guarded_call(raw_payload) {
4537
4427
  try {
@@ -4541,23 +4431,55 @@ function guarded_call(raw_payload) {
4541
4431
  return void 0;
4542
4432
  }
4543
4433
  }
4434
+ function rewrite_context(note, outcome) {
4435
+ return [note, outcome.notice ?? outcome.fault].filter(Boolean).join("\n\n");
4436
+ }
4544
4437
  function foreground_rewrite(outcome, raw_payload, is_lane_child) {
4545
4438
  if (outcome.reason !== void 0) return void 0;
4546
4439
  const call = guarded_call(raw_payload);
4547
4440
  const input = call === void 0 ? void 0 : lane_background.foreground_input(call, is_lane_child);
4548
4441
  if (input === void 0) return void 0;
4549
- const context = [lane_background.FOREGROUND_NOTE, outcome.notice ?? outcome.fault];
4550
- return hook_decision.rewrite_envelope(input, context.filter(Boolean).join("\n\n"));
4442
+ return hook_decision.rewrite_envelope(
4443
+ input,
4444
+ rewrite_context(lane_background.FOREGROUND_NOTE, outcome)
4445
+ );
4551
4446
  }
4552
- function emit(raw_payload, outcome) {
4553
- const rewrite = foreground_rewrite(outcome, raw_payload);
4447
+ function pipefail_rewrite(raw_payload) {
4448
+ if (!delivered_rules.is_enabled()) return void 0;
4449
+ const call = guarded_call(raw_payload);
4450
+ const input = call === void 0 ? void 0 : piped_verification.pipefail_input(call);
4451
+ const payload = json_value.parse_or_undefined(raw_payload);
4452
+ if (input === void 0 || !json_value.is_record(payload)) return void 0;
4453
+ return { payload: JSON.stringify({ ...payload, tool_input: input }), input };
4454
+ }
4455
+ function pipefail_envelope(outcome, input) {
4456
+ if (input === void 0 || outcome.reason !== void 0) return void 0;
4457
+ return hook_decision.rewrite_envelope(
4458
+ input,
4459
+ rewrite_context(piped_verification.PIPEFAIL_NOTE, outcome)
4460
+ );
4461
+ }
4462
+ function emit(raw_payload, outcome, pipefail_input2) {
4463
+ const rewrite = pipefail_envelope(outcome, pipefail_input2) ?? foreground_rewrite(outcome, raw_payload);
4554
4464
  if (rewrite === void 0) hook_decision.emit_outcome(outcome);
4555
4465
  else process.stdout.write(`${rewrite}
4556
4466
  `);
4557
4467
  }
4558
- var pretool_guard = { combine_outcomes, emit, foreground_rewrite, pretool_outcome };
4468
+ async function respond(raw_payload) {
4469
+ const pipefail = pipefail_rewrite(raw_payload);
4470
+ const judged = pipefail?.payload ?? raw_payload;
4471
+ emit(judged, await pretool_outcome_async(judged), pipefail?.input);
4472
+ }
4473
+ var pretool_guard = {
4474
+ combine_outcomes,
4475
+ emit,
4476
+ foreground_rewrite,
4477
+ mark_phase,
4478
+ pipefail_rewrite,
4479
+ pretool_outcome,
4480
+ respond
4481
+ };
4559
4482
 
4560
4483
  export {
4561
- pretool_outcome_async,
4562
4484
  pretool_guard
4563
4485
  };