@joshuafolkken/kit 1.1784.0 → 1.2114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/.claude/agents/reviewer-verification.md +19 -0
  2. package/.claude/agents/reviewer.md +19 -0
  3. package/.claude/settings.json +5 -6
  4. package/.claude/skills/dependency-update/SKILL.md +9 -10
  5. package/.claude/skills/workflow-commands/SKILL.md +6 -6
  6. package/.claude/skills/workflow-commands/background-commands.md +8 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
  13. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  14. package/.claude/skills/workflow-commands/chain-rule.md +41 -32
  15. package/.claude/skills/workflow-commands/delegation.md +6 -6
  16. package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
  17. package/.claude/skills/workflow-commands/followup.md +7 -5
  18. package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
  19. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  20. package/.claude/skills/workflow-commands/issue-comments.md +9 -4
  21. package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +2 -2
  23. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  24. package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
  25. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
  26. package/.claude/skills/workflow-commands/prerequisite.md +3 -2
  27. package/.claude/skills/workflow-commands/retrospective.md +2 -2
  28. package/.claude/skills/workflow-commands/split-assessment.md +7 -5
  29. package/.codex/hooks.json +5 -5
  30. package/.github/actions/setup-pnpm/action.yml +9 -16
  31. package/.github/dependabot.yml +16 -0
  32. package/.github/workflows/auto-tag.yml +45 -0
  33. package/.github/workflows/ci.yml +155 -15
  34. package/.github/workflows/dependabot-auto-merge.yml +63 -4
  35. package/.github/workflows/production.yml +8 -0
  36. package/.github/workflows/publish.yml +59 -45
  37. package/.vscode/settings.json +19 -0
  38. package/CLAUDE.md +5 -12
  39. package/SECURITY.md +2 -2
  40. package/dist/CLAUDE.md +5 -12
  41. package/dist/commands/document-read-cli.js +2 -1
  42. package/dist/commands/document-section-main.js +2 -1
  43. package/dist/commands/read-set-cli.js +194 -134
  44. package/dist/commands/run-prep-main.js +9694 -10173
  45. package/dist/commands/run-step-cli.js +22138 -18285
  46. package/dist/hooks/audit-provision.js +23 -5
  47. package/dist/hooks/chunk-3GKE653K.js +6204 -0
  48. package/dist/hooks/chunk-45CJJOO7.js +1243 -0
  49. package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
  50. package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
  51. package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
  52. package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
  53. package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
  54. package/dist/hooks/chunk-VVNYJWEI.js +30 -0
  55. package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
  56. package/dist/hooks/codex-hook-adapter.js +8 -8
  57. package/dist/hooks/format-edited.js +4 -5
  58. package/dist/hooks/inputs.json +415 -389
  59. package/dist/hooks/pretool-guard.js +7 -6
  60. package/dist/hooks/session-lang.js +101 -258
  61. package/dist/hooks/stop-guard.js +1744 -2576
  62. package/dist/josh.js +7133 -6368
  63. package/dist/version/index.js +161 -1
  64. package/eslint/rules/comment-why-only.js +108 -0
  65. package/package.json +15 -8
  66. package/prompts/coding-standards.md +1 -1
  67. package/prompts/collaboration-workflow/file-edits.md +14 -11
  68. package/prompts/collaboration-workflow/issue-citation.md +0 -4
  69. package/prompts/collaboration-workflow/issue-template.md +40 -21
  70. package/prompts/collaboration-workflow/lane-instructions.md +3 -1
  71. package/prompts/collaboration-workflow/measurement.md +8 -1
  72. package/prompts/collaboration-workflow/operating-rules.md +11 -2
  73. package/prompts/collaboration-workflow/output-bounds.md +6 -2
  74. package/prompts/collaboration-workflow/plan-comment.md +1 -1
  75. package/prompts/collaboration-workflow/principles.md +9 -0
  76. package/prompts/collaboration-workflow/residency.md +3 -2
  77. package/prompts/collaboration-workflow/rule-delivery.md +13 -1
  78. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  79. package/prompts/collaboration-workflow/wip-cap.md +3 -1
  80. package/prompts/review-rubric.md +8 -0
  81. package/prompts/review.md +31 -22
  82. package/prompts/testing-guide.md +1 -0
  83. package/scripts/agent/agent-argv.ts +14 -16
  84. package/scripts/agent/agent-provider.ts +138 -0
  85. package/scripts/agent/agent-role-profile.ts +108 -156
  86. package/scripts/agent/agent-safe-text.ts +20 -5
  87. package/scripts/agent/api-usage-limit.ts +65 -0
  88. package/scripts/agent/claude-agent-argv.ts +19 -9
  89. package/scripts/agent/codex-agent-argv.ts +118 -22
  90. package/scripts/agent/usage-limit-reset.ts +179 -0
  91. package/scripts/auth/claude-token-output.ts +2 -2
  92. package/scripts/auto-ok/auto-ok-author.ts +53 -0
  93. package/scripts/auto-ok/auto-ok-cli.ts +20 -9
  94. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  95. package/scripts/backlog/backlog-drive-cli.ts +6 -1
  96. package/scripts/backlog/backlog-drive-hold.ts +3 -3
  97. package/scripts/backlog/backlog-drive-idle.ts +10 -0
  98. package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
  99. package/scripts/backlog/backlog-drive-launch.ts +8 -2
  100. package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
  101. package/scripts/backlog/backlog-drive-named.ts +28 -7
  102. package/scripts/backlog/backlog-drive-paused.ts +5 -4
  103. package/scripts/backlog/backlog-drive-restore.ts +11 -3
  104. package/scripts/backlog/backlog-drive.ts +21 -60
  105. package/scripts/backlog/backlog-gate-scope.ts +61 -0
  106. package/scripts/backlog/backlog-measure-close.ts +112 -0
  107. package/scripts/backlog/backlog-next.ts +39 -49
  108. package/scripts/backlog/backlog-occupied.ts +2 -2
  109. package/scripts/backlog/backlog-overlap.ts +42 -8
  110. package/scripts/backlog/backlog-plan-cli.ts +4 -1
  111. package/scripts/backlog/backlog-plan-comment.ts +81 -0
  112. package/scripts/backlog/backlog-plan-read.ts +19 -6
  113. package/scripts/backlog/backlog-pool.ts +50 -12
  114. package/scripts/backlog/backlog-rank.ts +60 -13
  115. package/scripts/backlog/backlog-stalled-detect.ts +2 -1
  116. package/scripts/backlog/backlog-waves.ts +17 -2
  117. package/scripts/backlog/backlogrun-launch.ts +7 -4
  118. package/scripts/cost-runtime/context-cut-payback.ts +27 -25
  119. package/scripts/cost-runtime/cost-cli.ts +37 -16
  120. package/scripts/cost-runtime/cost-pricing.ts +9 -0
  121. package/scripts/cost-runtime/cost-verdict.ts +4 -1
  122. package/scripts/delegation/delegation-policy.ts +2 -2
  123. package/scripts/delegation/duplicate-read-guard.ts +1 -0
  124. package/scripts/delegation/investigation-guard.ts +14 -2
  125. package/scripts/delegation/investigation-reads.ts +45 -23
  126. package/scripts/dependabot/dependabot-cooldown.ts +3 -2
  127. package/scripts/discovery/repo-party.ts +80 -20
  128. package/scripts/discovery/session-owner-pin.ts +51 -0
  129. package/scripts/document/capped-print.ts +1 -1
  130. package/scripts/document/command-reference-line-budget.ts +42 -0
  131. package/scripts/document/document-byte-budget.ts +17 -7
  132. package/scripts/document/document-checks.ts +20 -0
  133. package/scripts/document/document-ratchet-check.ts +101 -0
  134. package/scripts/document/document-section.ts +1 -0
  135. package/scripts/document/entry-read-budget.ts +7 -3
  136. package/scripts/document/issue-citation-budget.ts +94 -0
  137. package/scripts/epic/epic-cli-argv.ts +2 -2
  138. package/scripts/epic/epic-fetch.ts +4 -2
  139. package/scripts/epic/epic-graph.ts +7 -2
  140. package/scripts/epic/epic-issue.ts +3 -1
  141. package/scripts/epic/epic-rank.ts +5 -0
  142. package/scripts/exports/unused-members-check.ts +62 -0
  143. package/scripts/exports/unused-members-cli.ts +6 -51
  144. package/scripts/followup/followup-ci-path.ts +67 -0
  145. package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
  146. package/scripts/followup/followup-coderabbit-wait.ts +13 -10
  147. package/scripts/followup/followup-failed-tests.ts +160 -0
  148. package/scripts/followup/followup-job-needs.ts +100 -0
  149. package/scripts/followup/followup-job-rerun.ts +303 -0
  150. package/scripts/followup/followup-main-move.ts +113 -0
  151. package/scripts/followup/git-followup-finish.ts +43 -11
  152. package/scripts/followup/git-followup-pending.ts +3 -3
  153. package/scripts/followup/git-followup-stages.ts +41 -11
  154. package/scripts/followup/git-pr-followup-checks.ts +55 -16
  155. package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
  156. package/scripts/followup/git-pr-followup.ts +62 -27
  157. package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
  158. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
  159. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
  160. package/scripts/gate/busy-average.ts +34 -2
  161. package/scripts/gate/check-related-pinned.ts +43 -0
  162. package/scripts/gate/check-related-reuse.ts +64 -0
  163. package/scripts/gate/check-related-section.ts +57 -0
  164. package/scripts/gate/check-related-unplanned.ts +34 -0
  165. package/scripts/gate/check-related.ts +265 -0
  166. package/scripts/gate/command-memory.ts +1 -1
  167. package/scripts/gate/core-admission.ts +24 -6
  168. package/scripts/gate/core-budget-timing.ts +20 -6
  169. package/scripts/gate/core-budget.ts +40 -11
  170. package/scripts/gate/core-wait-record.ts +3 -2
  171. package/scripts/gate/core-wait.ts +25 -2
  172. package/scripts/gate/core-weights.ts +15 -7
  173. package/scripts/gate/gate-labels.ts +46 -0
  174. package/scripts/gate/gate-ledger.ts +14 -6
  175. package/scripts/gate/gate-plan.ts +103 -58
  176. package/scripts/gate/gate-report.ts +9 -7
  177. package/scripts/gate/gate-run-steps.ts +26 -3
  178. package/scripts/gate/gate-skip.ts +45 -6
  179. package/scripts/gate/gate-static-group.ts +60 -0
  180. package/scripts/gate/gate-steps.ts +2 -1
  181. package/scripts/gate/gate-tree.ts +4 -2
  182. package/scripts/gate/hook-gate-reuse.ts +19 -2
  183. package/scripts/gate/metrics-totals.ts +59 -0
  184. package/scripts/gate/scoped-green.ts +68 -15
  185. package/scripts/gate/since-green.ts +14 -6
  186. package/scripts/gate/structural-checks.ts +96 -0
  187. package/scripts/gate/verification-gate.ts +51 -16
  188. package/scripts/gate/verification-inputs.ts +21 -6
  189. package/scripts/gate/verification-tail.ts +52 -0
  190. package/scripts/gh/gh-author-association.ts +23 -2
  191. package/scripts/gh/git-ai-review-scan.ts +1 -1
  192. package/scripts/gh/git-gh-api-path.ts +12 -3
  193. package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
  194. package/scripts/gh/git-gh-exec.ts +32 -2
  195. package/scripts/gh/git-gh-issue-read.ts +4 -2
  196. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  197. package/scripts/gh/git-gh-issue-write.ts +8 -2
  198. package/scripts/gh/git-gh-issue.ts +12 -5
  199. package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
  200. package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
  201. package/scripts/gh/git-gh-pr-read.ts +74 -13
  202. package/scripts/gh/git-gh-pr-rest.ts +3 -1
  203. package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
  204. package/scripts/gh/git-gh-pr-too-large.ts +20 -0
  205. package/scripts/gh/git-gh-pr.ts +21 -7
  206. package/scripts/gh/git-gh-retry.ts +141 -0
  207. package/scripts/gh/git-pr-ai-review.ts +15 -6
  208. package/scripts/gh/git-pr-checks-eval.ts +3 -0
  209. package/scripts/gh/git-pr-checks.ts +5 -5
  210. package/scripts/git/base-tree.ts +30 -0
  211. package/scripts/git/change-base.ts +4 -10
  212. package/scripts/git/changed-paths-sync.ts +63 -0
  213. package/scripts/git/changed-paths.ts +16 -1
  214. package/scripts/git/git-common-directory.ts +11 -1
  215. package/scripts/git/git-diff-reads.ts +66 -20
  216. package/scripts/git/git-error.ts +9 -2
  217. package/scripts/git/git-schemas.ts +13 -0
  218. package/scripts/git/git-worktree.ts +13 -0
  219. package/scripts/git/gone-branch.ts +4 -1
  220. package/scripts/git/main-merge-budget-conflict.ts +89 -0
  221. package/scripts/git/main-merge-guard.ts +55 -18
  222. package/scripts/git/main-merge-regenerate.ts +59 -25
  223. package/scripts/git/main-merge.ts +53 -5
  224. package/scripts/git/no-base-checkout.ts +127 -0
  225. package/scripts/git/stash/git-stash.ts +11 -2
  226. package/scripts/git/stash/stash-push-cli.ts +10 -0
  227. package/scripts/hooks/batch-guard.ts +40 -21
  228. package/scripts/hooks/edited-cspell.ts +37 -11
  229. package/scripts/hooks/format-edited-file.ts +22 -2
  230. package/scripts/hooks/lane-plan-guard.ts +50 -0
  231. package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
  232. package/scripts/hooks/pre-commit-lint.ts +14 -1
  233. package/scripts/hooks/pre-push-related-switch.ts +10 -0
  234. package/scripts/hooks/pre-push-unit.ts +76 -10
  235. package/scripts/hooks/pretool-guard.ts +2 -0
  236. package/scripts/hooks/reserved-run.ts +14 -1
  237. package/scripts/hooks/run-hook.sh +23 -8
  238. package/scripts/hooks/step-zero-notice.ts +6 -1
  239. package/scripts/hooks/stop-actions.ts +3 -15
  240. package/scripts/hooks/stop-guard.ts +7 -6
  241. package/scripts/init/distributed-paths.ts +1 -0
  242. package/scripts/init/hook-command-rewrite.ts +5 -1
  243. package/scripts/init/hook-launch.ts +4 -2
  244. package/scripts/issue/aggregation-split.ts +52 -0
  245. package/scripts/issue/defect-origin-line.ts +19 -1
  246. package/scripts/issue/defect-rate-cli.ts +5 -102
  247. package/scripts/issue/defect-report.ts +4 -4
  248. package/scripts/issue/effect-audit-cli.ts +178 -0
  249. package/scripts/issue/effect-audit.ts +185 -0
  250. package/scripts/issue/expected-effect-lint.ts +64 -9
  251. package/scripts/issue/git-next-issues.ts +5 -0
  252. package/scripts/issue/issue-body-or-comment.ts +38 -4
  253. package/scripts/issue/issue-cite-cli.ts +1 -3
  254. package/scripts/issue/issue-classification.ts +1 -0
  255. package/scripts/issue/issue-closing-pr.ts +8 -1
  256. package/scripts/issue/issue-evidence-exit.ts +4 -2
  257. package/scripts/issue/issue-file-cap.ts +58 -0
  258. package/scripts/issue/issue-file-cli.ts +111 -38
  259. package/scripts/issue/issue-file-deferral.ts +52 -0
  260. package/scripts/issue/issue-file-fold.ts +29 -10
  261. package/scripts/issue/issue-file-interrupt.ts +85 -0
  262. package/scripts/issue/issue-file-lane-hint.ts +30 -0
  263. package/scripts/issue/issue-file.ts +43 -6
  264. package/scripts/issue/issue-labels.ts +19 -2
  265. package/scripts/issue/issue-lint-cli.ts +27 -2
  266. package/scripts/issue/issue-measured-cli.ts +13 -35
  267. package/scripts/issue/issue-measured-close.ts +58 -0
  268. package/scripts/issue/issue-not-before-label.ts +2 -2
  269. package/scripts/issue/issue-not-before.ts +4 -1
  270. package/scripts/issue/issue-priority-cli.ts +60 -0
  271. package/scripts/issue/issue-priority-raise.ts +55 -0
  272. package/scripts/issue/issue-priority.ts +174 -43
  273. package/scripts/issue/issue-rank.ts +21 -4
  274. package/scripts/issue/issue-read-cli.ts +32 -6
  275. package/scripts/issue/issue-read.ts +37 -12
  276. package/scripts/issue/issue-sample-gate.ts +95 -74
  277. package/scripts/issue/issue-sample-prime.ts +127 -0
  278. package/scripts/issue/issue-sample-required.ts +57 -0
  279. package/scripts/issue/issue-sample-source.ts +12 -11
  280. package/scripts/issue/issue-search.ts +124 -0
  281. package/scripts/issue/issue-state.ts +13 -1
  282. package/scripts/issue/issue-template-section.ts +43 -0
  283. package/scripts/issue/issue-watch-diff.ts +31 -1
  284. package/scripts/issue/issue-watch-hook.ts +11 -2
  285. package/scripts/issue/issue-watch.ts +55 -12
  286. package/scripts/issue/markdown-section.ts +1 -0
  287. package/scripts/issue/measure-declaration.ts +121 -0
  288. package/scripts/issue/measure-rerun-cli.ts +7 -70
  289. package/scripts/issue/plan-consumers-section.ts +22 -1
  290. package/scripts/issue/reproduction-measure.ts +3 -1
  291. package/scripts/issue/size-estimate.ts +90 -0
  292. package/scripts/issue/trusted-measure.ts +84 -0
  293. package/scripts/josh/command-usage.ts +3 -2
  294. package/scripts/josh/durable-state-migrate.ts +105 -0
  295. package/scripts/josh/durable-state.ts +112 -0
  296. package/scripts/josh/josh-aliases.ts +95 -0
  297. package/scripts/josh/josh-command-map.ts +1 -82
  298. package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
  299. package/scripts/josh/josh-commands-ai.ts +5 -5
  300. package/scripts/josh/josh-commands-development.ts +59 -58
  301. package/scripts/josh/josh-commands-issue-write.ts +23 -4
  302. package/scripts/josh/josh-commands-issue.ts +32 -0
  303. package/scripts/josh/josh-commands-lane.ts +87 -4
  304. package/scripts/josh/josh-commands-lint.ts +16 -3
  305. package/scripts/josh/josh-commands-maintenance.ts +15 -0
  306. package/scripts/josh/josh-commands-review.ts +41 -6
  307. package/scripts/josh/josh-commands-ship.ts +56 -8
  308. package/scripts/josh/josh-commands-test-check.ts +80 -0
  309. package/scripts/josh/josh-commands-workflow.ts +1 -1
  310. package/scripts/josh/josh-environment-file.ts +43 -3
  311. package/scripts/josh/josh-in-process.ts +2 -2
  312. package/scripts/josh/josh-logic.ts +5 -3
  313. package/scripts/josh/josh-run.ts +19 -1
  314. package/scripts/josh/run-record-path.ts +15 -0
  315. package/scripts/josh/stamp-file.ts +11 -2
  316. package/scripts/lane/lane-adopt-cli.ts +98 -0
  317. package/scripts/lane/lane-adopt.ts +208 -0
  318. package/scripts/lane/lane-await.ts +5 -8
  319. package/scripts/lane/lane-baseline.ts +123 -0
  320. package/scripts/lane/lane-breakdown-cli.ts +64 -0
  321. package/scripts/lane/lane-breakdown-implement.ts +170 -0
  322. package/scripts/lane/lane-breakdown-laps.ts +107 -0
  323. package/scripts/lane/lane-breakdown-pinned.ts +76 -0
  324. package/scripts/lane/lane-breakdown-text.ts +145 -0
  325. package/scripts/lane/lane-breakdown.ts +239 -0
  326. package/scripts/lane/lane-check-stats.ts +29 -8
  327. package/scripts/lane/lane-child-invocation.ts +9 -2
  328. package/scripts/lane/lane-child-mark-keys.ts +12 -0
  329. package/scripts/lane/lane-child-marker.ts +20 -1
  330. package/scripts/lane/lane-ci-path-stats.ts +73 -0
  331. package/scripts/lane/lane-claim-stats.ts +84 -0
  332. package/scripts/lane/lane-close.ts +8 -3
  333. package/scripts/lane/lane-dispatch-log.ts +3 -3
  334. package/scripts/lane/lane-dispatch.ts +2 -2
  335. package/scripts/lane/lane-gap-stats.ts +7 -6
  336. package/scripts/lane/lane-guard-policy.ts +7 -1
  337. package/scripts/lane/lane-implement-stats.ts +207 -0
  338. package/scripts/lane/lane-implement-subagents.ts +90 -0
  339. package/scripts/lane/lane-investigation-switch.ts +16 -0
  340. package/scripts/lane/lane-ledger-kind.ts +31 -0
  341. package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
  342. package/scripts/lane/lane-ledger-schema.ts +119 -36
  343. package/scripts/lane/lane-ledger-write.ts +257 -0
  344. package/scripts/lane/lane-ledger.ts +31 -177
  345. package/scripts/lane/lane-load.ts +1 -1
  346. package/scripts/lane/lane-measure-cli.ts +176 -59
  347. package/scripts/lane/lane-net-read.ts +44 -0
  348. package/scripts/lane/lane-net-time.ts +212 -0
  349. package/scripts/lane/lane-open.ts +16 -7
  350. package/scripts/lane/lane-output.ts +2 -4
  351. package/scripts/lane/lane-overrun-prompt.ts +147 -0
  352. package/scripts/lane/lane-overrun.ts +274 -0
  353. package/scripts/lane/lane-paths.ts +24 -1
  354. package/scripts/lane/lane-pause-cli.ts +4 -1
  355. package/scripts/lane/lane-paused-spans.ts +97 -0
  356. package/scripts/lane/lane-plan-check.ts +207 -0
  357. package/scripts/lane/lane-plan-cli.ts +41 -0
  358. package/scripts/lane/lane-plan.ts +144 -0
  359. package/scripts/lane/lane-registry.ts +14 -1
  360. package/scripts/lane/lane-relaunch.ts +1 -1
  361. package/scripts/lane/lane-round-one-stats.ts +51 -0
  362. package/scripts/lane/lane-sampler.ts +23 -4
  363. package/scripts/lane/lane-session-action.ts +93 -18
  364. package/scripts/lane/lane-session-broker.ts +49 -21
  365. package/scripts/lane/lane-session-reread-main.ts +14 -0
  366. package/scripts/lane/lane-session-reread.ts +51 -0
  367. package/scripts/lane/lane-ship-lap-stats.ts +74 -0
  368. package/scripts/lane/lane-sleep.ts +58 -0
  369. package/scripts/lane/lane-spent.ts +1 -1
  370. package/scripts/lane/lane-split-stats.ts +28 -0
  371. package/scripts/lane/lane-stage-stats.ts +13 -1
  372. package/scripts/lane/lane-state.ts +41 -9
  373. package/scripts/lane/lane-stats-tables.ts +58 -0
  374. package/scripts/lane/lane-stats.ts +21 -5
  375. package/scripts/lane/lane-stop-stats.ts +51 -19
  376. package/scripts/lane/lane-tail-stats.ts +32 -0
  377. package/scripts/lane/lane-transcript-calls.ts +67 -10
  378. package/scripts/lane/lane-transcript-stats.ts +44 -12
  379. package/scripts/lane/lane-value-cell.ts +72 -0
  380. package/scripts/lane/lane-value.ts +118 -0
  381. package/scripts/lane/lane-withdraw-closed.ts +124 -0
  382. package/scripts/lane/lane-withdraw.ts +108 -31
  383. package/scripts/lane/lane-yield.ts +85 -0
  384. package/scripts/lane/openai-lane-ship.ts +12 -11
  385. package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
  386. package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
  387. package/scripts/lane/openai-lane-supervisor.ts +5 -4
  388. package/scripts/lib/poll.ts +38 -9
  389. package/scripts/lib/settled-together.ts +10 -0
  390. package/scripts/lib/shell-quote.ts +21 -0
  391. package/scripts/lib/token-shape.ts +111 -0
  392. package/scripts/lint/eslint-daemon-sources.ts +79 -0
  393. package/scripts/lint/eslint-daemon.ts +45 -6
  394. package/scripts/lint/eslint-warm-cli.ts +7 -0
  395. package/scripts/lint/eslint-warm.ts +66 -0
  396. package/scripts/lint/import-graph.ts +141 -11
  397. package/scripts/lint/lint-gate-scope.ts +2 -2
  398. package/scripts/lint/lint-parallel.ts +54 -6
  399. package/scripts/lint/lint-related.ts +154 -33
  400. package/scripts/lint/rename-cli.ts +179 -0
  401. package/scripts/lint/rename-identifier.ts +192 -0
  402. package/scripts/lint/type-only-imports.ts +58 -0
  403. package/scripts/notify/telegram-notify.ts +12 -3
  404. package/scripts/observations/observation-record.ts +42 -16
  405. package/scripts/observations/observations-flush.ts +32 -9
  406. package/scripts/overrides/overrides-check.ts +9 -1
  407. package/scripts/release/release-approval-prune.ts +7 -1
  408. package/scripts/repo/required-checks-logic.ts +4 -1
  409. package/scripts/repo/ruleset-check.ts +4 -3
  410. package/scripts/report/markdown-shape.ts +28 -0
  411. package/scripts/report/report-format-reference.ts +15 -1
  412. package/scripts/report/report-labels.ts +12 -1
  413. package/scripts/report/report-lint-child-summary.ts +94 -0
  414. package/scripts/report/report-lint-cli.ts +26 -7
  415. package/scripts/report/report-lint.ts +2 -20
  416. package/scripts/review/prose-stale-cli.ts +88 -0
  417. package/scripts/review/prose-stale-scan.ts +152 -0
  418. package/scripts/review/review-attest.ts +73 -23
  419. package/scripts/review/review-brief-cli.ts +68 -19
  420. package/scripts/review/review-brief-paths.ts +22 -0
  421. package/scripts/review/review-brief.ts +14 -7
  422. package/scripts/review/review-comment-carry.ts +47 -0
  423. package/scripts/review/review-comment-round.ts +85 -22
  424. package/scripts/review/review-finding-ledger.ts +109 -7
  425. package/scripts/review/review-finding-text.ts +43 -0
  426. package/scripts/review/review-interaction.ts +11 -0
  427. package/scripts/review/review-lane-handover.ts +68 -0
  428. package/scripts/review/review-lint-cli.ts +57 -0
  429. package/scripts/review/review-lint-findings.ts +92 -0
  430. package/scripts/review/review-lint-sections.ts +80 -0
  431. package/scripts/review/review-lint.ts +221 -0
  432. package/scripts/review/review-prior-findings.ts +102 -0
  433. package/scripts/review/review-readers-scan.ts +4 -2
  434. package/scripts/review/review-record-cli.ts +177 -54
  435. package/scripts/review/review-record-code-round.ts +55 -0
  436. package/scripts/review/review-record-comment.ts +60 -0
  437. package/scripts/review/review-record-interaction.ts +35 -0
  438. package/scripts/review/review-record-pinned.ts +108 -0
  439. package/scripts/review/review-record.ts +25 -1
  440. package/scripts/review/review-reviewed-tree.ts +307 -0
  441. package/scripts/review/review-round-one-closure.ts +62 -0
  442. package/scripts/review/review-round-reuse.ts +106 -0
  443. package/scripts/review/review-round2-cli.ts +91 -29
  444. package/scripts/review/review-round2-skip-comment.ts +55 -0
  445. package/scripts/review/review-round2.ts +24 -8
  446. package/scripts/review/review-stamps.ts +7 -3
  447. package/scripts/review/review-stop-log.ts +20 -6
  448. package/scripts/review/review-stops-cli.ts +2 -1
  449. package/scripts/review/review-submit-cli.ts +101 -0
  450. package/scripts/rules/agent-model.ts +1 -1
  451. package/scripts/rules/bash-triggers.ts +3 -3
  452. package/scripts/rules/concise-reply.ts +88 -0
  453. package/scripts/rules/credential-body.ts +77 -0
  454. package/scripts/rules/decision-oracle.ts +1 -1
  455. package/scripts/rules/delivered-rules.ts +36 -17
  456. package/scripts/rules/destructive-command.ts +3 -11
  457. package/scripts/rules/direct-issue-comment.ts +34 -12
  458. package/scripts/rules/direct-issue-read.ts +191 -0
  459. package/scripts/rules/direct-pr-create.ts +4 -24
  460. package/scripts/rules/early-heartbeat.ts +2 -0
  461. package/scripts/rules/edit-tool.ts +27 -0
  462. package/scripts/rules/file-edit-rows.ts +17 -0
  463. package/scripts/rules/file-rewrite.ts +146 -0
  464. package/scripts/rules/firing-point.ts +3 -2
  465. package/scripts/rules/gh-api.ts +128 -7
  466. package/scripts/rules/gh-repo-selector.ts +84 -0
  467. package/scripts/rules/git-force.ts +2 -2
  468. package/scripts/rules/guard-remedy.ts +14 -5
  469. package/scripts/rules/implementation-cut.ts +61 -28
  470. package/scripts/rules/investigator-foreground.ts +231 -0
  471. package/scripts/rules/issue-citation.ts +15 -60
  472. package/scripts/rules/issue-comments.ts +9 -4
  473. package/scripts/rules/josh-git-bare.ts +1 -1
  474. package/scripts/rules/lane-background.ts +41 -13
  475. package/scripts/rules/lane-direct-tool.ts +107 -9
  476. package/scripts/rules/lane-interactive-ask.ts +4 -1
  477. package/scripts/rules/lane-park.ts +4 -2
  478. package/scripts/rules/lane-spilled-read.ts +90 -0
  479. package/scripts/rules/lane-split-park.ts +1 -1
  480. package/scripts/rules/lane-switch-main.ts +4 -4
  481. package/scripts/rules/last-prompt.ts +20 -47
  482. package/scripts/rules/oracle-consulted.ts +1 -1
  483. package/scripts/rules/permission-guards.ts +5 -1
  484. package/scripts/rules/piped-verification-shapes.ts +110 -0
  485. package/scripts/rules/piped-verification.ts +67 -17
  486. package/scripts/rules/poll-loop.ts +72 -9
  487. package/scripts/rules/pr-merge.ts +5 -5
  488. package/scripts/rules/pre-gate-cut.ts +3 -3
  489. package/scripts/rules/protected-files.ts +88 -24
  490. package/scripts/rules/reply-language.ts +3 -17
  491. package/scripts/rules/reply-prose.ts +34 -0
  492. package/scripts/rules/repo-admin.ts +132 -0
  493. package/scripts/rules/reviewer-foreground.ts +88 -0
  494. package/scripts/rules/round-one-check-related.ts +120 -0
  495. package/scripts/rules/rule-body-guard.ts +5 -15
  496. package/scripts/rules/rule-value-cache.ts +53 -5
  497. package/scripts/rules/rule-value-candidates.ts +84 -0
  498. package/scripts/rules/rule-value-cli.ts +59 -11
  499. package/scripts/rules/rule-value.ts +17 -4
  500. package/scripts/rules/run-tail-rule.ts +1 -1
  501. package/scripts/rules/sed-in-place.ts +196 -0
  502. package/scripts/rules/shell-segments.ts +10 -2
  503. package/scripts/rules/stop-rule-rows.ts +1 -13
  504. package/scripts/rules/stop-rules.ts +33 -58
  505. package/scripts/rules/test-declared-commit.ts +1 -1
  506. package/scripts/rules/third-party-write.ts +110 -23
  507. package/scripts/rules/timed-rerun.ts +1 -1
  508. package/scripts/rules/turn-filings.ts +90 -0
  509. package/scripts/rules/worktree-guard.ts +22 -7
  510. package/scripts/run/board/run-board-ending-reason.ts +6 -2
  511. package/scripts/run/board/run-board-fit.ts +24 -1
  512. package/scripts/run/board/run-board-found.ts +134 -0
  513. package/scripts/run/board/run-board-header.ts +8 -0
  514. package/scripts/run/board/run-board-labels.ts +11 -1
  515. package/scripts/run/board/run-board-lane-time.ts +1 -0
  516. package/scripts/run/board/run-board-link.ts +15 -2
  517. package/scripts/run/board/run-board-notes.ts +7 -1
  518. package/scripts/run/board/run-board-operation.ts +4 -2
  519. package/scripts/run/board/run-board-phase.ts +2 -5
  520. package/scripts/run/board/run-board-plan.ts +1 -1
  521. package/scripts/run/board/run-board-read.ts +46 -1
  522. package/scripts/run/board/run-board-reason.ts +20 -8
  523. package/scripts/run/board/run-board-render-checks.ts +3 -2
  524. package/scripts/run/board/run-board-render-drawn.ts +11 -0
  525. package/scripts/run/board/run-board-render-found.ts +157 -0
  526. package/scripts/run/board/run-board-render-lead.ts +9 -0
  527. package/scripts/run/board/run-board-render-legend.ts +19 -0
  528. package/scripts/run/board/run-board-render-tail.ts +1 -1
  529. package/scripts/run/board/run-board-render.ts +82 -18
  530. package/scripts/run/board/run-board-spin.ts +1 -3
  531. package/scripts/run/board/run-board-status.ts +2 -0
  532. package/scripts/run/board/run-board-tick.ts +39 -10
  533. package/scripts/run/carry/run-carry-ended.ts +3 -2
  534. package/scripts/run/carry/run-carry.ts +3 -2
  535. package/scripts/run/cut/run-cut-cli.ts +33 -133
  536. package/scripts/run/cut/run-cut-digest.ts +152 -0
  537. package/scripts/run/cut/run-cut-handoff.ts +52 -8
  538. package/scripts/run/cut/run-cut-report.ts +24 -3
  539. package/scripts/run/cut/run-cut-resume.ts +129 -0
  540. package/scripts/run/cut/run-cut-review.ts +105 -0
  541. package/scripts/run/cut/run-cut.ts +18 -7
  542. package/scripts/run/detached-launch.ts +1 -1
  543. package/scripts/run/entry/run-entry-cli.ts +88 -19
  544. package/scripts/run/entry/run-entry-spill.ts +83 -0
  545. package/scripts/run/entry/run-entry-stop.ts +5 -1
  546. package/scripts/run/entry/run-entry-trust.ts +91 -0
  547. package/scripts/run/event/run-event-baseline.ts +56 -0
  548. package/scripts/run/event/run-event-filed.ts +9 -3
  549. package/scripts/run/event/run-event-overrun.ts +197 -0
  550. package/scripts/run/event/run-event-render.ts +4 -0
  551. package/scripts/run/event/run-event-stream.ts +25 -3
  552. package/scripts/run/hold/run-stop-notify.ts +3 -8
  553. package/scripts/run/merge/run-merge-cap.ts +57 -0
  554. package/scripts/run/merge/run-merge-cli.ts +66 -23
  555. package/scripts/run/merge/run-merge-outcome.ts +2 -0
  556. package/scripts/run/merge/run-merge-ship.ts +13 -3
  557. package/scripts/run/merge/run-merge-signals.ts +40 -23
  558. package/scripts/run/merge/run-merge-steps.ts +56 -36
  559. package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
  560. package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
  561. package/scripts/run/merge/run-merge.ts +41 -14
  562. package/scripts/run/review/run-review-cli.ts +306 -20
  563. package/scripts/run/review/run-review-early-push.ts +121 -0
  564. package/scripts/run/review/run-review-join-read.ts +94 -0
  565. package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
  566. package/scripts/run/review/run-review-pinned-join.ts +216 -0
  567. package/scripts/run/review/run-review-pinned-steps.ts +383 -0
  568. package/scripts/run/review/run-review-pinned-tree.ts +37 -0
  569. package/scripts/run/review/run-review-pinned.ts +53 -0
  570. package/scripts/run/review/run-review-steps.ts +70 -34
  571. package/scripts/run/review/run-review.ts +26 -6
  572. package/scripts/run/run-parent-cut-hook.ts +3 -4
  573. package/scripts/run/run-prep.ts +1 -0
  574. package/scripts/run/run-stage-read.ts +2 -6
  575. package/scripts/run/run-step-cli.ts +17 -5
  576. package/scripts/run/run-step-lane-plan.ts +27 -0
  577. package/scripts/run/run-step-section.ts +26 -7
  578. package/scripts/run/run-step.ts +5 -2
  579. package/scripts/run/ship/run-ship-args.ts +19 -0
  580. package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
  581. package/scripts/run/ship/run-ship-cli.ts +131 -39
  582. package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
  583. package/scripts/run/ship/run-ship-detach.ts +10 -4
  584. package/scripts/run/ship/run-ship-handover.ts +106 -0
  585. package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
  586. package/scripts/run/ship/run-ship-lap.ts +63 -0
  587. package/scripts/run/ship/run-ship-next.ts +27 -26
  588. package/scripts/run/ship/run-ship-overlap.ts +8 -3
  589. package/scripts/run/ship/run-ship-place.ts +9 -2
  590. package/scripts/run/ship/run-ship-planned.ts +92 -0
  591. package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
  592. package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
  593. package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
  594. package/scripts/run/ship/run-ship-preflight.ts +16 -4
  595. package/scripts/run/ship/run-ship-probe.ts +23 -9
  596. package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
  597. package/scripts/run/ship/run-ship-relaunch.ts +51 -0
  598. package/scripts/run/ship/run-ship-return.ts +103 -29
  599. package/scripts/run/ship/run-ship-review-reason.ts +24 -0
  600. package/scripts/run/ship/run-ship-review-steps.ts +230 -59
  601. package/scripts/run/ship/run-ship-review.ts +171 -51
  602. package/scripts/run/ship/run-ship-reviewer.ts +138 -42
  603. package/scripts/run/ship/run-ship-scoped.ts +39 -10
  604. package/scripts/run/ship/run-ship-stage.ts +49 -13
  605. package/scripts/run/ship/run-ship-steps.ts +24 -4
  606. package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
  607. package/scripts/run/ship/run-ship-stop-context.ts +50 -13
  608. package/scripts/run/ship/run-ship-sync.ts +171 -30
  609. package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
  610. package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
  611. package/scripts/run/ship/run-ship-waiter.ts +114 -0
  612. package/scripts/run/ship/run-ship.ts +16 -2
  613. package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
  614. package/scripts/run/wake/run-wake-cli.ts +6 -113
  615. package/scripts/run/wake/run-wake-code.ts +53 -11
  616. package/scripts/run/wake/run-wake-driver.ts +5 -3
  617. package/scripts/run/wake/run-wake-finish.ts +137 -0
  618. package/scripts/run/wake/run-wake-loop.ts +5 -3
  619. package/scripts/run/wake/run-wake-ports.ts +11 -3
  620. package/scripts/run/wake/run-wake-session.ts +2 -0
  621. package/scripts/run/wake/run-wake.ts +3 -2
  622. package/scripts/safe-chain/ci-installer-pin.ts +49 -8
  623. package/scripts/safe-chain/project-config.ts +3 -2
  624. package/scripts/security/secretlint-scan.ts +42 -9
  625. package/scripts/security/security-audit-provision-main.ts +9 -1
  626. package/scripts/security/security-audit-provision.ts +1 -1
  627. package/scripts/security/security-audit.ts +21 -1
  628. package/scripts/security/sonar-file.ts +10 -5
  629. package/scripts/split/split-assess.ts +12 -3
  630. package/scripts/split/split-early-notice.ts +116 -0
  631. package/scripts/split/split-measure.ts +20 -2
  632. package/scripts/split/split-resolution.ts +107 -0
  633. package/scripts/test/direct-importers.ts +185 -0
  634. package/scripts/test/pure-files-entries.ts +83 -0
  635. package/scripts/test/related-heavy-tests.ts +81 -0
  636. package/scripts/test/test-no-base.ts +88 -0
  637. package/scripts/test/test-pinned-cache-logic.ts +363 -0
  638. package/scripts/test/test-pinned-cache.ts +337 -0
  639. package/scripts/test/test-pinned-emit.ts +49 -0
  640. package/scripts/test/test-pinned-functions.ts +116 -0
  641. package/scripts/test/test-pinned-ledger.ts +80 -0
  642. package/scripts/test/test-pinned-logic.ts +208 -20
  643. package/scripts/test/test-pinned-main.ts +84 -18
  644. package/scripts/test/test-pinned-pool.ts +73 -0
  645. package/scripts/test/test-pinned-probe.ts +254 -0
  646. package/scripts/test/test-pinned-prune-logic.ts +118 -0
  647. package/scripts/test/test-pinned-reach.ts +194 -0
  648. package/scripts/test/test-pinned-record.ts +196 -0
  649. package/scripts/test/test-pinned-rewire.ts +148 -0
  650. package/scripts/test/test-pinned-spawn.ts +81 -0
  651. package/scripts/test/test-pinned-stage.ts +70 -0
  652. package/scripts/test/test-pinned.ts +258 -47
  653. package/scripts/test/test-reach-cli.ts +61 -0
  654. package/scripts/test/test-read-map.ts +35 -11
  655. package/scripts/test/test-read-recording.ts +17 -5
  656. package/scripts/test/test-read-trace.ts +26 -8
  657. package/scripts/test/test-red-logic.ts +46 -7
  658. package/scripts/test/test-red.ts +34 -14
  659. package/scripts/test/test-related-claim.ts +37 -0
  660. package/scripts/test/test-related-main.ts +4 -1
  661. package/scripts/test/test-related-moved.ts +91 -0
  662. package/scripts/test/test-related-record.ts +43 -23
  663. package/scripts/test/test-related-scope.ts +99 -34
  664. package/scripts/test/test-related-synced.ts +135 -0
  665. package/scripts/test/test-related-touched.ts +84 -0
  666. package/scripts/test/test-related.ts +167 -83
  667. package/scripts/test/test-unit-guard.ts +28 -5
  668. package/scripts/test/unit-durations.ts +41 -0
  669. package/scripts/test/unit-worker-share.ts +5 -4
  670. package/scripts/test/vitest-cache-directory.ts +19 -0
  671. package/scripts/test/vitest-cli-excludes.ts +14 -0
  672. package/scripts/test/vitest-failure-summary.ts +111 -0
  673. package/scripts/test/whole-suite-files.ts +12 -1
  674. package/scripts/time-runtime/time-batch-guard.ts +42 -4
  675. package/scripts/time-runtime/time-bundle-call.ts +1 -1
  676. package/scripts/time-runtime/time-bundles.ts +36 -5
  677. package/scripts/time-runtime/time-markers.ts +2 -2
  678. package/scripts/time-runtime/time-search-call.ts +38 -0
  679. package/scripts/time-runtime/time-shell.ts +1 -1
  680. package/scripts/time-runtime/time-transcript-line.ts +17 -5
  681. package/scripts/version/development-engines-verdict.ts +124 -0
  682. package/scripts/version/latest-corepack.ts +2 -0
  683. package/templates/gitignore +2 -0
  684. package/templates/workflows/ci.yml +41 -0
  685. package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
  686. package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
  687. package/dist/hooks/chunk-TUIFS5E7.js +0 -72
  688. package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
  689. package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
  690. package/scripts/rules/filing-cap.ts +0 -139
@@ -16,22 +16,30 @@ import {
16
16
  read_string,
17
17
  string_array_schema,
18
18
  string_record_schema,
19
+ time_batch_guard,
20
+ time_bundle_call,
19
21
  time_density_hook,
20
22
  time_hook_transcript,
23
+ time_search_call,
21
24
  time_shell,
25
+ time_spans,
22
26
  time_transcript_line,
23
27
  transcript_record,
24
28
  vscode_settings_schema
25
- } from "./chunk-PT5R6UJD.js";
29
+ } from "./chunk-Z6DQJKH3.js";
26
30
  import {
27
31
  BLOCKED_BY_COUNT_FIELD,
28
32
  BLOCKED_BY_FIELD,
29
33
  FULL_PAGE_QUERY,
30
34
  agent_role_profile,
31
35
  agent_session_role,
36
+ changed_paths,
32
37
  detached_launch,
33
38
  distributed_paths,
39
+ document_section,
40
+ find_package_directory,
34
41
  forget_pr_numbers,
42
+ gh_author_association,
35
43
  gh_failure,
36
44
  git_gh_api_path,
37
45
  git_gh_conditional,
@@ -49,24 +57,24 @@ import {
49
57
  parse_json_object_or_undefined,
50
58
  read_blocked_by,
51
59
  read_json_listing,
60
+ read_listed_pull,
52
61
  read_pull,
62
+ read_pull_or_listed,
53
63
  require_pr_number,
54
64
  run_hold,
55
65
  session_language,
56
66
  stamp_record,
57
67
  to_gh_state
58
- } from "./chunk-YCDOIR3I.js";
68
+ } from "./chunk-3GKE653K.js";
59
69
  import {
60
- GIT_LOCATION_VARIABLES,
61
70
  PACKAGE_JSON,
62
71
  ancestor_directories,
63
72
  cli_flags,
64
73
  cost_transcript,
65
74
  cost_usage,
75
+ durable_state,
66
76
  file_reader,
67
- git_command,
68
- git_common_directory,
69
- git_spawn_sync,
77
+ issue_citation,
70
78
  issue_cite,
71
79
  issue_number_shape,
72
80
  json_value,
@@ -76,15 +84,17 @@ import {
76
84
  own_session,
77
85
  package_manifest,
78
86
  process_identity,
79
- repo_discovery,
80
- repo_origin,
81
87
  run_carry,
82
88
  run_event_stream,
83
89
  run_event_stream_emit,
84
90
  run_ship_stage,
85
91
  session_cite
86
- } from "./chunk-WR2KQAYZ.js";
92
+ } from "./chunk-7SIUNQXK.js";
87
93
  import {
94
+ session_owner_pin
95
+ } from "./chunk-VVNYJWEI.js";
96
+ import {
97
+ GIT_LOCATION_VARIABLES,
88
98
  MS_PER_MINUTE,
89
99
  MS_PER_SECOND,
90
100
  PACKAGE_DIR,
@@ -92,9 +102,16 @@ import {
92
102
  PROBE_TIMEOUT_MS,
93
103
  PROJECT_ROOT,
94
104
  error_text,
105
+ git_command,
106
+ git_common_directory,
107
+ git_diff_reads,
108
+ git_location_environment,
109
+ git_spawn_sync,
95
110
  package_path,
111
+ repo_discovery,
112
+ repo_origin,
96
113
  stamp_file
97
- } from "./chunk-AZX4FGQ3.js";
114
+ } from "./chunk-45CJJOO7.js";
98
115
 
99
116
  // scripts/josh/hook-decision.ts
100
117
  import { text } from "node:stream/consumers";
@@ -186,10 +203,10 @@ function record_refusal(target, now_ms) {
186
203
  }
187
204
  }
188
205
  function create_refusal_stamp(prefix) {
189
- function path15(transcript_path) {
206
+ function path19(transcript_path) {
190
207
  return stamp_file.stamp_path(prefix, transcript_path);
191
208
  }
192
- return { path: path15, last_ms: last_refusal_ms, record: record_refusal };
209
+ return { path: path19, last_ms: last_refusal_ms, record: record_refusal };
193
210
  }
194
211
  var { load_environment_file } = josh_environment_file;
195
212
  var NO_OUTCOME = { reason: void 0, notice: void 0, fault: void 0 };
@@ -245,10 +262,10 @@ function create_transcript_guard(spec) {
245
262
  const stamp = create_refusal_stamp(spec.prefix);
246
263
  const notify_stamp = spec.notify ? create_refusal_stamp(spec.notify.prefix) : void 0;
247
264
  const parts = { spec, stamp, notify_stamp };
248
- function is_enabled() {
265
+ function is_enabled3() {
249
266
  return is_switch_enabled(spec.switch_key);
250
267
  }
251
- function refusal_path(transcript_path) {
268
+ function refusal_path2(transcript_path) {
252
269
  return stamp.path(transcript_path);
253
270
  }
254
271
  function outcome(raw_payload, now_ms = Date.now()) {
@@ -261,7 +278,7 @@ function create_transcript_guard(spec) {
261
278
  function refusal(raw_payload, now_ms = Date.now()) {
262
279
  return outcome(raw_payload, now_ms).reason;
263
280
  }
264
- return { is_enabled, outcome, refusal, refusal_path };
281
+ return { is_enabled: is_enabled3, outcome, refusal, refusal_path: refusal_path2 };
265
282
  }
266
283
  function emit_outcome(outcome) {
267
284
  const { reason, notice, fault } = outcome;
@@ -356,273 +373,119 @@ async function bounded_map(items, limit, worker) {
356
373
  }
357
374
  var bounded_pool = { bounded_map, pool_width };
358
375
 
359
- // scripts/rules/tail-commands.ts
360
- function is_bash_use(block) {
361
- return block.type === cost_blocks.TOOL_USE_TYPE && block.name === cost_blocks.BASH_TOOL;
362
- }
363
- function bash_commands_of(line) {
364
- const parsed = time_transcript_line.parse_line(line);
365
- if (parsed === void 0) return [];
366
- return parsed.blocks.filter((block) => is_bash_use(block)).map((block) => time_shell.bash_command(block.input));
367
- }
368
- function prior_bash_commands(tail) {
369
- return tail.split("\n").flatMap((line) => bash_commands_of(line));
370
- }
371
- function answered(text2, is_call, token) {
372
- const blocks = time_transcript_line.parse_text(text2).flatMap((line) => line.blocks);
373
- const call_ids = new Set(
374
- blocks.filter((block) => is_call(time_shell.bash_command(block.input))).map((block) => block.id)
375
- );
376
- return blocks.some((block) => call_ids.has(block.result_id) && block.token_lines.includes(token));
377
- }
378
- var tail_commands = { answered, is_bash_use, prior_bash_commands };
376
+ // scripts/rules/stop-decision.ts
377
+ var RECOMMENDED_LABEL = /^recommended[*_]*:/iu;
378
+ var ALTERNATIVE_LABEL = /^alternative[*_]*:/iu;
379
+ var LEADING_EMPHASIS = /^[*_]+/u;
380
+ var TRAILING_EMPHASIS = /(?<![*_])[*_]+$/u;
381
+ var BULLET = /^[-*]\s+/u;
382
+ var QUESTION_ENDING = /(?:[??]|か|かな|かね)[\s。.]*$/u;
383
+ function unwrapped(text2) {
384
+ return text2.trim().replace(LEADING_EMPHASIS, "").replace(TRAILING_EMPHASIS, "").trim();
385
+ }
386
+ function bare_line(line) {
387
+ return unwrapped(line.trim().replace(BULLET, ""));
388
+ }
389
+ function choice_of(message, label) {
390
+ const line = message.split("\n").map((one) => bare_line(one)).find((one) => label.test(one));
391
+ const choice = line === void 0 ? void 0 : unwrapped(line.replace(label, ""));
392
+ return choice === "" ? void 0 : choice;
393
+ }
394
+ function decision_of(message) {
395
+ const recommended = choice_of(message, RECOMMENDED_LABEL);
396
+ if (recommended === void 0) return void 0;
397
+ return { recommended, alternative: choice_of(message, ALTERNATIVE_LABEL) };
398
+ }
399
+ function is_question(prompt) {
400
+ return QUESTION_ENDING.test(prompt.trim());
401
+ }
402
+ var DECISION_REASON = "\u26D4 stop decision: this working tree is held by your run and your reply names no decision. If the run waits on a person, end the reply with a `Recommended: <choice>` line and, where there is one, an `Alternative: <choice>` line \u2014 the hook sends the `confirmation` Telegram from them. If it waits on nobody, continue the run, or say in one line that nothing is awaited. Do not repeat your previous reply.";
403
+ var stop_decision = { DECISION_REASON, decision_of, is_question };
379
404
 
380
- // scripts/rules/git-argv.ts
381
- var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/u;
382
- var WRAPPER_WORDS = /* @__PURE__ */ new Set([
383
- "pnpm",
384
- "npx",
385
- "exec",
386
- "run",
387
- "sudo",
388
- "command",
389
- "env",
390
- "time"
391
- ]);
392
- var FLAGGED_WRAPPERS = /* @__PURE__ */ new Set(["time"]);
393
- var WRAPPER_VALUE_FLAGS = /* @__PURE__ */ new Set(["-o", "-f", "--output", "--format"]);
394
- var SUBSHELL_OPEN = "(";
395
- var VALUE_TAKING_GLOBALS = /* @__PURE__ */ new Set([
396
- "-C",
397
- "-c",
398
- "--git-dir",
399
- "--work-tree",
400
- "--namespace",
401
- "--exec-path",
402
- "--super-prefix"
403
- ]);
404
- var FLAG_PREFIX = "-";
405
- var GIT_COMMAND = "git";
406
- var NEXT = 1;
407
- var AFTER_VALUE = 2;
408
- var WORD = /(?:\\.|'[^']*'|"(?:\\.|[^"\\])*"|\S)+/gsu;
409
- function words_of(segment) {
410
- return segment.match(WORD) ?? [];
411
- }
412
- var PATH_SEPARATOR = "/";
413
- function program_name(word) {
414
- return word.slice(word.lastIndexOf(PATH_SEPARATOR) + NEXT);
415
- }
416
- function is_wrapper_token(word) {
417
- return word === SUBSHELL_OPEN || ENV_ASSIGNMENT.test(word) || WRAPPER_WORDS.has(program_name(word));
418
- }
419
- function wrapper_flag_width(word = "") {
420
- if (!word.startsWith(FLAG_PREFIX)) return 0;
421
- return WRAPPER_VALUE_FLAGS.has(word) ? AFTER_VALUE : NEXT;
422
- }
423
- function after_wrapper_word(words, index) {
424
- let next = index + NEXT;
425
- if (!FLAGGED_WRAPPERS.has(program_name(words[index] ?? ""))) return next;
426
- let width = wrapper_flag_width(words[next]);
427
- while (width > 0) {
428
- next += width;
429
- width = wrapper_flag_width(words[next]);
430
- }
431
- return next;
432
- }
433
- function after_wrapper(words) {
434
- let index = 0;
435
- while (index < words.length && is_wrapper_token(words[index] ?? "")) {
436
- index = after_wrapper_word(words, index);
437
- }
438
- return index;
405
+ // scripts/discovery/repo-party.ts
406
+ var FIRST_PARTY = "first-party";
407
+ var THIRD_PARTY = "third-party";
408
+ var UNKNOWN = "unknown";
409
+ var GH_REPO_ENV_KEY = "GH_REPO";
410
+ var GH_REPO_PATH_SEGMENTS = 2;
411
+ function classify(session_owner, target) {
412
+ if (session_owner === void 0 || target === void 0) return UNKNOWN;
413
+ return session_owner.toLowerCase() === target.toLowerCase() ? FIRST_PARTY : THIRD_PARTY;
439
414
  }
440
- function global_step(flag) {
441
- return VALUE_TAKING_GLOBALS.has(flag) ? AFTER_VALUE : NEXT;
415
+ function parse_owner_repo(owner_repo) {
416
+ return repo_origin.parse_origin_url(`https://${repo_origin.GITHUB_HOST}/${owner_repo}`);
442
417
  }
443
- function after_globals(words, start) {
444
- let index = start;
445
- while (index < words.length) {
446
- const flag = words[index] ?? "";
447
- if (!flag.startsWith(FLAG_PREFIX)) return index;
448
- index += global_step(flag);
449
- }
450
- return index;
418
+ function target_owner(owner_repo) {
419
+ return parse_owner_repo(owner_repo)?.owner;
451
420
  }
452
- function program_of(segment) {
453
- const words = words_of(segment);
454
- const command_index = after_wrapper(words);
455
- const word = words[command_index];
456
- if (word === void 0) return void 0;
457
- return { name: program_name(word), args: words.slice(command_index + NEXT) };
421
+ function current_owner(repository_path = process.cwd()) {
422
+ return session_owner_pin.pinned(repository_path) ?? repo_discovery.resolve_current_owner(repository_path);
458
423
  }
459
- function arguments_of(segment, command) {
460
- const call = program_of(segment);
461
- return call?.name === command ? call.args : void 0;
424
+ function selector_repository(selector) {
425
+ return parse_owner_repo(selector.split("/").slice(-GH_REPO_PATH_SEGMENTS).join("/"));
462
426
  }
463
- function parse(segment) {
464
- const words = arguments_of(segment, GIT_COMMAND);
465
- if (words === void 0) return void 0;
466
- const subcommand_index = after_globals(words, 0);
467
- return { subcommand: words[subcommand_index], args: words.slice(subcommand_index + NEXT) };
427
+ function selector_owner(selector) {
428
+ return selector_repository(selector)?.owner;
468
429
  }
469
- var SHORT_CLUSTER = /^-[A-Za-z]+$/u;
470
- function short_cluster_has(token, letters) {
471
- if (!SHORT_CLUSTER.test(token)) return false;
472
- const cluster = token.slice(FLAG_PREFIX.length);
473
- for (let index = 0; index < letters.length; index += 1) {
474
- if (cluster.includes(letters.charAt(index))) return true;
475
- }
476
- return false;
430
+ function remote_repositories(repository_path) {
431
+ return repo_discovery.read_remote_urls(repository_path).map((url) => repo_origin.parse_origin_url(url));
477
432
  }
478
- var git_argv = { arguments_of, parse, program_of, short_cluster_has };
479
-
480
- // scripts/rules/shell-segments.ts
481
- var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
482
- var SEGMENT_PARTS = new RegExp(`(${SEGMENT_SEPARATOR.source})`, "u");
483
- var COMMAND_SEPARATOR = /&&|\|\||;|\n|\|&?|(?<![<>])&(?!>)/u;
484
- function segments_of(command) {
485
- return command.split(SEGMENT_SEPARATOR).map((segment) => segment.trim());
433
+ function placeholder_repositories(repository_path, environment) {
434
+ const gh_repo = environment[GH_REPO_ENV_KEY];
435
+ return gh_repo === void 0 || gh_repo === "" ? remote_repositories(repository_path) : [selector_repository(gh_repo)];
486
436
  }
487
- function commands_of(command) {
488
- return command.split(COMMAND_SEPARATOR).map((segment) => segment.trim());
437
+ function placeholder_owners(repository_path = process.cwd(), environment = process.env) {
438
+ return placeholder_repositories(repository_path, environment).map((identity) => identity?.owner);
489
439
  }
490
- var COMMAND_SEPARATORS = new RegExp(COMMAND_SEPARATOR.source, "gu");
491
- function quote_aware_commands_of(command) {
492
- const plain = time_shell.blanked(command);
493
- if (plain === void 0) return commands_of(command);
494
- const cuts = [...plain.matchAll(COMMAND_SEPARATORS)].map((match) => ({
495
- begin: match.index,
496
- end: match.index + match[0].length
497
- }));
498
- const starts = [0, ...cuts.map((cut) => cut.end)];
499
- const stops = cuts.map((cut) => cut.begin);
500
- return starts.map((start, index) => command.slice(start, stops[index] ?? command.length).trim());
440
+ function is_same_repository(left, right) {
441
+ if (left === void 0 || classify(left.owner, right.owner) !== FIRST_PARTY) return false;
442
+ return left.repo.toLowerCase() === right.repo.toLowerCase();
501
443
  }
502
- function parts_of(command) {
503
- return command.split(SEGMENT_PARTS);
444
+ function sole_repository(candidates) {
445
+ const [first, ...rest] = candidates;
446
+ if (first === void 0) return void 0;
447
+ return rest.every((candidate) => is_same_repository(candidate, first)) ? first : void 0;
504
448
  }
505
- function first_of(command) {
506
- const plain = time_shell.blanked(command);
507
- if (plain === void 0) return void 0;
508
- const cut = plain.search(SEGMENT_SEPARATOR);
509
- return (cut === -1 ? command : command.slice(0, cut)).trim();
449
+ function current_repository(repository_path = process.cwd(), environment = process.env) {
450
+ return sole_repository(placeholder_repositories(repository_path, environment));
510
451
  }
511
- function is_josh_command(segment, names) {
512
- const named = time_shell.josh_command_of(segment);
513
- if (!named.startsWith(time_shell.JOSH_PREFIX)) return false;
514
- return names.has(named.slice(time_shell.JOSH_PREFIX.length));
452
+ function is_own_repository(target, own) {
453
+ return is_same_repository(own, target);
515
454
  }
516
- var shell_segments = {
517
- commands_of,
518
- first_of,
519
- is_josh_command,
520
- parts_of,
521
- quote_aware_commands_of,
522
- segments_of
455
+ var repo_party = {
456
+ FIRST_PARTY,
457
+ THIRD_PARTY,
458
+ UNKNOWN,
459
+ classify,
460
+ current_owner,
461
+ current_repository,
462
+ is_own_repository,
463
+ placeholder_owners,
464
+ selector_owner,
465
+ sole_repository,
466
+ target_owner
523
467
  };
524
468
 
525
- // scripts/rules/bash-triggers.ts
526
- function on_bash_command(is_match) {
527
- return function is_trigger(call) {
528
- if (call.name !== cost_blocks.BASH_TOOL) return false;
529
- return is_match(time_shell.bash_command(call.input));
530
- };
531
- }
532
- var ISSUE_CREATE_COMMAND = /\bgh\s+(?:\S+\s+)*?issue\s+create\b/u;
533
- var ISSUES_ENDPOINT = /repos\/[^\s'"]*\/issues(?=$|["'\s])/u;
534
- var TITLE_FIELD = /(?:-f|-F|--field|--raw-field)\s*'?title=/u;
535
- var GH_API_COMMAND = /\bgh\s+api\b/u;
536
- var SHELL_SCRIPT_RUNNER = /\b(?:(?:ba|z|da|k)?sh\s+(?:-\w+\s+)*-\w*c\b|eval\b)/u;
537
- function spoken_line(command) {
538
- if (time_shell.has_heredoc(command)) return command;
539
- const plain = time_shell.unquoted(command);
540
- return SHELL_SCRIPT_RUNNER.test(plain) ? command : plain;
541
- }
542
- var LEADING_SHELL_SCRIPT_RUNNER = new RegExp(`^${SHELL_SCRIPT_RUNNER.source}`, "u");
543
- var QUOTE_MARK = /["']/gu;
544
- function shell_script_of(segment) {
545
- const call = git_argv.program_of(segment);
546
- if (call === void 0) return void 0;
547
- const spoken = [call.name, ...call.args].join(" ");
548
- const runner = LEADING_SHELL_SCRIPT_RUNNER.exec(spoken);
549
- if (runner === null) return void 0;
550
- return spoken.slice(runner[0].length).replaceAll(QUOTE_MARK, "").trim();
551
- }
552
- function is_direct_filing(command) {
553
- const spoken = spoken_line(command);
554
- if (ISSUE_CREATE_COMMAND.test(spoken)) return true;
555
- return GH_API_COMMAND.test(spoken) && ISSUES_ENDPOINT.test(command) && TITLE_FIELD.test(command);
556
- }
557
- var FILING_NAMES = /* @__PURE__ */ new Set(["issue:file"]);
558
- function is_issue_filing(command) {
559
- return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, FILING_NAMES));
560
- }
561
- var bash_triggers = { is_direct_filing, is_issue_filing, on_bash_command, shell_script_of };
562
-
563
- // scripts/rules/filing-cap.ts
564
- var FILING_CAP = 10;
565
- var CAP_REACHED = FILING_CAP;
566
- function is_filing_use(block) {
567
- if (!tail_commands.is_bash_use(block)) return false;
568
- return bash_triggers.is_issue_filing(time_shell.bash_command(block.input));
569
- }
570
- function collect_block(block, found) {
571
- if (block.result_id !== "" && block.is_error === true) found.failures.add(block.result_id);
572
- if (is_filing_use(block)) found.filing_ids.push(block.id);
573
- }
574
- function collect_line(line, found) {
575
- const parsed = time_transcript_line.parse_line(line);
576
- if (parsed === void 0) return;
577
- for (const block of parsed.blocks) collect_block(block, found);
578
- }
579
- function scan_tail(tail) {
580
- const found = { filing_ids: [], failures: /* @__PURE__ */ new Set() };
581
- for (const line of tail.split("\n")) collect_line(line, found);
582
- return found;
583
- }
584
- function prior_filing_count(tail) {
585
- const { filing_ids, failures } = scan_tail(tail);
586
- return filing_ids.filter((id) => !failures.has(id)).length;
469
+ // scripts/rules/tail-commands.ts
470
+ function is_bash_use(block) {
471
+ return block.type === cost_blocks.TOOL_USE_TYPE && block.name === cost_blocks.BASH_TOOL;
587
472
  }
588
- function is_prompt_line(line) {
473
+ function bash_commands_of(line) {
589
474
  const parsed = time_transcript_line.parse_line(line);
590
- if (parsed?.type !== "user") return false;
591
- return parsed.blocks.every((block) => block.result_id === "");
592
- }
593
- function current_turn(tail) {
594
- const lines = tail.split("\n");
595
- const start = lines.findLastIndex((line) => is_prompt_line(line));
596
- return lines.slice(Math.max(start, 0)).join("\n");
475
+ if (parsed === void 0) return [];
476
+ return parsed.blocks.filter((block) => is_bash_use(block)).map((block) => time_shell.bash_command(block.input));
597
477
  }
598
- function turn_filing_count(tail) {
599
- return prior_filing_count(current_turn(tail));
478
+ function prior_bash_commands(tail) {
479
+ return tail.split("\n").flatMap((line) => bash_commands_of(line));
600
480
  }
601
- function decide(_call, run2) {
602
- return prior_filing_count(time_density_hook.read_tail(run2.transcript)) >= CAP_REACHED;
481
+ function answered(text2, is_call, token) {
482
+ const blocks = time_transcript_line.parse_text(text2).flatMap((line) => line.blocks);
483
+ const call_ids = new Set(
484
+ blocks.filter((block) => is_call(time_shell.bash_command(block.input))).map((block) => block.id)
485
+ );
486
+ return blocks.some((block) => call_ids.has(block.result_id) && block.token_lines.includes(token));
603
487
  }
604
- var FILING_CAP_REASON = `\u26D4 filing cap: this run has already filed ${String(FILING_CAP)} Issues, the per-run ceiling. Stop filing and report; a further Issue is a person's call. It fires on every occurrence (\`.claude/skills/workflow-commands/observation-filing.md\`).`;
605
- var ROW = {
606
- id: "filing-cap",
607
- is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
608
- reason: FILING_CAP_REASON,
609
- decide,
610
- about: {
611
- title: "The 10-Issue filing cap per run",
612
- topic: "`.claude/skills/workflow-commands/prerequisite.md`, `observation-filing.md`",
613
- fires: "the 11th `pnpm josh issue:file` in a run that already has 10 filings not refused; failed calls do not count. Every occurrence",
614
- quiet: "fewer than 10 filings not refused"
615
- }
616
- };
617
- var filing_cap = {
618
- FILING_CAP,
619
- FILING_CAP_REASON,
620
- ROW,
621
- current_turn,
622
- is_prompt_line,
623
- prior_filing_count,
624
- turn_filing_count
625
- };
488
+ var tail_commands = { answered, is_bash_use, prior_bash_commands };
626
489
 
627
490
  // scripts/backlog/backlog-budget.ts
628
491
  var MINUTES_PER_HOUR = 60;
@@ -707,7 +570,7 @@ function answer_decision(input) {
707
570
  if (input.answer === "candidates") return { verdict: RUN_VERDICT, reason: run_reason(input) };
708
571
  return FIXED_DECISIONS[input.answer] ?? idle_decision(input);
709
572
  }
710
- function decide2(input) {
573
+ function decide(input) {
711
574
  return max_decision(input) ?? answer_decision(input);
712
575
  }
713
576
  var backlog_budget = {
@@ -723,7 +586,7 @@ var backlog_budget = {
723
586
  TRIAGE_VERDICT,
724
587
  WAITING_ON,
725
588
  WATCH_VERDICT,
726
- decide: decide2,
589
+ decide,
727
590
  draining_reason,
728
591
  idle_expired_reason,
729
592
  idle_watch_reason,
@@ -735,171 +598,660 @@ var backlog_budget = {
735
598
  taken_by
736
599
  };
737
600
 
738
- // scripts/cost-runtime/cost-pricing.ts
739
- var CACHE_WRITE_5M_MULTIPLIER = 1.25;
740
- var CACHE_WRITE_1H_MULTIPLIER = 2;
741
- var DEFAULT_CACHE_READ_MULTIPLIER = 0.1;
742
- var HALVED_CACHE_READ_MULTIPLIER = 0.05;
743
- var QUARTERED_CACHE_READ_MULTIPLIER = 0.025;
744
- var TOKENS_PER_MILLION = 1e6;
745
- var LONG_PROMPT_SUFFIX = ":long-prompt";
746
- var LONG_PROMPT_THRESHOLD = 1e5;
747
- var MODEL_PRICES = {
748
- "claude-fable-5-1": {
749
- input: 10,
750
- output: 50,
751
- cache_read_multiplier: QUARTERED_CACHE_READ_MULTIPLIER
601
+ // scripts/run/cut/run-cut.ts
602
+ import { z as z4 } from "zod";
603
+
604
+ // scripts/run/cut/run-cut-digest.ts
605
+ import path3 from "node:path";
606
+
607
+ // scripts/delegation/duplicate-read-guard.ts
608
+ import { statSync } from "node:fs";
609
+
610
+ // scripts/lane/lane-guard-policy.ts
611
+ var DEFAULT_MODE = "refuse";
612
+ var OFF_MODE = "off";
613
+ var LANE_GUARD_POLICY = [
614
+ {
615
+ id: "investigation",
616
+ mode_in_lane_child: "refuse",
617
+ because: "the notice was measured not to move the reading \u2014 52% of lane cost stayed in main-line exploration \u2014 and the refusal fires once per accumulation, a round trip the child survives as it survives the rule row, so the child is refused like the main line"
752
618
  },
753
- "claude-mythos-5-1": {
754
- input: 10,
755
- output: 50,
756
- cache_read_multiplier: QUARTERED_CACHE_READ_MULTIPLIER
619
+ {
620
+ id: "batching",
621
+ mode_in_lane_child: "off",
622
+ because: "a refusal ends a child turn and every advisory notice was measured not to move the call density, so the guard is off and the density stays readable through `josh time:density`"
757
623
  },
758
- "claude-fable-5": { input: 10, output: 50 },
759
- "claude-mythos-5": { input: 10, output: 50 },
760
- "claude-opus-5-5": { input: 4, output: 20, cache_read_multiplier: HALVED_CACHE_READ_MULTIPLIER },
761
- "claude-opus-5": { input: 5, output: 25 },
762
- "claude-opus-4-8": { input: 5, output: 25 },
763
- "claude-opus-4-7": { input: 5, output: 25 },
764
- "claude-opus-4-6": { input: 5, output: 25 },
765
- "claude-sonnet-5-5": {
766
- input: 2,
767
- output: 10,
768
- cache_read_multiplier: HALVED_CACHE_READ_MULTIPLIER
624
+ {
625
+ id: "search-batching",
626
+ mode_in_lane_child: "refuse",
627
+ because: "the largest share of single-call pairs in lane children is a search chain (`grep` / `sed` issued one per turn, about 10,600 pairs in 14 days), and a refusal re-fires only every third single-call search turn, so one refused round trip (about $0.036) is repaid once it folds two later searches into one turn"
769
628
  },
770
- "claude-sonnet-5": { input: 2, output: 10 },
771
- "claude-sonnet-4-6": { input: 3, output: 15 },
772
- "claude-haiku-5-5": { input: 0.1, output: 0.5 },
773
- [`claude-haiku-5-5${LONG_PROMPT_SUFFIX}`]: { input: 0.5, output: 2.5 },
774
- "claude-haiku-4-5": { input: 1, output: 5 }
629
+ {
630
+ id: "duplicate-read",
631
+ mode_in_lane_child: "notice",
632
+ because: "a refusal ends a child turn, yet unchanged re-reads are measured in lane children above all, so the child is nudged to scroll up to the earlier read by a notice rather than killed"
633
+ },
634
+ {
635
+ id: "rule",
636
+ mode_in_lane_child: "refuse",
637
+ because: "it carries the lane-only rules a child depends on (`pre-gate-cut`, `implementation-cut`, `lane-park`) and the safety rules it must still obey, and a notice was rejected despite the collateral a refusal costs a batched sibling, because a cut carried as prose was measured never to fire"
638
+ }
639
+ ];
640
+ var KNOWN_GUARD_IDS = LANE_GUARD_POLICY.map((entry) => entry.id);
641
+ function entry_for(id) {
642
+ return LANE_GUARD_POLICY.find((entry) => entry.id === id.trim());
643
+ }
644
+ function mode_in_lane_child(id) {
645
+ return entry_for(id)?.mode_in_lane_child ?? DEFAULT_MODE;
646
+ }
647
+ function mode_here(id, directory = process.cwd(), source = process.env) {
648
+ const lane_mode = mode_in_lane_child(id);
649
+ if (lane_mode === DEFAULT_MODE) return DEFAULT_MODE;
650
+ return lane_child_marker.is_child_of(directory, source) ? lane_mode : DEFAULT_MODE;
651
+ }
652
+ function is_suppressed_here(id, directory = process.cwd(), source = process.env) {
653
+ return mode_here(id, directory, source) === OFF_MODE;
654
+ }
655
+ var lane_guard_policy = {
656
+ LANE_GUARD_POLICY,
657
+ KNOWN_GUARD_IDS,
658
+ entry_for,
659
+ mode_in_lane_child,
660
+ mode_here,
661
+ is_suppressed_here
775
662
  };
776
- var CONTEXT_MARKER = "[";
777
- var DATE_SUFFIX = /-\d{8}$/u;
778
- function known_id(model) {
779
- const base = model.split(CONTEXT_MARKER)[0] ?? model;
780
- return [base, base.replace(DATE_SUFFIX, "")].find((id) => Object.hasOwn(MODEL_PRICES, id));
781
- }
782
- function resolve_price(model) {
783
- const id = known_id(model);
784
- return id === void 0 ? void 0 : MODEL_PRICES[id];
785
- }
786
- function cache_read_multiplier(price) {
787
- return price.cache_read_multiplier ?? DEFAULT_CACHE_READ_MULTIPLIER;
663
+
664
+ // scripts/delegation/investigation-reads.ts
665
+ import path2 from "node:path";
666
+
667
+ // scripts/document/capped-print-part.ts
668
+ import path from "node:path";
669
+ var DIRECTORY_PREFIX = "josh-capped-print-";
670
+ var PART_PREFIX = "part-";
671
+ var PART_SUFFIX = ".md";
672
+ var PART_PATTERN = /^part-[1-9]\d*\.md$/u;
673
+ function part_name(index) {
674
+ return `${PART_PREFIX}${String(index + 1)}${PART_SUFFIX}`;
675
+ }
676
+ function is_part_file(absolute) {
677
+ const parent = path.basename(path.dirname(absolute));
678
+ return parent.startsWith(DIRECTORY_PREFIX) && PART_PATTERN.test(path.basename(absolute));
679
+ }
680
+ var capped_print_part = { DIRECTORY_PREFIX, part_name, is_part_file };
681
+
682
+ // scripts/git/changed-paths-sync.ts
683
+ function options_of(cwd) {
684
+ if (cwd === void 0) return {};
685
+ return { cwd, env: git_location_environment.location_free_environment() };
686
+ }
687
+ function default_branch_reference(options) {
688
+ const output = git_spawn_sync.read([...git_diff_reads.DEFAULT_BRANCH_ARGUMENTS], options);
689
+ const default_branch = git_diff_reads.default_branch_of(output);
690
+ const remote_reference = git_diff_reads.remote_reference_of(default_branch);
691
+ const check2 = git_diff_reads.reference_check_arguments(remote_reference);
692
+ return git_spawn_sync.read(check2, options) === void 0 ? default_branch : remote_reference;
693
+ }
694
+ function through_merge(base, reference, options) {
695
+ const incoming = git_spawn_sync.read(git_diff_reads.incoming_base_arguments(reference), options);
696
+ if (incoming === void 0) return base;
697
+ const kept = git_spawn_sync.read(git_diff_reads.later_base_arguments(base, incoming), options);
698
+ return git_diff_reads.later_base_of(kept, base);
699
+ }
700
+ function change_base(options) {
701
+ const reference = default_branch_reference(options);
702
+ const base = git_spawn_sync.read(git_diff_reads.merge_base_arguments(reference), options);
703
+ if (base === void 0) return reference;
704
+ return through_merge(base, reference, options);
705
+ }
706
+ function read_changed_paths(cwd) {
707
+ const options = options_of(cwd);
708
+ const tracked = git_spawn_sync.read(
709
+ git_diff_reads.names_since_arguments(change_base(options)),
710
+ options
711
+ );
712
+ const untracked = git_spawn_sync.read(git_diff_reads.UNTRACKED_ARGUMENTS, options);
713
+ return [tracked, untracked].flatMap((raw = "") => changed_paths.to_paths(raw));
788
714
  }
789
- function estimate_cost(totals, price) {
790
- const weighted_input = totals.input_tokens + totals.cache_write_5m_tokens * CACHE_WRITE_5M_MULTIPLIER + totals.cache_write_1h_tokens * CACHE_WRITE_1H_MULTIPLIER + totals.cache_read_tokens * cache_read_multiplier(price);
791
- return (weighted_input * price.input + totals.output_tokens * price.output) / TOKENS_PER_MILLION;
715
+ var changed_paths_sync = { read_changed_paths };
716
+
717
+ // scripts/delegation/delegation-policy.ts
718
+ var DELEGATE_VERDICT = "delegate";
719
+ var KEEP_VERDICT = "keep";
720
+ var INVESTIGATION_FILE_THRESHOLD = 3;
721
+ var INVESTIGATION_SEARCH_TURN_THRESHOLD = 3;
722
+ var INVESTIGATOR_AGENT = "investigator";
723
+ var INVESTIGATION_LIMIT = `${String(INVESTIGATION_FILE_THRESHOLD)} files read and not edited, or ${String(INVESTIGATION_SEARCH_TURN_THRESHOLD)} search turns, since the last delegated unit`;
724
+ var INVESTIGATION_STEP = `Dispatch the \`${INVESTIGATOR_AGENT}\` agent (\`kit:${INVESTIGATOR_AGENT}\` in a consumer) with \`run_in_background: false\`; it returns \`file:line\` citations, never the file text.`;
725
+ var DELEGATABLE_STEPS = [
726
+ {
727
+ name: "gate-fix",
728
+ does: "apply the fixes the verification gate already named \u2014 a lint rule, a type error, a spelling the dictionary rejects",
729
+ verifier: "`pnpm josh gate` is re-run; a wrong fix fails it again, and the failure names the file"
730
+ },
731
+ {
732
+ name: "epic-child",
733
+ // One row, two entry points: an epic's child and one named issue of a `backlogrun` are the same
734
+ // unit — same brief, same summary, same verifier. A second row for the batch would be the clone
735
+ // `CLAUDE.md` prohibits.
736
+ does: "run one child of a batch end to end in an isolated unit \u2014 an epic's child or one named issue of a `backlogrun` alike \u2014 plan, verification gate, PR, merge \u2014 and return only its summary to the parent loop",
737
+ verifier: "the parent reads the child's state from GitHub with `pnpm josh issue:state`, not from the summary; a child reported done but not merged is still open, which is the failure showing rather than a run continuing, and its own gate, `/code-review` and CI ran inside the unit before `followup` would touch the PR"
738
+ },
739
+ {
740
+ name: "followup-filing",
741
+ // The late-run follow-up filing chain — `issue:file` (which scouts, lints, labels and runs
742
+ // epic:bundle itself) → epic --add — is a dependency chain the batching guard
743
+ // correctly keeps one call per step, and what makes it expensive is the ~340k context it runs in
744
+ // at the tail of a run, not the step count. A fresh unit runs the same
745
+ // chain at a small context, and the review round cap already composed the finding text, so what
746
+ // the unit is handed is mechanical: run the commands and act on their deterministic answers. This
747
+ // is not the batching guard's business — delegation only moves the chain to a cheaper context.
748
+ does: "run the late-run follow-up filing chain in an isolated unit \u2014 issue:file (lint, labels, scout, epic:bundle), then epic --add \u2014 given the finding text the parent already composed, and return the new Issue number and the epic:bundle answer",
749
+ verifier: "the parent reads the filed Issue from GitHub with `pnpm josh issue:state <new>`, not the unit's summary; a follow-up reported filed but not created is still absent, the way `epic-child`'s reported-done-but-not-merged child is still open. The finding text came from the parent and `issue:file`'s steps are deterministic, so the unit's work is the mechanical execution `issue:state` confirms"
750
+ },
751
+ {
752
+ name: "implementation-unit",
753
+ // The Step 0 change list already decided *what* to change — that design stays in the main line,
754
+ // which is why `design` is rejected below. What is delegated is the *writing* of one unit of that
755
+ // list, and only a unit whose files are disjoint from every other dispatched unit: two subagents
756
+ // editing one file in parallel race on it. `pnpm josh fanout` confirms that disjointness
757
+ // mechanically, so the split is read off the file sets rather than judged.
758
+ does: "implement one file-disjoint unit of the Step 0 change list in an isolated subagent \u2014 the files it edits shared with no other dispatched unit, confirmed by `pnpm josh fanout` \u2014 and return the edited files, never a summary standing in for the diff; the main line keeps integration and the verification gate. Units that share a file, or an Issue that will not split, stay serial",
759
+ verifier: "the parent runs the whole integrated change through `pnpm josh gate` and a `/code-review` subagent it would run anyway \u2014 a unit's mistake fails the same gate and review a serial edit passes through, so delegating writes nothing the backstop does not already catch, and the `pnpm josh fanout` disjointness removes the one failure parallelism adds, two units colliding on a file"
760
+ },
761
+ {
762
+ name: "survey",
763
+ does: "read across many files and report where something appears \u2014 every reference to a symbol, which documents carry a marker",
764
+ verifier: "the reported locations are checked directly; a fabricated or missed location does not survive one `grep` of what it claimed"
765
+ },
766
+ {
767
+ name: "investigation",
768
+ // Not `survey`: that row reports *where* something appears and is checked by one `grep`, while
769
+ // this one reports *how the subject works* and is checked by opening the lines it cited. The two
770
+ // verifiers differ, so widening `survey` would put both in one field and lose the boundary below.
771
+ // **No ordinal.** `${N}rd` would print `1rd` for any other threshold, past a test that only looks
772
+ // for the numeral, so the count is phrased as a plain "N or more".
773
+ does: `read the Issue's subject to find out how it currently works \u2014 once ${String(INVESTIGATION_FILE_THRESHOLD)} or more files the run will not edit have to be read, or ${String(INVESTIGATION_SEARCH_TURN_THRESHOLD)} or more search turns run without an edit \u2014 and return the conclusion plus the \`file:line\` citations that support it, never the file text, which would put the cost back where it was. A throwaway probe script is written, run and deleted inside the unit, which returns its output alone`,
774
+ verifier: "the parent opens the cited lines; a conclusion those lines do not support fails there, and reading a handful of cited regions costs far less than redoing the reading. The unit reports what the code does and never what to change \u2014 deciding that is `diagnosis`, which stays kept, so a unit returning a root cause would be delegating the rejected row under this name"
775
+ },
776
+ {
777
+ name: "lane-failure-investigation",
778
+ // The `backlogrun` parent's counterpart of `investigation`. The parent
779
+ // orchestrates and never implements, yet it read a failed lane's logs, records and sources in its
780
+ // own context — about 150 grep / cat / sed calls on 2026-10-02 — and every later request of the
781
+ // batch re-read them. The verifier is `investigation`'s, and so is the boundary: the unit reports
782
+ // what happened, and what the parent does about it (park, re-dispatch, file) stays its own call.
783
+ does: "find out why a lane's child stopped, failed or conflicted \u2014 its output log, run records, PR checks and the files they name \u2014 and return the conclusion plus the `file:line` or log-line citations that support it, never the text read, so the batch parent's context stays the size its orchestration needs",
784
+ verifier: "the parent opens the cited lines and re-reads the child with `pnpm josh issue:state`; a conclusion those do not support fails there. The unit reports what happened and never what to do \u2014 parking, re-dispatching or filing stays with the parent, so a wrong account is caught before it is acted on"
785
+ }
786
+ ];
787
+ var REJECTED_STEPS = [
788
+ {
789
+ name: "notify-body",
790
+ because: "no verifier. A notification with a wrong body is sent and read as though it were right; nothing in the run disagrees with it afterwards"
791
+ },
792
+ {
793
+ name: "issue-comment",
794
+ because: "no verifier. A decision log or completion comment that says the wrong thing is the record, so there is nothing left to check it against"
795
+ },
796
+ {
797
+ name: "status-read",
798
+ because: "a misread routes the run to the wrong child, and no later step disagrees. Running the status command is already cheap; what is expensive is acting on it, which is not this step"
799
+ },
800
+ {
801
+ name: "diagnosis",
802
+ because: "a wrong root cause produces a fix that passes the gate and leaves the defect"
803
+ },
804
+ {
805
+ name: "design",
806
+ because: "the cost of a wrong design is paid by every step after it"
807
+ },
808
+ {
809
+ name: "split-assessment",
810
+ because: "a missed split widens one Issue into a batch nobody authorized"
811
+ },
812
+ {
813
+ name: "review",
814
+ because: "the review is the last thing between a defect and a merge; a cheaper one finds less"
815
+ }
816
+ ];
817
+ function find_step(name) {
818
+ return DELEGATABLE_STEPS.find((step) => step.name === name.trim());
819
+ }
820
+ function verdict_for(name) {
821
+ return find_step(name) === void 0 ? KEEP_VERDICT : DELEGATE_VERDICT;
822
+ }
823
+ function rejection_for(name) {
824
+ return REJECTED_STEPS.find((step) => step.name === name.trim());
825
+ }
826
+ function reason_for(name) {
827
+ const step = find_step(name);
828
+ if (step !== void 0) return `delegatable: ${step.verifier}`;
829
+ const rejected = rejection_for(name);
830
+ if (rejected !== void 0) return `kept deliberately: ${rejected.because}`;
831
+ return "kept by default: not on the delegatable list, and the list is the whole of it";
832
+ }
833
+ var delegation_policy = {
834
+ DELEGATE_VERDICT,
835
+ INVESTIGATION_FILE_THRESHOLD,
836
+ INVESTIGATION_LIMIT,
837
+ INVESTIGATION_SEARCH_TURN_THRESHOLD,
838
+ INVESTIGATION_STEP,
839
+ INVESTIGATOR_AGENT,
840
+ KEEP_VERDICT,
841
+ DELEGATABLE_STEPS,
842
+ REJECTED_STEPS,
843
+ find_step,
844
+ verdict_for,
845
+ reason_for
846
+ };
847
+
848
+ // scripts/delegation/investigation-reads.ts
849
+ var READ_TOOLS = /* @__PURE__ */ new Set(["Read"]);
850
+ var DELEGATION_TOOLS = /* @__PURE__ */ new Set(["Agent", "Task"]);
851
+ var CONTENT_READ_COMMANDS = [
852
+ "bat",
853
+ "cat",
854
+ "head",
855
+ "less",
856
+ "more",
857
+ "nl",
858
+ "sed",
859
+ "tail"
860
+ ];
861
+ var CONTENT_READ_LABELS = new Set(
862
+ CONTENT_READ_COMMANDS.map((command) => time_shell.bash_label(command))
863
+ );
864
+ var { INVESTIGATOR_AGENT: INVESTIGATOR_AGENT2 } = delegation_policy;
865
+ var REASON = `\u26D4 investigation: ${delegation_policy.INVESTIGATION_LIMIT}. ${delegation_policy.INVESTIGATION_STEP} Keep main-line reads to files you will edit; every read-only search turn counts. \`.claude/skills/workflow-commands/delegation.md\``;
866
+ var INSTRUCTION_PATHS = ["prompts", ".claude/skills"];
867
+ var INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "AGENTS.md", "GEMINI.md"]);
868
+ var REPOSITORY_ROOT = process.cwd();
869
+ function resolved(target) {
870
+ return path2.resolve(REPOSITORY_ROOT, target);
871
+ }
872
+ function repository_relative(absolute) {
873
+ return path2.relative(REPOSITORY_ROOT, absolute);
874
+ }
875
+ function is_instruction_document(target) {
876
+ const relative = repository_relative(resolved(target));
877
+ const segments = relative.split(path2.sep);
878
+ return INSTRUCTION_FILES.has(segments.at(-1) ?? "") || INSTRUCTION_PATHS.some((prefix) => relative.startsWith(`${prefix}${path2.sep}`));
879
+ }
880
+ var SESSION_TEMP_SEGMENT = /^claude-\d+$/u;
881
+ var SESSION_STATE_SEGMENTS = cost_transcript.TRANSCRIPT_ROOT.split(path2.sep);
882
+ var HARNESS_OUTPUT_DIRECTORY = "tasks";
883
+ var HARNESS_OUTPUT_EXTENSION = ".output";
884
+ var FILE_SEGMENT_INDEX = -1;
885
+ var PARENT_SEGMENT_INDEX = -2;
886
+ function has_state_root(segments, index) {
887
+ return SESSION_STATE_SEGMENTS.every((segment, offset) => segments[index + offset] === segment);
888
+ }
889
+ function is_harness_output(segments) {
890
+ return segments.some((segment) => SESSION_TEMP_SEGMENT.test(segment)) && segments.at(PARENT_SEGMENT_INDEX) === HARNESS_OUTPUT_DIRECTORY && (segments.at(FILE_SEGMENT_INDEX) ?? "").endsWith(HARNESS_OUTPUT_EXTENSION);
891
+ }
892
+ function is_outside_repository(absolute) {
893
+ return repository_relative(absolute).startsWith("..");
894
+ }
895
+ function is_session_artifact(target) {
896
+ const absolute = resolved(target);
897
+ const segments = absolute.split(path2.sep);
898
+ if (!is_outside_repository(absolute)) return false;
899
+ return segments.some((_segment, index) => has_state_root(segments, index)) || is_harness_output(segments) || capped_print_part.is_part_file(absolute);
900
+ }
901
+ function is_subject_file(target) {
902
+ return !is_instruction_document(target) && !is_session_artifact(target);
903
+ }
904
+ function is_nameable_file(target) {
905
+ return !target.includes("*") && !target.includes("?");
906
+ }
907
+ function is_content_read(label) {
908
+ return READ_TOOLS.has(label) || CONTENT_READ_LABELS.has(label);
909
+ }
910
+ function subject_targets(targets) {
911
+ return targets.filter((target) => is_nameable_file(target) && is_subject_file(target)).map((target) => resolved(target));
912
+ }
913
+ function call_targets(call) {
914
+ return subject_targets(time_bundle_call.call_facts(call.name, call.input).targets);
915
+ }
916
+ function delegation_instant(span) {
917
+ return span === void 0 ? hook_decision.NEVER_MS : span.ended_ms;
918
+ }
919
+ function is_failed_outcome(span) {
920
+ return span.outcome === time_spans.FAILED_OUTCOME;
921
+ }
922
+ function edited_files(spans) {
923
+ const written = spans.filter((span) => !is_failed_outcome(span)).flatMap((span) => subject_targets(span.writes));
924
+ return new Set(written);
925
+ }
926
+ function add_all(pending, targets, at_ms) {
927
+ for (const target of targets) if (!pending.has(target)) pending.set(target, at_ms);
928
+ }
929
+ function search_turn_key(searches, span) {
930
+ if (span.message_id !== time_spans.NO_MESSAGE_ID) return span.message_id;
931
+ return `${time_spans.NO_MESSAGE_ID}#${String(searches.size)}`;
932
+ }
933
+ function apply_search(searches, span) {
934
+ if (span.is_writing) {
935
+ searches.clear();
936
+ return;
937
+ }
938
+ const turn = search_turn_key(searches, span);
939
+ if (time_search_call.is_read_only_search(span) && !searches.has(turn)) {
940
+ searches.set(turn, span.ended_ms);
941
+ }
792
942
  }
793
- function priced(tokens, rate) {
794
- return tokens * rate / TOKENS_PER_MILLION;
943
+ function is_delegation(span) {
944
+ return DELEGATION_TOOLS.has(span.label) && !is_failed_outcome(span);
795
945
  }
796
- function estimate_composition(totals, price) {
946
+ function apply_span(accumulation, span, edited) {
947
+ if (is_failed_outcome(span)) return;
948
+ if (is_delegation(span)) {
949
+ accumulation.pending.clear();
950
+ accumulation.searches.clear();
951
+ return;
952
+ }
953
+ apply_search(accumulation.searches, span);
954
+ if (!is_content_read(span.label)) return;
955
+ const fresh = subject_targets(span.targets).filter((target) => !edited.has(target));
956
+ add_all(accumulation.pending, fresh, span.ended_ms);
957
+ }
958
+ function last_delegation_ms(spans) {
959
+ return delegation_instant(spans.findLast((span) => is_delegation(span)));
960
+ }
961
+ function pending_after(pending, since_ms) {
962
+ return [...pending.values()].filter((at_ms) => at_ms > since_ms).length;
963
+ }
964
+ function tally_of(text2, since_ms = hook_decision.NEVER_MS, branch_files = []) {
965
+ const { spans, started_ms } = time_spans.parse_timeline(text2);
966
+ const edited = /* @__PURE__ */ new Set([...edited_files(spans), ...subject_targets(branch_files)]);
967
+ const accumulation = { pending: /* @__PURE__ */ new Map(), searches: /* @__PURE__ */ new Map() };
968
+ for (const span of spans) apply_span(accumulation, span, edited);
797
969
  return {
798
- input_usd: priced(totals.input_tokens, price.input),
799
- cache_write_5m_usd: priced(
800
- totals.cache_write_5m_tokens * CACHE_WRITE_5M_MULTIPLIER,
801
- price.input
802
- ),
803
- cache_write_1h_usd: priced(
804
- totals.cache_write_1h_tokens * CACHE_WRITE_1H_MULTIPLIER,
805
- price.input
806
- ),
807
- cache_read_usd: priced(totals.cache_read_tokens * cache_read_multiplier(price), price.input),
808
- output_usd: priced(totals.output_tokens, price.output)
970
+ pending: [...accumulation.pending.keys()],
971
+ pending_since: pending_after(accumulation.pending, since_ms),
972
+ reset_ms: last_delegation_ms(spans),
973
+ window_start_ms: started_ms,
974
+ searches: accumulation.searches.size,
975
+ searches_since: pending_after(accumulation.searches, since_ms),
976
+ edited
809
977
  };
810
978
  }
811
- function to_model_cost(model, totals) {
812
- const price = resolve_price(model);
813
- if (price === void 0) return { model, totals };
814
- return { model, totals, cost_usd: estimate_cost(totals, price) };
979
+ function projected_count(pending, call, edited = /* @__PURE__ */ new Set()) {
980
+ return (/* @__PURE__ */ new Set([...pending, ...call_targets(call).filter((target) => !edited.has(target))])).size;
815
981
  }
816
- function billed_model(usage) {
817
- const long_id = `${known_id(usage.model) ?? usage.model}${LONG_PROMPT_SUFFIX}`;
818
- const is_long = cost_usage.billed_input(usage.totals) > LONG_PROMPT_THRESHOLD;
819
- return is_long && Object.hasOwn(MODEL_PRICES, long_id) ? long_id : usage.model;
982
+ function is_at_threshold(pending_count) {
983
+ return pending_count + 1 >= delegation_policy.INVESTIGATION_FILE_THRESHOLD;
820
984
  }
821
- function cost_by_model(records) {
822
- const grouped = /* @__PURE__ */ new Map();
823
- for (const record2 of records) {
824
- const model = billed_model(record2);
825
- const running = grouped.get(model) ?? cost_usage.EMPTY_TOTALS;
826
- grouped.set(model, cost_usage.add_totals(running, record2.totals));
985
+ function is_at_search_threshold(search_count) {
986
+ return search_count + 1 >= delegation_policy.INVESTIGATION_SEARCH_TURN_THRESHOLD;
987
+ }
988
+ function has_cleared_the_disarm(tally, refused_at_ms) {
989
+ return tally.reset_ms > refused_at_ms || refused_at_ms < tally.window_start_ms;
990
+ }
991
+ function is_rearmed(tally, refused_at_ms) {
992
+ if (refused_at_ms === hook_decision.NEVER_MS || has_cleared_the_disarm(tally, refused_at_ms)) {
993
+ return true;
827
994
  }
828
- return [...grouped].map(([model, totals]) => to_model_cost(model, totals));
995
+ return is_at_threshold(tally.pending_since) || is_at_search_threshold(tally.searches_since);
829
996
  }
830
- function total_cost(costs) {
831
- return {
832
- usd: costs.reduce((sum, entry) => sum + (entry.cost_usd ?? 0), 0),
833
- unpriced: costs.filter((entry) => entry.cost_usd === void 0).map((entry) => entry.model)
834
- };
997
+ function is_unit_transcript(transcript) {
998
+ return transcript.split(path2.sep).includes(cost_transcript.UNIT_DIRECTORY);
835
999
  }
836
- function cost_of(records) {
837
- return total_cost(cost_by_model(records)).usd;
1000
+ function is_unit_run(run2) {
1001
+ return run2 !== void 0 && is_unit_transcript(run2.transcript);
838
1002
  }
839
- var cost_pricing = {
840
- CACHE_WRITE_5M_MULTIPLIER,
841
- CACHE_WRITE_1H_MULTIPLIER,
842
- DEFAULT_CACHE_READ_MULTIPLIER,
843
- LONG_PROMPT_THRESHOLD,
844
- resolve_price,
845
- estimate_cost,
846
- estimate_composition,
847
- cost_by_model,
848
- total_cost,
849
- cost_of
1003
+ function is_read_only_command(call, labels) {
1004
+ if (call.name !== cost_blocks.BASH_TOOL) return false;
1005
+ const label = time_shell.bash_label(time_shell.bash_command(call.input));
1006
+ return labels.has(label) && time_batch_guard.is_read_only_call(call);
1007
+ }
1008
+ function is_search_call(call) {
1009
+ return time_search_call.is_read_only_search(
1010
+ time_spans.to_tool_call(call.name, call.input, time_spans.NO_MESSAGE_ID)
1011
+ );
1012
+ }
1013
+ function is_refusable_call(call) {
1014
+ if (is_search_call(call)) return true;
1015
+ if (call_targets(call).length === 0) return false;
1016
+ if (READ_TOOLS.has(call.name)) return true;
1017
+ return is_read_only_command(call, CONTENT_READ_LABELS);
1018
+ }
1019
+ function is_growing_past_threshold(tally, call) {
1020
+ if (is_search_call(call)) return is_at_search_threshold(tally.searches);
1021
+ if (!is_at_threshold(tally.pending.length)) return false;
1022
+ return projected_count(tally.pending, call, tally.edited) > tally.pending.length;
1023
+ }
1024
+ function is_blocking(tally, call, refused_at_ms) {
1025
+ return is_growing_past_threshold(tally, call) && is_rearmed(tally, refused_at_ms);
1026
+ }
1027
+ function is_blocking_past_branch(text2, call, refused_at_ms) {
1028
+ if (!is_blocking(tally_of(text2, refused_at_ms), call, refused_at_ms)) return false;
1029
+ const branch_files = changed_paths_sync.read_changed_paths();
1030
+ return is_blocking(tally_of(text2, refused_at_ms, branch_files), call, refused_at_ms);
1031
+ }
1032
+ function should_block(text2, call, refused_at_ms, run2) {
1033
+ if (!is_refusable_call(call) || is_unit_run(run2)) return false;
1034
+ return is_blocking_past_branch(text2, call, refused_at_ms);
1035
+ }
1036
+ var investigation_reads = {
1037
+ DELEGATION_TOOLS,
1038
+ INVESTIGATOR_AGENT: INVESTIGATOR_AGENT2,
1039
+ READ_TOOLS,
1040
+ REASON,
1041
+ is_at_threshold,
1042
+ is_instruction_document,
1043
+ is_rearmed,
1044
+ is_refusable_call,
1045
+ is_session_artifact,
1046
+ is_subject_file,
1047
+ projected_count,
1048
+ resolved,
1049
+ should_block,
1050
+ tally_of
850
1051
  };
851
1052
 
852
- // scripts/cost-runtime/context-cut-payback.ts
853
- var POST_CUT_CONTEXT = 6e4;
854
- var EXPECTED_REMAINING_REQUESTS = 10;
855
- function write_over_read() {
856
- return cost_pricing.CACHE_WRITE_5M_MULTIPLIER / cost_pricing.DEFAULT_CACHE_READ_MULTIPLIER;
1053
+ // scripts/delegation/duplicate-read-guard.ts
1054
+ var SWITCH_ENV_KEY = "JOSH_DUPLICATE_READ_GUARD";
1055
+ var LANE_GUARD_ID = "duplicate-read";
1056
+ var STAMP_PREFIX = "josh-duplicate-read-guard-";
1057
+ var NOTICE_STAMP_PREFIX = "josh-duplicate-read-notice-";
1058
+ var READ_TOOL = "Read";
1059
+ var GLOB_CHARACTERS = /[*?]/u;
1060
+ var REASON2 = `\u26D4 duplicate read: this file was already read in this session and has not changed since (its mtime predates that read), so reading it again returns nothing new. Scroll up to the earlier result instead of re-reading it. A read with a new offset/limit, or of a file that has since changed, is allowed; set \`JOSH_DUPLICATE_READ_GUARD=off\` to disable this guard.`;
1061
+ var NOTICE = `\u26A0 duplicate read: this file was already read in this session and has not changed since, so re-reading it returns nothing new \u2014 scroll up to the earlier result instead of re-reading it. Set \`JOSH_DUPLICATE_READ_GUARD=off\` to silence this.`;
1062
+ function is_paginated(input) {
1063
+ return input["offset"] !== void 0 || input["limit"] !== void 0;
1064
+ }
1065
+ function names_one_file(file_path) {
1066
+ return typeof file_path === "string" && file_path !== "" && !GLOB_CHARACTERS.test(file_path);
1067
+ }
1068
+ function read_target(call) {
1069
+ if (call.name !== READ_TOOL || !json_value.is_record(call.input)) return void 0;
1070
+ const { input } = call;
1071
+ if (is_paginated(input) || !names_one_file(input["file_path"])) return void 0;
1072
+ return investigation_reads.resolved(input["file_path"]);
1073
+ }
1074
+ function is_refusable_call2(call) {
1075
+ return read_target(call) !== void 0;
1076
+ }
1077
+ function is_successful_read(span) {
1078
+ const is_read = investigation_reads.READ_TOOLS.has(span.label);
1079
+ return is_read && span.outcome !== time_spans.FAILED_OUTCOME;
1080
+ }
1081
+ function reads_target(span, target) {
1082
+ return span.targets.some((one) => investigation_reads.resolved(one) === target);
1083
+ }
1084
+ function last_read_ms(spans, target) {
1085
+ const times = spans.filter((span) => is_successful_read(span) && reads_target(span, target)).map((span) => span.ended_ms);
1086
+ return times.length === 0 ? hook_decision.NEVER_MS : Math.max(...times);
1087
+ }
1088
+ function file_mtime_ms(target) {
1089
+ try {
1090
+ return statSync(target).mtimeMs;
1091
+ } catch {
1092
+ return void 0;
1093
+ }
857
1094
  }
858
- function break_even_context(remaining = EXPECTED_REMAINING_REQUESTS) {
859
- if (remaining <= 0) return Infinity;
860
- return Math.round(POST_CUT_CONTEXT * (1 + write_over_read() / remaining));
1095
+ function read_window(tail, target) {
1096
+ const { spans, started_ms } = time_spans.parse_timeline(tail);
1097
+ return { last_ms: last_read_ms(spans, target), window_start_ms: started_ms };
861
1098
  }
862
- function break_even_requests(current_context) {
863
- const saved_per_request = current_context - POST_CUT_CONTEXT;
864
- if (saved_per_request <= 0) return Infinity;
865
- return POST_CUT_CONTEXT * write_over_read() / saved_per_request;
1099
+ function is_unchanged_reread(last_ms, mtime) {
1100
+ if (mtime === void 0 || last_ms === hook_decision.NEVER_MS) return false;
1101
+ return mtime < last_ms;
866
1102
  }
867
- function pays_back(current_context, remaining = EXPECTED_REMAINING_REQUESTS) {
868
- return current_context > break_even_context(remaining);
1103
+ function is_rearmed2(window, fired_at_ms) {
1104
+ if (fired_at_ms === hook_decision.NEVER_MS || fired_at_ms < window.window_start_ms) return true;
1105
+ return window.last_ms > fired_at_ms;
869
1106
  }
870
- var context_cut_payback = {
871
- POST_CUT_CONTEXT,
872
- EXPECTED_REMAINING_REQUESTS,
873
- break_even_context,
874
- break_even_requests,
875
- pays_back
1107
+ function is_redundant_reread(tail, call, fired_at_ms) {
1108
+ const target = read_target(call);
1109
+ if (target === void 0) return false;
1110
+ const window = read_window(tail, target);
1111
+ if (!is_unchanged_reread(window.last_ms, file_mtime_ms(target))) return false;
1112
+ return is_rearmed2(window, fired_at_ms);
1113
+ }
1114
+ function duplicate_mode() {
1115
+ return lane_guard_policy.mode_here(LANE_GUARD_ID);
1116
+ }
1117
+ function is_candidate(call) {
1118
+ return is_refusable_call2(call) && duplicate_mode() !== "off";
1119
+ }
1120
+ function should_block_here(tail, call, refused_at_ms) {
1121
+ if (duplicate_mode() !== "refuse") return false;
1122
+ return is_redundant_reread(tail, call, refused_at_ms);
1123
+ }
1124
+ function should_notify_here(tail, call, notified_at_ms) {
1125
+ if (duplicate_mode() !== "notice") return false;
1126
+ return is_redundant_reread(tail, call, notified_at_ms);
1127
+ }
1128
+ var GUARD = hook_decision.create_transcript_guard({
1129
+ prefix: STAMP_PREFIX,
1130
+ switch_key: SWITCH_ENV_KEY,
1131
+ is_candidate,
1132
+ should_block: should_block_here,
1133
+ reason: REASON2,
1134
+ notify: {
1135
+ prefix: NOTICE_STAMP_PREFIX,
1136
+ should_notify: should_notify_here,
1137
+ text: () => NOTICE
1138
+ }
1139
+ });
1140
+ var duplicate_read_refusal = GUARD.refusal;
1141
+ var duplicate_read_outcome = GUARD.outcome;
1142
+ var { is_enabled, refusal_path } = GUARD;
1143
+ var notice_refusal_path = hook_decision.create_refusal_stamp(NOTICE_STAMP_PREFIX).path;
1144
+ var { deny_envelope: deny_envelope2, DISABLED_VALUES: DISABLED_VALUES2 } = hook_decision;
1145
+ var duplicate_reads = {
1146
+ is_successful_read,
1147
+ REASON: REASON2,
1148
+ NOTICE
876
1149
  };
877
1150
 
878
- // scripts/cost-runtime/context-cut-threshold.ts
879
- var CONTEXT_CUT_THRESHOLD = context_cut_payback.break_even_context();
880
-
881
- // scripts/run/cut/run-cut.ts
882
- import { z as z3 } from "zod";
1151
+ // scripts/run/cut/run-cut-digest.ts
1152
+ import { z as z2 } from "zod";
1153
+ var session_digest_schema = z2.object({
1154
+ read: z2.array(z2.string()),
1155
+ decisions: z2.array(z2.string())
1156
+ });
1157
+ var MAX_READ_ENTRIES = 40;
1158
+ var MAX_DECISIONS = 12;
1159
+ var MAX_DECISION_CHARS = 240;
1160
+ var DECISION_PREFIX = "Auto-decided:";
1161
+ var DECISION_LINE = /(?:^|[\s'"=])(Auto-decided: [^\n]+)/gu;
1162
+ var TRAILING_QUOTES = /['"]$/u;
1163
+ var ASSISTANT = "assistant";
1164
+ var READ_LABEL = "Already read before the cut \u2014 re-read only the lines you need";
1165
+ var DECISIONS_LABEL = "Decided before the cut \u2014 do not reopen";
1166
+ function shown_path(target, cwd) {
1167
+ const relative = path3.relative(cwd, path3.resolve(cwd, target));
1168
+ return relative.startsWith("..") ? target : relative;
1169
+ }
1170
+ function newest_unique(entries, limit) {
1171
+ const unique = [...new Set(entries.toReversed())].toReversed();
1172
+ return unique.slice(-limit);
1173
+ }
1174
+ function read_files_of(text2, cwd) {
1175
+ const reads = time_spans.parse_timeline(text2).spans.filter((span) => duplicate_reads.is_successful_read(span)).flatMap((span) => span.targets.map((target) => shown_path(target, cwd)));
1176
+ return newest_unique(reads, MAX_READ_ENTRIES);
1177
+ }
1178
+ var COMMENT_INPUT_KEYS = ["command", "content"];
1179
+ function input_strings(input) {
1180
+ if (!json_value.is_record(input)) return [];
1181
+ return COMMENT_INPUT_KEYS.map((key2) => input[key2]).filter((value) => typeof value === "string");
1182
+ }
1183
+ function assistant_texts(record3) {
1184
+ if (record3?.type !== ASSISTANT) return [];
1185
+ return transcript_record.blocks_of(record3).flatMap((block) => [block.text ?? "", ...input_strings(block.input)]);
1186
+ }
1187
+ function decisions_of(text2) {
1188
+ const decisions = text2.split("\n").flatMap((line) => assistant_texts(transcript_record.parse(line))).flatMap((written) => [...written.matchAll(DECISION_LINE)].map((match) => match[1] ?? "")).map((line) => line.replace(TRAILING_QUOTES, "").slice(0, MAX_DECISION_CHARS));
1189
+ return newest_unique(decisions, MAX_DECISIONS);
1190
+ }
1191
+ function digest_of(text2, cwd) {
1192
+ return { read: read_files_of(text2, cwd), decisions: decisions_of(text2) };
1193
+ }
1194
+ function own_transcript(cwd, environment) {
1195
+ if (own_session.session_id_of(environment) === void 0) return void 0;
1196
+ const files = cost_transcript.list_sessions_across(cost_transcript.transcript_directories(cwd));
1197
+ const selection = own_session.select_own_session(files, environment);
1198
+ if (selection.kind === "absent") return void 0;
1199
+ const file = files[selection.index];
1200
+ return file === void 0 ? void 0 : cost_transcript.read_optional(file);
1201
+ }
1202
+ function own_digest(cwd = process.cwd(), environment = process.env) {
1203
+ const text2 = own_transcript(cwd, environment);
1204
+ return text2 === void 0 ? void 0 : digest_of(text2, cwd);
1205
+ }
1206
+ function describe_list(label, entries) {
1207
+ if (entries.length === 0) return [];
1208
+ const bullets = entries.map((entry) => ` - ${entry}`).join("\n");
1209
+ return [`${label}:
1210
+ ${bullets}`];
1211
+ }
1212
+ function describe_digest(digest) {
1213
+ if (digest === void 0) return void 0;
1214
+ const blocks = [
1215
+ ...describe_list(READ_LABEL, digest.read),
1216
+ ...describe_list(DECISIONS_LABEL, digest.decisions)
1217
+ ];
1218
+ return blocks.length === 0 ? void 0 : blocks.join("\n\n");
1219
+ }
1220
+ var run_cut_digest = {
1221
+ DECISION_PREFIX,
1222
+ describe_digest,
1223
+ digest_of,
1224
+ own_digest,
1225
+ session_digest_schema
1226
+ };
883
1227
 
884
1228
  // scripts/run/cut/run-cut-handoff.ts
885
1229
  import { readFileSync } from "node:fs";
886
- import { z as z2 } from "zod";
887
- var handoff_schema = z2.object({
888
- instruction: z2.string(),
889
- completed: z2.array(z2.string()),
890
- remaining: z2.array(z2.string()),
891
- untouched: z2.array(z2.string())
1230
+ import { z as z3 } from "zod";
1231
+ var handoff_schema = z3.object({
1232
+ instruction: z3.string(),
1233
+ completed: z3.array(z3.string()),
1234
+ remaining: z3.array(z3.string()),
1235
+ untouched: z3.array(z3.string()),
1236
+ findings: z3.array(z3.string()).optional()
892
1237
  });
893
1238
  var COMPLETED_LABEL = "Completed";
894
1239
  var REMAINING_LABEL = "Remaining";
895
1240
  var UNTOUCHED_LABEL = "Deliberately untouched";
896
1241
  var INSTRUCTION_LABEL = "Instruction (verbatim)";
1242
+ var FINDINGS_LABEL = "Round-1 findings, attested but not recorded \u2014 do not rerun round 1: fix them, then record them with `pnpm josh review:record --issue <N>`";
897
1243
  var EMPTY_LIST_NOTE = "(none)";
898
- function describe_key(key2) {
899
- const value = handoff_schema.shape[key2] instanceof z2.ZodArray ? '["\u2026"]' : '"\u2026"';
900
- return `"${key2}": ${value}`;
901
- }
902
- var HANDOFF_FORMAT = `a JSON object {${handoff_schema.keyof().options.map((key2) => describe_key(key2)).join(", ")}}`;
1244
+ var PLACEHOLDER = "\u2026";
1245
+ var FINDINGS_PLACEHOLDER = "unrecorded";
1246
+ var EXAMPLE_LIST = `["${PLACEHOLDER}"]`;
1247
+ var EXAMPLE_VALUES = {
1248
+ instruction: `"${PLACEHOLDER}"`,
1249
+ completed: EXAMPLE_LIST,
1250
+ remaining: EXAMPLE_LIST,
1251
+ untouched: EXAMPLE_LIST,
1252
+ findings: `["${FINDINGS_PLACEHOLDER}"]`
1253
+ };
1254
+ var HANDOFF_FORMAT = `{${handoff_schema.keyof().options.map((key2) => `"${key2}":${EXAMPLE_VALUES[key2]}`).join(",")}}`;
903
1255
  function parse_handoff(raw) {
904
1256
  return json_value.parse_with(raw, handoff_schema);
905
1257
  }
@@ -930,25 +1282,43 @@ function load_handoff(handoff_path, max_bytes) {
930
1282
  }
931
1283
  return { kind: "ok", handoff };
932
1284
  }
933
- function describe_list(label, items) {
1285
+ function describe_list2(label, items) {
934
1286
  if (items.length === 0) return `${label}: ${EMPTY_LIST_NOTE}`;
935
1287
  const bullets = items.map((item) => ` - ${item}`).join("\n");
936
1288
  return `${label}:
937
1289
  ${bullets}`;
938
1290
  }
1291
+ function carried_findings(handoff) {
1292
+ const findings = handoff?.findings ?? [];
1293
+ return findings.filter(
1294
+ (finding) => ![PLACEHOLDER, FINDINGS_PLACEHOLDER, ""].includes(finding.trim())
1295
+ );
1296
+ }
1297
+ function describe_findings(handoff) {
1298
+ const findings = carried_findings(handoff);
1299
+ if (findings.length === 0) return [];
1300
+ return [describe_list2(FINDINGS_LABEL, findings)];
1301
+ }
939
1302
  function describe_handoff(handoff) {
940
1303
  return [
941
1304
  `${INSTRUCTION_LABEL}:
942
1305
  ${handoff.instruction}`,
943
- describe_list(COMPLETED_LABEL, handoff.completed),
944
- describe_list(REMAINING_LABEL, handoff.remaining),
945
- describe_list(UNTOUCHED_LABEL, handoff.untouched)
1306
+ describe_list2(COMPLETED_LABEL, handoff.completed),
1307
+ describe_list2(REMAINING_LABEL, handoff.remaining),
1308
+ describe_list2(UNTOUCHED_LABEL, handoff.untouched),
1309
+ ...describe_findings(handoff)
946
1310
  ].join("\n\n");
947
1311
  }
948
1312
  var HANDOFF_PATH = ".josh/tmp/handoff-<N>.json";
1313
+ var ISSUE_PLACEHOLDER = "<N>";
1314
+ function handoff_path_of(issue) {
1315
+ return HANDOFF_PATH.replace(ISSUE_PLACEHOLDER, () => issue);
1316
+ }
949
1317
  var run_cut_handoff = {
950
1318
  HANDOFF_FORMAT,
951
1319
  HANDOFF_PATH,
1320
+ handoff_path_of,
1321
+ carried_findings,
952
1322
  describe_handoff,
953
1323
  handoff_schema,
954
1324
  is_complete_handoff,
@@ -960,21 +1330,21 @@ var run_cut_handoff = {
960
1330
  // scripts/run/cut/run-cut.ts
961
1331
  var CUT_PREFIX = "josh-run-cut-";
962
1332
  var { PRE_GATE_PHASE, IMPLEMENTATION_PHASE } = agent_role_profile;
963
- var IMPLEMENTATION_CONTEXT_THRESHOLD = CONTEXT_CUT_THRESHOLD;
964
1333
  var CUT_MAX_AGE_MS = backlog_budget.DEFAULT_IDLE_MS;
965
1334
  var CUT_MAX_AGE_HOURS = backlog_budget.IDLE_WAIT_HOURS;
966
1335
  var END_COMMAND = "pnpm josh run:cut --end";
967
1336
  var READ_COMMAND = "pnpm josh run:cut --json";
968
1337
  var MAX_HANDOFF_BYTES = 8192;
969
- var run_cut_schema = z3.object({
970
- invocation: z3.string(),
971
- issue: z3.string(),
972
- branch: z3.string(),
973
- phase: z3.string(),
974
- cut_at: z3.string(),
975
- is_handed_off: z3.boolean().optional(),
1338
+ var run_cut_schema = z4.object({
1339
+ invocation: z4.string(),
1340
+ issue: z4.string(),
1341
+ branch: z4.string(),
1342
+ phase: z4.string(),
1343
+ cut_at: z4.string(),
1344
+ is_handed_off: z4.boolean().optional(),
976
1345
  handoff: run_cut_handoff.handoff_schema.optional(),
977
- is_merge_relaunched: z3.boolean().optional()
1346
+ digest: run_cut_digest.session_digest_schema.optional(),
1347
+ is_merge_relaunched: z4.boolean().optional()
978
1348
  });
979
1349
  function cut_path(git_directory) {
980
1350
  return stamp_file.stamp_path(CUT_PREFIX, git_directory);
@@ -997,21 +1367,21 @@ var CUT_SPEC = {
997
1367
  function is_expired(cut, now) {
998
1368
  return stamp_record.is_older_than(cut_at_of(cut), CUT_MAX_AGE_MS, now);
999
1369
  }
1000
- function classify(raw, now) {
1001
- const read3 = stamp_record.classify(raw, CUT_SPEC, now);
1002
- if (!("record" in read3)) return read3;
1003
- return { kind: read3.kind, cut: read3.record };
1370
+ function classify2(raw, now) {
1371
+ const read4 = stamp_record.classify(raw, CUT_SPEC, now);
1372
+ if (!("record" in read4)) return read4;
1373
+ return { kind: read4.kind, cut: read4.record };
1004
1374
  }
1005
1375
  function read_cut(target, now = /* @__PURE__ */ new Date()) {
1006
- return classify(stamp_file.read_stamp_text(target), now);
1376
+ return classify2(stamp_file.read_stamp_text(target), now);
1007
1377
  }
1008
1378
  function worktree_git_directory_sync() {
1009
1379
  return git_common_directory.own();
1010
1380
  }
1011
1381
  function carried_cut_sync(now = /* @__PURE__ */ new Date(), directory = worktree_git_directory_sync()) {
1012
1382
  if (directory === void 0) return void 0;
1013
- const read3 = read_cut(cut_path(directory), now);
1014
- return read3.kind === "carried" ? read3.cut : void 0;
1383
+ const read4 = read_cut(cut_path(directory), now);
1384
+ return read4.kind === "carried" ? read4.cut : void 0;
1015
1385
  }
1016
1386
  function lane_cut_sync(lane_directory) {
1017
1387
  const directory = git_common_directory.own(lane_directory);
@@ -1027,7 +1397,8 @@ function fresh_cut(spec, now) {
1027
1397
  phase: spec.phase,
1028
1398
  cut_at: now.toISOString(),
1029
1399
  is_handed_off: true,
1030
- handoff: spec.handoff
1400
+ handoff: spec.handoff,
1401
+ digest: spec.digest
1031
1402
  };
1032
1403
  }
1033
1404
  function resumes_into_implementation(phase) {
@@ -1039,6 +1410,11 @@ function within_handoff_bound(cut) {
1039
1410
  function record_within_bound(spec, now = /* @__PURE__ */ new Date()) {
1040
1411
  return within_handoff_bound(fresh_cut(spec, now));
1041
1412
  }
1413
+ function with_digest(spec, digest_of2) {
1414
+ if (spec.handoff === void 0) return spec;
1415
+ const digested = { ...spec, digest: digest_of2() };
1416
+ return record_within_bound(digested) ? digested : spec;
1417
+ }
1042
1418
  function begin_cut(target, spec, now = /* @__PURE__ */ new Date()) {
1043
1419
  const cut = fresh_cut(spec, now);
1044
1420
  if (!within_handoff_bound(cut)) return void 0;
@@ -1078,8 +1454,8 @@ function end_cut(target) {
1078
1454
  }
1079
1455
  function hold_of(directory) {
1080
1456
  if (directory === void 0) return void 0;
1081
- const read3 = run_hold.read_hold(run_hold.hold_path(directory));
1082
- return read3.kind === "held" || read3.kind === "stale" ? read3.hold : void 0;
1457
+ const read4 = run_hold.read_hold(run_hold.hold_path(directory));
1458
+ return read4.kind === "held" || read4.kind === "stale" ? read4.hold : void 0;
1083
1459
  }
1084
1460
  function fullrun_issue(hold) {
1085
1461
  return hold?.is_fullrun === true ? hold.issue : void 0;
@@ -1124,7 +1500,6 @@ var run_cut = {
1124
1500
  CUT_MAX_AGE_HOURS,
1125
1501
  CUT_MAX_AGE_MS,
1126
1502
  END_COMMAND,
1127
- IMPLEMENTATION_CONTEXT_THRESHOLD,
1128
1503
  IMPLEMENTATION_PHASE,
1129
1504
  MAX_HANDOFF_BYTES,
1130
1505
  PRE_GATE_PHASE,
@@ -1132,9 +1507,10 @@ var run_cut = {
1132
1507
  begin_cut,
1133
1508
  busy_message,
1134
1509
  carried_cut_sync,
1135
- classify,
1510
+ classify: classify2,
1136
1511
  classify_resume,
1137
1512
  current_state,
1513
+ with_digest,
1138
1514
  cut_path,
1139
1515
  describe_cut,
1140
1516
  end_cut,
@@ -1161,6 +1537,56 @@ var run_cut = {
1161
1537
  worktree_git_directory_sync
1162
1538
  };
1163
1539
 
1540
+ // scripts/rules/shell-segments.ts
1541
+ var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
1542
+ var SEGMENT_PARTS = new RegExp(`(${SEGMENT_SEPARATOR.source})`, "u");
1543
+ var COMMAND_SEPARATOR = /&&|\|\||;|\n|(?<!>)\|&?|(?<![<>])&(?!>)/u;
1544
+ function segments_of(command) {
1545
+ return command.split(SEGMENT_SEPARATOR).map((segment) => segment.trim());
1546
+ }
1547
+ function commands_of(command) {
1548
+ return command.split(COMMAND_SEPARATOR).map((segment) => segment.trim());
1549
+ }
1550
+ var COMMAND_SEPARATORS = new RegExp(COMMAND_SEPARATOR.source, "gu");
1551
+ function quote_aware_commands_of(command) {
1552
+ const plain = time_shell.blanked(command);
1553
+ if (plain === void 0) return commands_of(command);
1554
+ const cuts = [...plain.matchAll(COMMAND_SEPARATORS)].map((match) => ({
1555
+ begin: match.index,
1556
+ end: match.index + match[0].length
1557
+ }));
1558
+ const starts = [0, ...cuts.map((cut) => cut.end)];
1559
+ const stops = cuts.map((cut) => cut.begin);
1560
+ return starts.map((start, index) => command.slice(start, stops[index] ?? command.length).trim());
1561
+ }
1562
+ var LINE_CONTINUATION = /\\\n/gu;
1563
+ function joined_continuations(command) {
1564
+ return command.replaceAll(LINE_CONTINUATION, " ");
1565
+ }
1566
+ function parts_of(command) {
1567
+ return command.split(SEGMENT_PARTS);
1568
+ }
1569
+ function first_of(command) {
1570
+ const plain = time_shell.blanked(command);
1571
+ if (plain === void 0) return void 0;
1572
+ const cut = plain.search(SEGMENT_SEPARATOR);
1573
+ return (cut === -1 ? command : command.slice(0, cut)).trim();
1574
+ }
1575
+ function is_josh_command(segment, names) {
1576
+ const named = time_shell.josh_command_of(segment);
1577
+ if (!named.startsWith(time_shell.JOSH_PREFIX)) return false;
1578
+ return names.has(named.slice(time_shell.JOSH_PREFIX.length));
1579
+ }
1580
+ var shell_segments = {
1581
+ commands_of,
1582
+ first_of,
1583
+ is_josh_command,
1584
+ joined_continuations,
1585
+ parts_of,
1586
+ quote_aware_commands_of,
1587
+ segments_of
1588
+ };
1589
+
1164
1590
  // scripts/rules/run-tail-rule.ts
1165
1591
  var SEGMENT_PREFIX = String.raw`(?:\(\s*)?(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*(?:npx\s+|pnpm\s+(?:exec\s+|run\s+)?)?`;
1166
1592
  var WORD_END = String.raw`(?=\s|$|\))`;
@@ -1197,13 +1623,13 @@ function is_foreground_push_step(call) {
1197
1623
  if (is_backgrounded(call.input)) return false;
1198
1624
  return is_push_step_call(call);
1199
1625
  }
1200
- function decide3() {
1626
+ function decide2() {
1201
1627
  return true;
1202
1628
  }
1203
1629
  var RUN_TAIL_REASON = "\u26D4 run tail: this issues the commit-push-PR step in the foreground, where the harness decides when to stop waiting. Reissue it with `run_in_background` set; when it exits, go straight on to `pnpm josh followup` in the foreground \u2014 the turn never ends at the push (`.claude/skills/workflow-commands/background-commands.md`). It fires on every foreground push, not once per run.";
1204
1630
  var run_tail_rule = {
1205
1631
  RUN_TAIL_REASON,
1206
- decide: decide3,
1632
+ decide: decide2,
1207
1633
  is_backgrounded_push_step,
1208
1634
  is_foreground_push_step,
1209
1635
  is_push_step,
@@ -1211,10 +1637,12 @@ var run_tail_rule = {
1211
1637
  };
1212
1638
 
1213
1639
  // scripts/rules/lane-background.ts
1214
- var SHIP_COMMAND = `${time_shell.JOSH_PREFIX}ship`;
1640
+ var FOREGROUND_COMMANDS = new Set(
1641
+ ["ship", "ship:wait", "lane:plan"].map((name) => `${time_shell.JOSH_PREFIX}${name}`)
1642
+ );
1215
1643
  var LONG_RUNNING_COMMANDS = /* @__PURE__ */ new Set([
1216
1644
  ...["gate", "git", "followup"].map((name) => `${time_shell.JOSH_PREFIX}${name}`),
1217
- SHIP_COMMAND
1645
+ ...FOREGROUND_COMMANDS
1218
1646
  ]);
1219
1647
  var BACKGROUND_KEY2 = "run_in_background";
1220
1648
  var TIMEOUT_KEY = "timeout";
@@ -1227,7 +1655,7 @@ function long_commands_of(command) {
1227
1655
  }
1228
1656
  function ships_alone(command) {
1229
1657
  const names = long_commands_of(command);
1230
- return names.length > 0 && names.every((name) => name === SHIP_COMMAND);
1658
+ return names.length > 0 && names.every((name) => FOREGROUND_COMMANDS.has(name));
1231
1659
  }
1232
1660
  function backgrounded_bash_command(call) {
1233
1661
  if (call.name !== cost_blocks.BASH_TOOL || !is_backgrounded2(call.input)) return void 0;
@@ -1237,14 +1665,14 @@ function is_backgrounded_ship(call) {
1237
1665
  const command = backgrounded_bash_command(call);
1238
1666
  return command !== void 0 && ships_alone(command);
1239
1667
  }
1240
- function is_candidate(call) {
1668
+ function is_candidate2(call) {
1241
1669
  if (run_tail_rule.is_push_step_call(call)) return true;
1242
1670
  const command = backgrounded_bash_command(call);
1243
1671
  if (command === void 0 || is_backgrounded_ship(call)) return false;
1244
1672
  return long_commands_of(command).length > 0;
1245
1673
  }
1246
1674
  function is_background_long_run(call, is_lane_child = () => lane_child_marker.is_child_of(process.cwd())) {
1247
- return is_candidate(call) && is_lane_child();
1675
+ return is_candidate2(call) && is_lane_child();
1248
1676
  }
1249
1677
  function ship_alone_input(call) {
1250
1678
  if (call.name !== cost_blocks.BASH_TOOL || !json_value.is_record(call.input)) return void 0;
@@ -1259,7 +1687,7 @@ function foreground_input(call, is_lane_child = () => lane_child_marker.is_child
1259
1687
  if (input === void 0 || is_capped_foreground(input) || !is_lane_child()) return void 0;
1260
1688
  return { ...input, [BACKGROUND_KEY2]: false, [TIMEOUT_KEY]: FOREGROUND_TIMEOUT_MS };
1261
1689
  }
1262
- var FOREGROUND_NOTE = "lane background: a lane child's `josh ship` backgrounded, by its flag or by outrunning the Bash timeout, dies with the headless turn before its supervisor exists \u2014 so it was run in the foreground at the 600000 ms cap, where it detaches itself after the preflight. End the turn on `launched` / `busy`.";
1690
+ var FOREGROUND_NOTE = "lane background: a lane child's `josh ship`, `josh ship:wait` or `josh lane:plan` backgrounded, by its flag or by outrunning the Bash timeout, dies with the headless turn \u2014 so it was run in the foreground at the 600000 ms cap, where `ship` detaches itself after the preflight. Then follow `chain-rule.md` step 0 (`josh ship:wait` on `launched`).";
1263
1691
  function apply_line(open, line, launched_ids) {
1264
1692
  const parsed = time_transcript_line.parse_line(line);
1265
1693
  if (parsed === void 0) return;
@@ -1279,36 +1707,376 @@ function pending_background_ids(tail) {
1279
1707
  function pending_agent_ids(tail) {
1280
1708
  return unfinished_ids(tail, (block) => [block.agent_id]);
1281
1709
  }
1282
- var LANE_BACKGROUND_REASON = '\u26D4 lane background: this session is a dispatched lane child (`JOSH_LANE_CHILD`), a headless process whose background tasks die when the turn ends. Hand over to `pnpm josh ship --detach --review "<title> #<N>"`, issued in the foreground, then end the turn on `launched` / `busy`. It fires on every occurrence (`.claude/skills/workflow-commands/chain-rule.md` step 0).';
1710
+ function pending_command_ids(tail) {
1711
+ return unfinished_ids(tail, (block) => [block.background_id]);
1712
+ }
1713
+ var LANE_BACKGROUND_REASON = '\u26D4 lane background: this session is a dispatched lane child (`JOSH_LANE_CHILD`), a headless process whose background tasks die when the turn ends. Hand over to `pnpm josh ship --detach --review "<title> #<N>"`, issued in the foreground, then `josh ship:wait <N>` on `launched` or end the turn. It fires on every occurrence (`.claude/skills/workflow-commands/chain-rule.md` step 0).';
1283
1714
  var LANE_BACKGROUND_STOP_REASON = "\u26D4 lane background stop: this is a dispatched lane child (`JOSH_LANE_CHILD`) and a background task it launched has not finished, and ending this turn kills it. Wait for it in the foreground, or re-issue the work through `pnpm josh ship --detach` \u2014 do not notify, stop or repeat your reply.";
1284
- var ROW2 = {
1715
+ var LANE_AGENT_STOP_REASON = "\u26D4 lane agent stop: this is a dispatched lane child (`JOSH_LANE_CHILD`) and a subagent it launched has not finished; ending this turn kills it. Its completion notice arrives only when a tool call ends: wait with one short foreground call at a time (`sleep 20`) until it arrives \u2014 never a loop over its `.output`. Do not notify, stop or repeat your reply.";
1716
+ var ROW = {
1285
1717
  id: "lane-background",
1286
1718
  is_trigger: (call) => is_background_long_run(call),
1287
1719
  reason: LANE_BACKGROUND_REASON,
1288
1720
  decide: () => true,
1721
+ // `pretool-guard.ts` rewrites a lone `josh ship` to the foreground rather than refusing it; this is
1722
+ // the note it attaches, so `rule-value.ts` counts that rewrite as this row's delivery.
1723
+ rewrite_note: FOREGROUND_NOTE,
1724
+ // The Stop hook delivers the same rule at the turn's end, for the background task or subagent the
1725
+ // turn would kill.
1726
+ stop_reasons: [LANE_BACKGROUND_STOP_REASON, LANE_AGENT_STOP_REASON],
1289
1727
  about: {
1290
1728
  title: "Lane child background push",
1291
1729
  topic: "`.claude/skills/workflow-commands/background-commands.md`",
1292
- fires: "a dispatched lane child backgrounding `josh gate` / `git` / `followup` / `ship`; it hands over `pnpm josh ship --detach`. Every occurrence",
1730
+ fires: "a dispatched lane child backgrounding `josh gate` / `git` / `followup` / `ship`; it hands over `pnpm josh ship --detach`; the Stop hook also blocks ending a turn with its background task or subagent unfinished. Every occurrence",
1293
1731
  quiet: "not a lane child, or the foreground `ship --detach`"
1294
1732
  }
1295
1733
  };
1296
1734
  var lane_background = {
1297
1735
  FOREGROUND_NOTE,
1736
+ LANE_AGENT_STOP_REASON,
1298
1737
  LANE_BACKGROUND_REASON,
1299
1738
  LANE_BACKGROUND_STOP_REASON,
1300
- ROW: ROW2,
1739
+ ROW,
1301
1740
  foreground_input,
1302
1741
  is_background_long_run,
1303
1742
  pending_agent_ids,
1304
- pending_background_ids
1743
+ pending_background_ids,
1744
+ pending_command_ids
1745
+ };
1746
+
1747
+ // scripts/rules/stop-rules.ts
1748
+ import { z as z5 } from "zod";
1749
+
1750
+ // scripts/rules/concise-reply.ts
1751
+ import path4 from "node:path";
1752
+ import { fileURLToPath } from "node:url";
1753
+
1754
+ // scripts/rules/reply-prose.ts
1755
+ var CODE_PATTERNS = [
1756
+ /```[\s\S]*?(?:```|$)/gu,
1757
+ /`[^`\n]*`/gu,
1758
+ /\]\([^)\s]*\)/gu,
1759
+ /https?:\/\/\S+/gu
1760
+ ];
1761
+ var ASCII_RUN_PATTERN = /[!-~]+/gu;
1762
+ var STRUCTURE_LINE_PATTERN = /^\s*(?:\||[-*+] |[・•] ?|\d+\. )/u;
1763
+ function prose_of(message) {
1764
+ let text2 = message;
1765
+ for (const pattern of CODE_PATTERNS) text2 = text2.replaceAll(pattern, " ");
1766
+ return text2.replaceAll(ASCII_RUN_PATTERN, (run2) => run2.includes("/") ? " " : run2);
1767
+ }
1768
+ function narrative_of(message) {
1769
+ return prose_of(message).split("\n").filter((line) => !STRUCTURE_LINE_PATTERN.test(line)).join("\n");
1770
+ }
1771
+ var reply_prose = { narrative_of, prose_of };
1772
+
1773
+ // scripts/rules/concise-reply.ts
1774
+ var PROSE_LIMIT = 400;
1775
+ var EXEMPT_WORDS = ["\u8A73\u3057\u304F", "\u5177\u4F53\u7684\u306B", "\u8A73\u7D30"];
1776
+ var COUNTED_PATTERN = /[\p{L}\p{N}]/gu;
1777
+ var PRINCIPLES_FILE = "prompts/collaboration-workflow/principles.md";
1778
+ var SECTION = "concise-reply";
1779
+ var PACKAGE_DIRECTORY = find_package_directory(path4.dirname(fileURLToPath(import.meta.url)));
1780
+ var REASON_OPENING = `\u26D4 concise reply: your reply's prose outside code, tables and lists is over the limit of \`${PRINCIPLES_FILE}\` \u2192 "${SECTION}"`;
1781
+ var KEEP_DECISION = " Keep its `Recommended:` / `Alternative:` lines as they are.";
1782
+ function prose_length(message) {
1783
+ return reply_prose.narrative_of(message).match(COUNTED_PATTERN)?.length ?? 0;
1784
+ }
1785
+ function is_exempt(prompt) {
1786
+ return EXEMPT_WORDS.some((word) => prompt.includes(word));
1787
+ }
1788
+ function is_over(message, prompt) {
1789
+ if (prompt === void 0 || is_exempt(prompt)) return false;
1790
+ return prose_length(message) > PROSE_LIMIT;
1791
+ }
1792
+ function section_text(root) {
1793
+ const markdown = document_section.read_optional(path4.join(root, PRINCIPLES_FILE));
1794
+ return markdown === void 0 ? void 0 : document_section.section(markdown, SECTION)?.text;
1795
+ }
1796
+ function decision_note(message) {
1797
+ return stop_decision.decision_of(message) === void 0 ? "" : KEEP_DECISION;
1798
+ }
1799
+ function build_reason(message, root = PACKAGE_DIRECTORY) {
1800
+ const head = `${REASON_OPENING} (${String(prose_length(message))} of ${String(PROSE_LIMIT)}). Rewrite it shorter as that section says \u2014 do not repeat your previous reply.${decision_note(message)}`;
1801
+ const body = section_text(root);
1802
+ return body === void 0 ? head : `${head}
1803
+
1804
+ ${body}`;
1805
+ }
1806
+ var ROW2 = {
1807
+ id: SECTION,
1808
+ reason: REASON_OPENING,
1809
+ about: {
1810
+ title: "Concise reply",
1811
+ topic: `\`principles.md\` \u2192 "${SECTION}"`,
1812
+ entry: "pnpm josh stop:guard",
1813
+ fires: `a reply whose narrative outside code, tables and lists is over ${String(PROSE_LIMIT)} letters. Blocks the stop`,
1814
+ quiet: "a reply under the limit, a prompt asking for the detail (`\u8A73\u3057\u304F` / `\u5177\u4F53\u7684\u306B` / `\u8A73\u7D30`), a fixed output in a code block, no prompt of the person\u2019s on the transcript tail, or a headless session"
1815
+ }
1816
+ };
1817
+ var concise_reply = {
1818
+ EXEMPT_WORDS,
1819
+ PROSE_LIMIT,
1820
+ REASON_OPENING,
1821
+ ROW: ROW2,
1822
+ build_reason,
1823
+ is_over,
1824
+ prose_length
1825
+ };
1826
+
1827
+ // scripts/rules/filing-offer.ts
1828
+ var OFFER_PATTERNS = [
1829
+ /起票(?:して)?(?:も)?(?:よければ|よろしければ|よいですか|良いですか|よろしいですか|いいですか)/u,
1830
+ /起票(?:しましょうか|しますか|するのが妥当|すべきだと|すべきと|を提案|をご提案)/u,
1831
+ /Issue\s*[化に](?:しましょうか|しますか|してよければ)/iu,
1832
+ /\b(?:shall|should)\s+I\s+(?:file|open|raise)\b/iu,
1833
+ /\b(?:want|like)\s+me\s+to\s+(?:file|open|raise)\b/iu,
1834
+ /\blet\s+me\s+know\s+if\s+you\s+(?:want|would\s+like)\s+(?:me\s+to\s+file|it\s+filed)\b/iu
1835
+ ];
1836
+ var GITHUB_URL_OWNER = /github\.com\/([\w.-]+)\//gu;
1837
+ var OWNER_SEPARATOR = "/";
1838
+ function is_offer_line(line) {
1839
+ return OFFER_PATTERNS.some((pattern) => pattern.test(line));
1840
+ }
1841
+ function offers_filing(message) {
1842
+ return issue_citation.prose_lines(message).some((line) => is_offer_line(line));
1843
+ }
1844
+ function url_owners(message) {
1845
+ return [...message.matchAll(GITHUB_URL_OWNER)].map((match) => match[1] ?? "");
1846
+ }
1847
+ function qualified_owners(message) {
1848
+ return issue_citation.bare_references(message).filter((reference) => reference.includes(OWNER_SEPARATOR)).map((reference) => reference.split(OWNER_SEPARATOR)[0] ?? "");
1849
+ }
1850
+ function mentioned_owners(message) {
1851
+ return [...url_owners(message), ...qualified_owners(message)];
1852
+ }
1853
+ function is_first_party_target(message, session_owner) {
1854
+ if (session_owner === void 0) return false;
1855
+ return mentioned_owners(message).every(
1856
+ (owner) => repo_party.classify(session_owner, owner) === repo_party.FIRST_PARTY
1857
+ );
1858
+ }
1859
+ var filing_offer = { is_first_party_target, mentioned_owners, offers_filing };
1860
+
1861
+ // scripts/rules/reply-language.ts
1862
+ var LATIN_SCRIPT = "Latn";
1863
+ var COMPOUND_SCRIPTS = {
1864
+ Jpan: ["Hira", "Kana", "Hani"],
1865
+ Kore: ["Hang", "Hani"],
1866
+ Hans: ["Hani"],
1867
+ Hant: ["Hani"]
1868
+ };
1869
+ var MIN_PROSE_LETTERS = 20;
1870
+ var MATCH_SHARE_FLOOR = 0.5;
1871
+ var MIN_VOTING_LETTERS = 8;
1872
+ var LETTER_PATTERN = new RegExp("\\p{L}", "gu");
1873
+ var NON_LATIN_LETTER_PATTERN = new RegExp("(?![\\p{Script=Latn}\\p{Script=Common}\\p{Script=Inherited}])\\p{L}", "u");
1874
+ function script_of(lang) {
1875
+ try {
1876
+ return new Intl.Locale(lang).maximize().script;
1877
+ } catch {
1878
+ return void 0;
1879
+ }
1880
+ }
1881
+ function letter_pattern(script) {
1882
+ const scripts = COMPOUND_SCRIPTS[script] ?? [script];
1883
+ const classes = scripts.map((name) => String.raw`\p{Script=${name}}`).join("");
1884
+ try {
1885
+ return new RegExp(`[${classes}]`, "u");
1886
+ } catch {
1887
+ return void 0;
1888
+ }
1889
+ }
1890
+ function letter_count(text2) {
1891
+ return text2.match(LETTER_PATTERN)?.length ?? 0;
1892
+ }
1893
+ function is_line_in_script(line, script, pattern) {
1894
+ if (script === LATIN_SCRIPT) return !NON_LATIN_LETTER_PATTERN.test(line);
1895
+ return pattern.test(line);
1896
+ }
1897
+ function voting_lines(prose) {
1898
+ const lines = prose.split("\n").filter((line) => letter_count(line) > 0);
1899
+ const voting = lines.filter((line) => letter_count(line) >= MIN_VOTING_LETTERS);
1900
+ return voting.length > 0 ? voting : lines;
1901
+ }
1902
+ function matches_share(prose, script, pattern) {
1903
+ const lines = voting_lines(prose);
1904
+ const matched = lines.filter((line) => is_line_in_script(line, script, pattern));
1905
+ return matched.length / lines.length;
1906
+ }
1907
+ function is_mismatch(message, lang) {
1908
+ const script = script_of(lang);
1909
+ if (script === void 0) return false;
1910
+ const pattern = letter_pattern(script);
1911
+ if (pattern === void 0) return false;
1912
+ const prose = reply_prose.prose_of(message);
1913
+ if (letter_count(prose) < MIN_PROSE_LETTERS) return false;
1914
+ return matches_share(prose, script, pattern) < MATCH_SHARE_FLOOR;
1915
+ }
1916
+ var reply_language = { is_mismatch, script_of };
1917
+
1918
+ // scripts/rules/stop-rules.ts
1919
+ var SWITCH_ENV_KEY2 = "JOSH_STOP_GUARD";
1920
+ var BLOCK_DECISION = "block";
1921
+ var stop_payload_schema = z5.object({
1922
+ transcript_path: z5.string().min(1),
1923
+ stop_hook_active: z5.boolean().nullish(),
1924
+ last_assistant_message: z5.string().nullish()
1925
+ });
1926
+ var NO_OUTCOME2 = { reason: void 0, actions: [], blocks_on_failure: false };
1927
+ var STOP_NOTIFY_REASON = '\u26D4 mid-workflow stop notification: this working tree is still held by a run and no `confirmation` Telegram was sent this turn. Send `pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\' --recommended "<choice>" --alternative "<choice>"` (`rule-delivery.md` \u2192 "stop-notify"), then confirm in one line that it was sent \u2014 do not repeat your previous reply.';
1928
+ var HOLD_RELEASE_REASON = "\u26D4 working-tree hold not released: this working tree is clean but its `run:hold` record is still in place. Release it with `pnpm josh run:release <N>` (bare for a `new` entry; `working-tree-hold.md`), then confirm in one line that it was released \u2014 do not repeat your previous reply.";
1929
+ var FILING_OFFER_REASON = '\u26D4 filing offer: your reply offers to file an Issue instead of filing it, and this run is unattended \u2014 nobody is there to answer. Run `pnpm josh issue:file "<title>" --body-file <path>` (Tier A, `observation-filing.md`), then end with the one-line citation of what was filed; do not repeat your previous reply. If it is not worth filing after all, say so in one line instead.';
1930
+ var HEADLESS_WAIT_MARKER = "\u26D4 headless parent:";
1931
+ var HEADLESS_REFUSAL_CAP = 3;
1932
+ var HEADLESS_WAIT_CALLS = ['"command":"pnpm josh lane:await'];
1933
+ var HEADLESS_WAIT_BODY = ' this is a `claude -p` `backlogrun` parent with lanes in flight, and ending this turn kills the process and its background waits. Hand the loop back with `pnpm josh run:carry --cut --owner "$PPID"` (or `--end` when the run is over), then end the turn (`backlogrun-progress.md`).';
1934
+ var HEADLESS_WAIT_REASON = `${HEADLESS_WAIT_MARKER}${HEADLESS_WAIT_BODY}`;
1935
+ var INDEX_COMMAND_PATTERN = /git (?:add|stage|rm --cached|restore --staged|restore -S)\b/u;
1936
+ var PERMISSION_PATTERN = /許可|承認|permission|approve|approval|authorization/iu;
1937
+ var LANE_INDEX_REASON = "\u26D4 lane index permission: this is a dispatched lane child (`JOSH_LANE_CHILD`) and your reply asks a person for permission to change the git index. That is Tier A here (`chain-rule.md`): remove any conflict markers, run `pnpm josh git -y` (it stages and commits, a conflicted merge included) and continue the run \u2014 do not repeat your previous reply.";
1938
+ function asks_index_permission(message) {
1939
+ return INDEX_COMMAND_PATTERN.test(message) && PERMISSION_PATTERN.test(message);
1940
+ }
1941
+ function needs_index_route(context) {
1942
+ return context.lane_child && asks_index_permission(context.message);
1943
+ }
1944
+ function language_note(lang) {
1945
+ return `Write your reply in the session language (JOSH_SESSION_LANG: ${lang}); code, commands and identifiers stay as they are.`;
1946
+ }
1947
+ function build_language_reason(lang) {
1948
+ return `\u26D4 session language: your reply is not written in the session language (JOSH_SESSION_LANG: ${lang}). Rewrite it in that language and keep writing in it on the turns that follow, whatever language a notification, tool output or hook message was in (\`CLAUDE.md\` \u2192 "Output language").`;
1949
+ }
1950
+ function language_reason(context) {
1951
+ if (!reply_language.is_mismatch(context.message, context.session_lang)) return void 0;
1952
+ return build_language_reason(context.session_lang);
1953
+ }
1954
+ function concise_reason(context) {
1955
+ if (!concise_reply.is_over(context.message, context.prompt)) return void 0;
1956
+ return concise_reply.build_reason(context.message);
1957
+ }
1958
+ function is_unattended(context) {
1959
+ return context.headless_agent || context.lane_child || context.backlog_parent;
1960
+ }
1961
+ function needs_filing(context) {
1962
+ if (!is_unattended(context) || context.filed) return false;
1963
+ return filing_offer.offers_filing(context.message) && filing_offer.is_first_party_target(context.message, context.session_owner);
1964
+ }
1965
+ function is_held_idle(context) {
1966
+ if (context.hold_issue === void 0 || !context.hold_owned) return false;
1967
+ return !context.agent_pending && !context.handed_off;
1968
+ }
1969
+ function needs_notify(context) {
1970
+ return is_held_idle(context) && !context.notified && !context.answers_question;
1971
+ }
1972
+ function needs_decision(context) {
1973
+ return needs_notify(context) && stop_decision.decision_of(context.message) === void 0;
1974
+ }
1975
+ function needs_release(context) {
1976
+ return is_held_idle(context) && context.tree_clean && !context.prrun_stopped;
1977
+ }
1978
+ function reply_reason(context) {
1979
+ if (needs_filing(context)) return FILING_OFFER_REASON;
1980
+ if (context.headless_agent) return void 0;
1981
+ return language_reason(context) ?? concise_reason(context);
1982
+ }
1983
+ function decision_reason(context) {
1984
+ return needs_decision(context) ? stop_decision.DECISION_REASON : void 0;
1985
+ }
1986
+ function is_headless_held(context) {
1987
+ if (!context.headless_waiting) return false;
1988
+ return !context.stop_hook_active || context.headless_refusals < HEADLESS_REFUSAL_CAP;
1989
+ }
1990
+ function count_headless_refusals(tail) {
1991
+ const last_wait = Math.max(...HEADLESS_WAIT_CALLS.map((call) => tail.lastIndexOf(call)));
1992
+ return tail.slice(Math.max(last_wait, 0)).split(HEADLESS_WAIT_MARKER).length - 1;
1993
+ }
1994
+ function needs_background_wait(context) {
1995
+ return context.lane_child && context.background_pending;
1996
+ }
1997
+ function background_wait_reason(context) {
1998
+ if (context.command_pending) return lane_background.LANE_BACKGROUND_STOP_REASON;
1999
+ return lane_background.LANE_AGENT_STOP_REASON;
2000
+ }
2001
+ function lane_reason(context) {
2002
+ if (needs_index_route(context)) return LANE_INDEX_REASON;
2003
+ if (needs_background_wait(context)) return background_wait_reason(context);
2004
+ return void 0;
2005
+ }
2006
+ function model_reason(context) {
2007
+ const lane = lane_reason(context);
2008
+ if (lane !== void 0) return lane;
2009
+ const reasons = [reply_reason(context), decision_reason(context)].filter(
2010
+ (reason) => reason !== void 0
2011
+ );
2012
+ return reasons.length === 0 ? void 0 : reasons.join("\n\n");
2013
+ }
2014
+ function notify_actions(context, issue) {
2015
+ const decision = stop_decision.decision_of(context.message);
2016
+ if (decision === void 0 || !needs_notify(context)) return [];
2017
+ return [{ kind: "notify", issue, message: context.message, decision }];
2018
+ }
2019
+ function hold_actions(context) {
2020
+ const issue = context.hold_issue;
2021
+ if (issue === void 0) return [];
2022
+ const notify = notify_actions(context, issue);
2023
+ return needs_release(context) ? [...notify, { kind: "release", issue }] : notify;
2024
+ }
2025
+ function block_reason(context) {
2026
+ if (is_headless_held(context)) return HEADLESS_WAIT_REASON;
2027
+ if (context.stop_hook_active || context.cut_pending) return void 0;
2028
+ return model_reason(context);
2029
+ }
2030
+ function runs_actions(context, reason) {
2031
+ return reason === void 0 && !context.cut_pending && lane_reason(context) === void 0;
2032
+ }
2033
+ function stop_outcome(context) {
2034
+ const reason = block_reason(context);
2035
+ if (!runs_actions(context, reason)) return { ...NO_OUTCOME2, reason };
2036
+ const actions = hold_actions(context);
2037
+ if (actions.length === 0) return NO_OUTCOME2;
2038
+ return { reason, actions, blocks_on_failure: !context.stop_hook_active };
2039
+ }
2040
+ function fallback_reason(action) {
2041
+ return action.kind === "notify" ? STOP_NOTIFY_REASON : HOLD_RELEASE_REASON;
2042
+ }
2043
+ function block_envelope(reason, lang, notice) {
2044
+ const refusal = { decision: BLOCK_DECISION, reason: `${reason} ${language_note(lang)}` };
2045
+ return JSON.stringify(notice === void 0 ? refusal : { ...refusal, systemMessage: notice });
2046
+ }
2047
+ function notice_envelope2(notice) {
2048
+ return JSON.stringify({ systemMessage: notice });
2049
+ }
2050
+ function parse_stop_payload(raw_payload) {
2051
+ return json_value.parse_with(raw_payload, stop_payload_schema);
2052
+ }
2053
+ function is_enabled2() {
2054
+ return hook_decision.is_switch_enabled(SWITCH_ENV_KEY2);
2055
+ }
2056
+ var stop_rules = {
2057
+ FILING_OFFER_REASON,
2058
+ HEADLESS_REFUSAL_CAP,
2059
+ HEADLESS_WAIT_REASON,
2060
+ HOLD_RELEASE_REASON,
2061
+ LANE_INDEX_REASON,
2062
+ NO_OUTCOME: NO_OUTCOME2,
2063
+ STOP_NOTIFY_REASON,
2064
+ SWITCH_ENV_KEY: SWITCH_ENV_KEY2,
2065
+ block_envelope,
2066
+ build_language_reason,
2067
+ count_headless_refusals,
2068
+ fallback_reason,
2069
+ is_enabled: is_enabled2,
2070
+ notice_envelope: notice_envelope2,
2071
+ parse_stop_payload,
2072
+ stop_outcome
1305
2073
  };
1306
2074
 
1307
2075
  // scripts/run/ship/run-ship-detach.ts
1308
2076
  import { randomUUID } from "node:crypto";
1309
2077
  import { setTimeout as sleep } from "node:timers/promises";
1310
- import { z as z4 } from "zod";
1311
- var LOG_PREFIX = "josh-ship-log-";
2078
+ import { z as z6 } from "zod";
2079
+ var LOG_PREFIX = durable_state.PREFIX.SHIP_LOG;
1312
2080
  var PID_PREFIX = "josh-ship-pid-";
1313
2081
  var BODY_PREFIX = "josh-ship-notify-";
1314
2082
  var KEY_SEPARATOR = "-";
@@ -1324,18 +2092,18 @@ var SUPERVISED_VALUE = "1";
1324
2092
  var LAUNCHED = "launched";
1325
2093
  var BUSY = "busy";
1326
2094
  var FAILED = "failed";
1327
- var RESULT_PREFIX = "josh-ship-result-";
2095
+ var RESULT_PREFIX = durable_state.PREFIX.SHIP_RESULT;
1328
2096
  var RECORD_POLL_MS = 25;
1329
2097
  var RECORD_WAIT_ATTEMPTS = 200;
1330
2098
  var CLAIM_WINDOW_MS = 12e4;
1331
- var ship_record_schema = z4.object({
1332
- pid: z4.number().int().positive().optional(),
1333
- process_start: z4.string().optional(),
1334
- launched_at: z4.number().optional(),
1335
- launcher_pid: z4.number().int().positive().optional(),
1336
- launcher_start: z4.string().optional(),
1337
- launch_id: z4.string(),
1338
- result: z4.enum(["success", "failed"]).optional()
2099
+ var ship_record_schema = z6.object({
2100
+ pid: z6.number().int().positive().optional(),
2101
+ process_start: z6.string().optional(),
2102
+ launched_at: z6.number().optional(),
2103
+ launcher_pid: z6.number().int().positive().optional(),
2104
+ launcher_start: z6.string().optional(),
2105
+ launch_id: z6.string(),
2106
+ result: z6.enum(["success", "failed"]).optional()
1339
2107
  });
1340
2108
  var LAUNCH_MARK = " \xB7 started by process ";
1341
2109
  var HEADER_START = "\n";
@@ -1343,10 +2111,10 @@ function key(prefix, request2) {
1343
2111
  return stamp_file.stamp_path(`${prefix}${request2.number}${KEY_SEPARATOR}`, request2.repository);
1344
2112
  }
1345
2113
  function log_path(repository, number) {
1346
- return stamp_file.stamp_path(`${LOG_PREFIX}${number}${KEY_SEPARATOR}`, repository, LOG_SUFFIX);
2114
+ return durable_state.path(`${LOG_PREFIX}${number}${KEY_SEPARATOR}`, repository, LOG_SUFFIX);
1347
2115
  }
1348
2116
  function result_path(repository, number) {
1349
- return stamp_file.stamp_path(`${RESULT_PREFIX}${number}${KEY_SEPARATOR}`, repository);
2117
+ return durable_state.path(`${RESULT_PREFIX}${number}${KEY_SEPARATOR}`, repository);
1350
2118
  }
1351
2119
  function read_record(repository, number) {
1352
2120
  try {
@@ -1373,56 +2141,56 @@ function supervisor_environment(launch_id) {
1373
2141
  [LAUNCH_ID_KEY]: launch_id
1374
2142
  };
1375
2143
  }
1376
- function is_within_claim_window(record2) {
1377
- return record2.launched_at !== void 0 && Date.now() - record2.launched_at < CLAIM_WINDOW_MS;
2144
+ function is_within_claim_window(record3) {
2145
+ return record3.launched_at !== void 0 && Date.now() - record3.launched_at < CLAIM_WINDOW_MS;
1378
2146
  }
1379
- function has_live_identity(pid, record2, read3) {
1380
- if (record2.process_start === void 0) {
1381
- return is_within_claim_window(record2) && process_identity.is_live_pid(pid);
2147
+ function has_live_identity(pid, record3, read4) {
2148
+ if (record3.process_start === void 0) {
2149
+ return is_within_claim_window(record3) && process_identity.is_live_pid(pid);
1382
2150
  }
1383
- return process_identity.is_same_process(pid, record2.process_start, read3) !== false;
2151
+ return process_identity.is_same_process(pid, record3.process_start, read4) !== false;
1384
2152
  }
1385
- function result_of(record2, read3) {
1386
- if (record2.pid === void 0) {
1387
- const { launcher_pid, launcher_start } = record2;
1388
- const is_launcher = process_identity.is_same_process(launcher_pid, launcher_start, read3);
2153
+ function result_of(record3, read4) {
2154
+ if (record3.pid === void 0) {
2155
+ const { launcher_pid, launcher_start } = record3;
2156
+ const is_launcher = process_identity.is_same_process(launcher_pid, launcher_start, read4);
1389
2157
  return is_launcher === false ? "abnormal" : "running";
1390
2158
  }
1391
- if (has_live_identity(record2.pid, record2, read3)) return "running";
1392
- return record2.result ?? "abnormal";
1393
- }
1394
- function read_result(repository, number, read3 = process_identity.read_start) {
1395
- const record2 = read_record(repository, number);
1396
- return record2 === void 0 ? void 0 : {
1397
- launch_id: record2.launch_id,
1398
- launched_at: record2.launched_at,
1399
- result: result_of(record2, read3)
2159
+ if (has_live_identity(record3.pid, record3, read4)) return "running";
2160
+ return record3.result ?? "abnormal";
2161
+ }
2162
+ function read_result(repository, number, read4 = process_identity.read_start) {
2163
+ const record3 = read_record(repository, number);
2164
+ return record3 === void 0 ? void 0 : {
2165
+ launch_id: record3.launch_id,
2166
+ launched_at: record3.launched_at,
2167
+ result: result_of(record3, read4)
1400
2168
  };
1401
2169
  }
1402
2170
  function current_record(repository, number) {
1403
- const record2 = read_record(repository, number);
1404
- return record2?.launch_id === process.env[LAUNCH_ID_KEY] ? record2 : void 0;
2171
+ const record3 = read_record(repository, number);
2172
+ return record3?.launch_id === process.env[LAUNCH_ID_KEY] ? record3 : void 0;
1405
2173
  }
1406
2174
  function running_since(repository, number) {
1407
- const record2 = read_record(repository, number);
1408
- return record2 !== void 0 && result_of(record2, process_identity.read_start) === "running" ? record2.launched_at : void 0;
2175
+ const record3 = read_record(repository, number);
2176
+ return record3 !== void 0 && result_of(record3, process_identity.read_start) === "running" ? record3.launched_at : void 0;
1409
2177
  }
1410
- function is_pending(record2) {
1411
- return record2 !== void 0 && record2.pid === void 0;
2178
+ function is_pending(record3) {
2179
+ return record3 !== void 0 && record3.pid === void 0;
1412
2180
  }
1413
2181
  async function ready_record(repository, number) {
1414
- let record2 = current_record(repository, number);
2182
+ let record3 = current_record(repository, number);
1415
2183
  for (let attempt = 0; attempt < RECORD_WAIT_ATTEMPTS; attempt += 1) {
1416
- if (!is_pending(record2)) return record2;
2184
+ if (!is_pending(record3)) return record3;
1417
2185
  await sleep(RECORD_POLL_MS);
1418
- record2 = current_record(repository, number);
2186
+ record3 = current_record(repository, number);
1419
2187
  }
1420
2188
  return void 0;
1421
2189
  }
1422
2190
  async function update_record(repository, number, fields) {
1423
- const record2 = await ready_record(repository, number);
1424
- if (record2 === void 0) return;
1425
- stamp_file.replace_stamp(result_path(repository, number), { ...record2, ...fields });
2191
+ const record3 = await ready_record(repository, number);
2192
+ if (record3 === void 0) return;
2193
+ stamp_file.replace_stamp(result_path(repository, number), { ...record3, ...fields });
1426
2194
  }
1427
2195
  async function mark_result(repository, number, code) {
1428
2196
  await update_record(repository, number, { result: code === 0 ? "success" : "failed" });
@@ -1456,6 +2224,7 @@ function supervisor_argv(request2) {
1456
2224
  "josh",
1457
2225
  "ship",
1458
2226
  ...review,
2227
+ ...request2.unpinned_ok,
1459
2228
  ...notify_arguments(request2),
1460
2229
  ...request2.body,
1461
2230
  ...cites,
@@ -1540,8 +2309,11 @@ var run_ship_detach = {
1540
2309
  last_launch,
1541
2310
  log_path,
1542
2311
  read_log,
2312
+ read_record,
2313
+ result_path,
1543
2314
  running_since,
1544
- supervisor_argv
2315
+ supervisor_argv,
2316
+ supervisor_environment
1545
2317
  };
1546
2318
 
1547
2319
  // scripts/lane/lane-handoff.ts
@@ -1561,17 +2333,17 @@ async function read_repository() {
1561
2333
  return void 0;
1562
2334
  }
1563
2335
  }
1564
- async function ship_reader(read3) {
2336
+ async function ship_reader(read4) {
1565
2337
  const repository = await read_repository();
1566
- return (issue) => repository !== void 0 && run_ship_detach.read_result(repository, issue, read3)?.result === "running";
2338
+ return (issue) => repository !== void 0 && run_ship_detach.read_result(repository, issue, read4)?.result === "running";
1567
2339
  }
1568
- function is_merge_waiting(repository, issue, read3) {
1569
- if (run_ship_detach.read_result(repository, issue, read3)?.result !== "running") return false;
2340
+ function is_merge_waiting(repository, issue, read4) {
2341
+ if (run_ship_detach.read_result(repository, issue, read4)?.result !== "running") return false;
1570
2342
  return run_ship_stage.read_merge_waiting(run_ship_stage.record_path(repository, issue));
1571
2343
  }
1572
- async function merge_wait_reader(read3) {
2344
+ async function merge_wait_reader(read4) {
1573
2345
  const repository = await read_repository();
1574
- return (issue) => repository !== void 0 && is_merge_waiting(repository, issue, read3);
2346
+ return (issue) => repository !== void 0 && is_merge_waiting(repository, issue, read4);
1575
2347
  }
1576
2348
  function is_place_waiting(issue, directory) {
1577
2349
  const repository = git_common_directory.repository(directory);
@@ -1614,6 +2386,7 @@ var AUTO_OK_LABEL = "auto-ok";
1614
2386
  var NEEDS_HUMAN_REVIEW_LABEL = "needs-human-review";
1615
2387
  var ALREADY_DONE_LABEL = "already-done";
1616
2388
  var MEASURED_LABEL = "measured";
2389
+ var MEASURED_UNMET_LABEL = "measured:unmet";
1617
2390
  var RELEASE_LABEL = "release";
1618
2391
  var DEFERRED_LABEL = "deferred";
1619
2392
  var NOT_BEFORE_LABEL = "not-before";
@@ -1720,6 +2493,11 @@ var WORKFLOW_LABELS = [
1720
2493
  color: "6f42c1",
1721
2494
  description: "Measurement-only \u2014 a run recorded the result and closed it"
1722
2495
  },
2496
+ {
2497
+ name: MEASURED_UNMET_LABEL,
2498
+ color: "6f42c1",
2499
+ description: "The measurement fell short of the expected effect \u2014 retire or shrink the mechanism"
2500
+ },
1723
2501
  {
1724
2502
  name: DEFERRED_LABEL,
1725
2503
  color: "d4c5f9",
@@ -1765,17 +2543,20 @@ function label_name_of(labels, wanted) {
1765
2543
  function has_label_name(labels, wanted) {
1766
2544
  return label_name_of(labels, wanted) !== void 0;
1767
2545
  }
2546
+ function parked_label_of(labels) {
2547
+ return [...PARKED_LABELS].find((label) => has_label_name(labels, label));
2548
+ }
1768
2549
  function has_parked_label(labels) {
1769
- return [...PARKED_LABELS].some((label) => has_label_name(labels, label));
2550
+ return parked_label_of(labels) !== void 0;
1770
2551
  }
1771
2552
 
1772
2553
  // scripts/run/park/run-park.ts
1773
- import { z as z5 } from "zod";
2554
+ import { z as z7 } from "zod";
1774
2555
  var PARK_PREFIX = "josh-run-park-";
1775
2556
  var PARK_MAX_AGE_MS = backlog_budget.DEFAULT_IDLE_MS;
1776
- var run_park_schema = z5.object({
1777
- issue: z5.string(),
1778
- parked_at: z5.string()
2557
+ var run_park_schema = z7.object({
2558
+ issue: z7.string(),
2559
+ parked_at: z7.string()
1779
2560
  });
1780
2561
  function parse_park(raw) {
1781
2562
  return json_value.parse_with(raw, run_park_schema);
@@ -1804,15 +2585,153 @@ function clear(issue, directory) {
1804
2585
  function is_parked_sync(issue, directory, now = /* @__PURE__ */ new Date()) {
1805
2586
  const repository = git_common_directory.repository(directory);
1806
2587
  if (repository === void 0) return false;
1807
- const read3 = stamp_record.classify(
2588
+ const read4 = stamp_record.classify(
1808
2589
  stamp_file.read_stamp_text(park_path(repository, issue)),
1809
2590
  PARK_SPEC,
1810
2591
  now
1811
2592
  );
1812
- return read3.kind === "carried" && read3.record.issue === issue;
2593
+ return read4.kind === "carried" && read4.record.issue === issue;
1813
2594
  }
1814
2595
  var run_park = { PARK_MAX_AGE_MS, clear, is_parked_sync, record };
1815
2596
 
2597
+ // scripts/rules/git-argv.ts
2598
+ var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/u;
2599
+ var WRAPPER_WORDS = /* @__PURE__ */ new Set([
2600
+ "pnpm",
2601
+ "npx",
2602
+ "exec",
2603
+ "run",
2604
+ "sudo",
2605
+ "command",
2606
+ "env",
2607
+ "time"
2608
+ ]);
2609
+ var FLAGGED_WRAPPERS = /* @__PURE__ */ new Set(["time"]);
2610
+ var WRAPPER_VALUE_FLAGS = /* @__PURE__ */ new Set(["-o", "-f", "--output", "--format"]);
2611
+ var SUBSHELL_OPEN = "(";
2612
+ var VALUE_TAKING_GLOBALS = /* @__PURE__ */ new Set([
2613
+ "-C",
2614
+ "-c",
2615
+ "--git-dir",
2616
+ "--work-tree",
2617
+ "--namespace",
2618
+ "--exec-path",
2619
+ "--super-prefix"
2620
+ ]);
2621
+ var FLAG_PREFIX = "-";
2622
+ var GIT_COMMAND = "git";
2623
+ var NEXT = 1;
2624
+ var AFTER_VALUE = 2;
2625
+ var WORD = /(?:\\.|'[^']*'|"(?:\\.|[^"\\])*"|\S)+/gsu;
2626
+ function words_of(segment) {
2627
+ return segment.match(WORD) ?? [];
2628
+ }
2629
+ var PATH_SEPARATOR = "/";
2630
+ function program_name(word) {
2631
+ return word.slice(word.lastIndexOf(PATH_SEPARATOR) + NEXT);
2632
+ }
2633
+ function is_wrapper_token(word) {
2634
+ return word === SUBSHELL_OPEN || ENV_ASSIGNMENT.test(word) || WRAPPER_WORDS.has(program_name(word));
2635
+ }
2636
+ function wrapper_flag_width(word = "") {
2637
+ if (!word.startsWith(FLAG_PREFIX)) return 0;
2638
+ return WRAPPER_VALUE_FLAGS.has(word) ? AFTER_VALUE : NEXT;
2639
+ }
2640
+ function after_wrapper_word(words, index) {
2641
+ let next = index + NEXT;
2642
+ if (!FLAGGED_WRAPPERS.has(program_name(words[index] ?? ""))) return next;
2643
+ let width = wrapper_flag_width(words[next]);
2644
+ while (width > 0) {
2645
+ next += width;
2646
+ width = wrapper_flag_width(words[next]);
2647
+ }
2648
+ return next;
2649
+ }
2650
+ function after_wrapper(words) {
2651
+ let index = 0;
2652
+ while (index < words.length && is_wrapper_token(words[index] ?? "")) {
2653
+ index = after_wrapper_word(words, index);
2654
+ }
2655
+ return index;
2656
+ }
2657
+ function global_step(flag) {
2658
+ return VALUE_TAKING_GLOBALS.has(flag) ? AFTER_VALUE : NEXT;
2659
+ }
2660
+ function after_globals(words, start) {
2661
+ let index = start;
2662
+ while (index < words.length) {
2663
+ const flag = words[index] ?? "";
2664
+ if (!flag.startsWith(FLAG_PREFIX)) return index;
2665
+ index += global_step(flag);
2666
+ }
2667
+ return index;
2668
+ }
2669
+ function program_of(segment) {
2670
+ const words = words_of(segment);
2671
+ const command_index = after_wrapper(words);
2672
+ const word = words[command_index];
2673
+ if (word === void 0) return void 0;
2674
+ return { name: program_name(word), args: words.slice(command_index + NEXT) };
2675
+ }
2676
+ function arguments_of(segment, command) {
2677
+ const call = program_of(segment);
2678
+ return call?.name === command ? call.args : void 0;
2679
+ }
2680
+ function parse(segment) {
2681
+ const words = arguments_of(segment, GIT_COMMAND);
2682
+ if (words === void 0) return void 0;
2683
+ const subcommand_index = after_globals(words, 0);
2684
+ return { subcommand: words[subcommand_index], args: words.slice(subcommand_index + NEXT) };
2685
+ }
2686
+ var SHORT_CLUSTER = /^-[A-Za-z]+$/u;
2687
+ function short_cluster_has(token, letters) {
2688
+ if (!SHORT_CLUSTER.test(token)) return false;
2689
+ const cluster = token.slice(FLAG_PREFIX.length);
2690
+ for (let index = 0; index < letters.length; index += 1) {
2691
+ if (cluster.includes(letters.charAt(index))) return true;
2692
+ }
2693
+ return false;
2694
+ }
2695
+ var git_argv = { arguments_of, parse, program_of, short_cluster_has };
2696
+
2697
+ // scripts/rules/bash-triggers.ts
2698
+ function on_bash_command(is_match) {
2699
+ return function is_trigger(call) {
2700
+ if (call.name !== cost_blocks.BASH_TOOL) return false;
2701
+ return is_match(time_shell.bash_command(call.input));
2702
+ };
2703
+ }
2704
+ var ISSUE_CREATE_COMMAND = /\bgh\s+(?:\S+\s+)*?issue\s+create\b/u;
2705
+ var ISSUES_ENDPOINT = /repos\/[^\s'"]*\/issues(?=$|["'\s])/u;
2706
+ var TITLE_FIELD = /(?:-f|-F|--field|--raw-field)\s*'?title=/u;
2707
+ var GH_API_COMMAND = /\bgh\s+api\b/u;
2708
+ var SHELL_SCRIPT_RUNNER = /\b(?:(?:ba|z|da|k)?sh\s+(?:-\w+\s+)*-\w*c\b|eval\b)/u;
2709
+ function spoken_line(command) {
2710
+ if (time_shell.has_heredoc(command)) return command;
2711
+ const plain = time_shell.unquoted(command);
2712
+ return SHELL_SCRIPT_RUNNER.test(plain) ? command : plain;
2713
+ }
2714
+ var LEADING_SHELL_SCRIPT_RUNNER = new RegExp(`^${SHELL_SCRIPT_RUNNER.source}`, "u");
2715
+ var QUOTE_MARK = /["']/gu;
2716
+ function shell_script_of(segment) {
2717
+ const call = git_argv.program_of(segment);
2718
+ if (call === void 0) return void 0;
2719
+ const spoken = [call.name, ...call.args].join(" ");
2720
+ const runner = LEADING_SHELL_SCRIPT_RUNNER.exec(spoken);
2721
+ if (runner === null) return void 0;
2722
+ return spoken.slice(runner[0].length).replaceAll(QUOTE_MARK, "").trim();
2723
+ }
2724
+ function is_direct_filing(command) {
2725
+ const spoken = spoken_line(command);
2726
+ if (ISSUE_CREATE_COMMAND.test(spoken)) return true;
2727
+ return GH_API_COMMAND.test(spoken) && ISSUES_ENDPOINT.test(command) && TITLE_FIELD.test(command);
2728
+ }
2729
+ var FILING_NAMES = /* @__PURE__ */ new Set(["issue:file"]);
2730
+ function is_issue_filing(command) {
2731
+ return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, FILING_NAMES));
2732
+ }
2733
+ var bash_triggers = { is_direct_filing, is_issue_filing, on_bash_command, shell_script_of };
2734
+
1816
2735
  // scripts/rules/lane-park.ts
1817
2736
  var NOTIFY_COMMANDS = /* @__PURE__ */ new Set(["notify"]);
1818
2737
  var CONFIRMATION_TASK_TYPE = /(?:^|\s)--task-type[=\s]+["']?confirmation\b/u;
@@ -1852,7 +2771,7 @@ function records_the_park(command) {
1852
2771
  if (APPLIES_NEEDS_DECISION.test(command)) return true;
1853
2772
  return shell_segments.segments_of(command).some((segment) => is_park_command(segment));
1854
2773
  }
1855
- var LANE_PARK_REASON = "\u26D4 lane child park: this session is a dispatched lane child, so a stop for a person parks first: `pnpm josh run:park <N> --body-file <path>` (question, options, stash \u2014 it applies `needs-decision`); a `needs-human-review` / `already-done` stop already is. Reissue this notify, once per run (`.claude/skills/workflow-commands/pre-gate-cut.md`).";
2774
+ var LANE_PARK_REASON = "\u26D4 lane child park: this session is a dispatched lane child, so a stop for a person parks first: `pnpm josh run:park <N> --body-file <path>` (question, options, stash \u2014 it applies `needs-decision`); a `needs-human-review` / `already-done` stop already is. The park is the stop: no notify after it. Refused once per run (`.claude/skills/workflow-commands/pre-gate-cut.md`).";
1856
2775
  var ROW3 = {
1857
2776
  id: "lane-park",
1858
2777
  is_trigger: bash_triggers.on_bash_command(is_unparked_stop),
@@ -1879,19 +2798,19 @@ var lane_park = {
1879
2798
 
1880
2799
  // scripts/cost-runtime/codex-usage.ts
1881
2800
  import { globSync, readFileSync as readFileSync2 } from "node:fs";
1882
- import path from "node:path";
1883
- import { z as z6 } from "zod";
2801
+ import path5 from "node:path";
2802
+ import { z as z8 } from "zod";
1884
2803
  var THREAD_ID_PATTERN = /^[A-Za-z0-9-]+$/u;
1885
- var SESSION_META_SCHEMA = z6.looseObject({
1886
- type: z6.literal("session_meta"),
1887
- payload: z6.looseObject({ id: z6.string(), cwd: z6.string() })
2804
+ var SESSION_META_SCHEMA = z8.looseObject({
2805
+ type: z8.literal("session_meta"),
2806
+ payload: z8.looseObject({ id: z8.string(), cwd: z8.string() })
1888
2807
  });
1889
- var TOTAL_USAGE_SCHEMA = z6.looseObject({ input_tokens: z6.number().nonnegative() });
1890
- var TOKEN_COUNT_SCHEMA = z6.looseObject({
1891
- type: z6.literal("event_msg"),
1892
- payload: z6.looseObject({
1893
- type: z6.literal("token_count"),
1894
- info: z6.looseObject({
2808
+ var TOTAL_USAGE_SCHEMA = z8.looseObject({ input_tokens: z8.number().nonnegative() });
2809
+ var TOKEN_COUNT_SCHEMA = z8.looseObject({
2810
+ type: z8.literal("event_msg"),
2811
+ payload: z8.looseObject({
2812
+ type: z8.literal("token_count"),
2813
+ info: z8.looseObject({
1895
2814
  total_token_usage: TOTAL_USAGE_SCHEMA,
1896
2815
  last_token_usage: TOTAL_USAGE_SCHEMA
1897
2816
  })
@@ -1900,52 +2819,197 @@ var TOKEN_COUNT_SCHEMA = z6.looseObject({
1900
2819
  function same_project(cwd, target) {
1901
2820
  return cost_transcript.session_cwd(cwd) === cost_transcript.session_cwd(target);
1902
2821
  }
1903
- function billed_per_request(usages) {
1904
- return usages.map((usage) => usage.last_token_usage.input_tokens);
2822
+ function billed_per_request(usages) {
2823
+ return usages.map((usage) => usage.last_token_usage.input_tokens);
2824
+ }
2825
+ function measurement_from(content, target, thread_id) {
2826
+ const values = content.split("\n").map((line) => json_value.parse_or_undefined(line));
2827
+ const metadata = values.flatMap((value) => {
2828
+ const parsed = SESSION_META_SCHEMA.safeParse(value);
2829
+ return parsed.success ? [parsed.data] : [];
2830
+ });
2831
+ const usages = values.flatMap((value) => {
2832
+ const parsed = TOKEN_COUNT_SCHEMA.safeParse(value);
2833
+ return parsed.success ? [parsed.data.payload.info] : [];
2834
+ });
2835
+ const session = metadata.find((entry) => entry.payload.id === thread_id);
2836
+ if (session === void 0 || usages.length === 0 || !same_project(session.payload.cwd, target)) {
2837
+ return void 0;
2838
+ }
2839
+ return { billed_input_per_request: billed_per_request(usages) };
2840
+ }
2841
+ function rollout_files(home, thread_id) {
2842
+ if (!THREAD_ID_PATTERN.test(thread_id)) return [];
2843
+ return globSync(`sessions/**/*${thread_id}.jsonl`, { cwd: home }).map(
2844
+ (file) => path5.join(home, file)
2845
+ );
2846
+ }
2847
+ function read_measurement(file, target, thread_id) {
2848
+ try {
2849
+ return measurement_from(readFileSync2(file, "utf8"), target, thread_id);
2850
+ } catch {
2851
+ return void 0;
2852
+ }
2853
+ }
2854
+ function latest_measurement(files, target, thread_id) {
2855
+ for (const file of files.toReversed()) {
2856
+ const measured = read_measurement(file, target, thread_id);
2857
+ if (measured !== void 0) return measured;
2858
+ }
2859
+ return void 0;
2860
+ }
2861
+ function measurement(target, default_home, environment = process.env) {
2862
+ const thread_id = environment["CODEX_THREAD_ID"];
2863
+ if (thread_id === void 0) return void 0;
2864
+ const home = environment["CODEX_HOME"] ?? path5.join(default_home, ".codex");
2865
+ return latest_measurement(rollout_files(home, thread_id), target, thread_id);
2866
+ }
2867
+ var codex_usage = { measurement };
2868
+
2869
+ // scripts/cost-runtime/cost-pricing.ts
2870
+ var CACHE_WRITE_5M_MULTIPLIER = 1.25;
2871
+ var CACHE_WRITE_1H_MULTIPLIER = 2;
2872
+ var DEFAULT_CACHE_READ_MULTIPLIER = 0.1;
2873
+ var HALVED_CACHE_READ_MULTIPLIER = 0.05;
2874
+ var QUARTERED_CACHE_READ_MULTIPLIER = 0.025;
2875
+ var TOKENS_PER_MILLION = 1e6;
2876
+ var LONG_PROMPT_SUFFIX = ":long-prompt";
2877
+ var LONG_PROMPT_THRESHOLD = 1e5;
2878
+ var MODEL_PRICES = {
2879
+ "claude-fable-5-1": {
2880
+ input: 10,
2881
+ output: 50,
2882
+ cache_read_multiplier: QUARTERED_CACHE_READ_MULTIPLIER
2883
+ },
2884
+ "claude-mythos-5-1": {
2885
+ input: 10,
2886
+ output: 50,
2887
+ cache_read_multiplier: QUARTERED_CACHE_READ_MULTIPLIER
2888
+ },
2889
+ "claude-fable-5": { input: 10, output: 50 },
2890
+ "claude-mythos-5": { input: 10, output: 50 },
2891
+ "claude-opus-5-5": { input: 4, output: 20, cache_read_multiplier: HALVED_CACHE_READ_MULTIPLIER },
2892
+ "claude-opus-5": { input: 5, output: 25 },
2893
+ "claude-opus-4-8": { input: 5, output: 25 },
2894
+ "claude-opus-4-7": { input: 5, output: 25 },
2895
+ "claude-opus-4-6": { input: 5, output: 25 },
2896
+ "claude-sonnet-5-5": {
2897
+ input: 2,
2898
+ output: 10,
2899
+ cache_read_multiplier: HALVED_CACHE_READ_MULTIPLIER
2900
+ },
2901
+ "claude-sonnet-5": { input: 2, output: 10 },
2902
+ "claude-sonnet-4-6": { input: 3, output: 15 },
2903
+ "claude-haiku-5-5": { input: 0.1, output: 0.5 },
2904
+ [`claude-haiku-5-5${LONG_PROMPT_SUFFIX}`]: { input: 0.5, output: 2.5 },
2905
+ "claude-haiku-4-5": { input: 1, output: 5 }
2906
+ };
2907
+ var CONTEXT_MARKER = "[";
2908
+ var DATE_SUFFIX = /-\d{8}$/u;
2909
+ function known_id(model) {
2910
+ const base = model.split(CONTEXT_MARKER)[0] ?? model;
2911
+ return [base, base.replace(DATE_SUFFIX, "")].find((id) => Object.hasOwn(MODEL_PRICES, id));
2912
+ }
2913
+ function resolve_price(model) {
2914
+ const id = known_id(model);
2915
+ return id === void 0 ? void 0 : MODEL_PRICES[id];
2916
+ }
2917
+ function cache_read_multiplier(price) {
2918
+ return price.cache_read_multiplier ?? DEFAULT_CACHE_READ_MULTIPLIER;
2919
+ }
2920
+ function cache_read_multiplier_of(model) {
2921
+ const price = model === void 0 ? void 0 : resolve_price(model);
2922
+ return price === void 0 ? DEFAULT_CACHE_READ_MULTIPLIER : cache_read_multiplier(price);
2923
+ }
2924
+ function estimate_cost(totals, price) {
2925
+ const weighted_input = totals.input_tokens + totals.cache_write_5m_tokens * CACHE_WRITE_5M_MULTIPLIER + totals.cache_write_1h_tokens * CACHE_WRITE_1H_MULTIPLIER + totals.cache_read_tokens * cache_read_multiplier(price);
2926
+ return (weighted_input * price.input + totals.output_tokens * price.output) / TOKENS_PER_MILLION;
2927
+ }
2928
+ function priced(tokens, rate) {
2929
+ return tokens * rate / TOKENS_PER_MILLION;
2930
+ }
2931
+ function estimate_composition(totals, price) {
2932
+ return {
2933
+ input_usd: priced(totals.input_tokens, price.input),
2934
+ cache_write_5m_usd: priced(
2935
+ totals.cache_write_5m_tokens * CACHE_WRITE_5M_MULTIPLIER,
2936
+ price.input
2937
+ ),
2938
+ cache_write_1h_usd: priced(
2939
+ totals.cache_write_1h_tokens * CACHE_WRITE_1H_MULTIPLIER,
2940
+ price.input
2941
+ ),
2942
+ cache_read_usd: priced(totals.cache_read_tokens * cache_read_multiplier(price), price.input),
2943
+ output_usd: priced(totals.output_tokens, price.output)
2944
+ };
2945
+ }
2946
+ function to_model_cost(model, totals) {
2947
+ const price = resolve_price(model);
2948
+ if (price === void 0) return { model, totals };
2949
+ return { model, totals, cost_usd: estimate_cost(totals, price) };
1905
2950
  }
1906
- function measurement_from(content, target, thread_id) {
1907
- const values = content.split("\n").map((line) => json_value.parse_or_undefined(line));
1908
- const metadata = values.flatMap((value) => {
1909
- const parsed = SESSION_META_SCHEMA.safeParse(value);
1910
- return parsed.success ? [parsed.data] : [];
1911
- });
1912
- const usages = values.flatMap((value) => {
1913
- const parsed = TOKEN_COUNT_SCHEMA.safeParse(value);
1914
- return parsed.success ? [parsed.data.payload.info] : [];
1915
- });
1916
- const session = metadata.find((entry) => entry.payload.id === thread_id);
1917
- if (session === void 0 || usages.length === 0 || !same_project(session.payload.cwd, target)) {
1918
- return void 0;
2951
+ function billed_model(usage) {
2952
+ const long_id = `${known_id(usage.model) ?? usage.model}${LONG_PROMPT_SUFFIX}`;
2953
+ const is_long = cost_usage.billed_input(usage.totals) > LONG_PROMPT_THRESHOLD;
2954
+ return is_long && Object.hasOwn(MODEL_PRICES, long_id) ? long_id : usage.model;
2955
+ }
2956
+ function cost_by_model(records) {
2957
+ const grouped = /* @__PURE__ */ new Map();
2958
+ for (const record3 of records) {
2959
+ const model = billed_model(record3);
2960
+ const running = grouped.get(model) ?? cost_usage.EMPTY_TOTALS;
2961
+ grouped.set(model, cost_usage.add_totals(running, record3.totals));
1919
2962
  }
1920
- return { billed_input_per_request: billed_per_request(usages) };
2963
+ return [...grouped].map(([model, totals]) => to_model_cost(model, totals));
1921
2964
  }
1922
- function rollout_files(home, thread_id) {
1923
- if (!THREAD_ID_PATTERN.test(thread_id)) return [];
1924
- return globSync(`sessions/**/*${thread_id}.jsonl`, { cwd: home }).map(
1925
- (file) => path.join(home, file)
1926
- );
2965
+ function total_cost(costs) {
2966
+ return {
2967
+ usd: costs.reduce((sum, entry) => sum + (entry.cost_usd ?? 0), 0),
2968
+ unpriced: costs.filter((entry) => entry.cost_usd === void 0).map((entry) => entry.model)
2969
+ };
1927
2970
  }
1928
- function read_measurement(file, target, thread_id) {
1929
- try {
1930
- return measurement_from(readFileSync2(file, "utf8"), target, thread_id);
1931
- } catch {
1932
- return void 0;
1933
- }
2971
+ function cost_of(records) {
2972
+ return total_cost(cost_by_model(records)).usd;
1934
2973
  }
1935
- function latest_measurement(files, target, thread_id) {
1936
- for (const file of files.toReversed()) {
1937
- const measured = read_measurement(file, target, thread_id);
1938
- if (measured !== void 0) return measured;
1939
- }
1940
- return void 0;
2974
+ var cost_pricing = {
2975
+ CACHE_WRITE_5M_MULTIPLIER,
2976
+ CACHE_WRITE_1H_MULTIPLIER,
2977
+ DEFAULT_CACHE_READ_MULTIPLIER,
2978
+ LONG_PROMPT_THRESHOLD,
2979
+ resolve_price,
2980
+ cache_read_multiplier_of,
2981
+ estimate_cost,
2982
+ estimate_composition,
2983
+ cost_by_model,
2984
+ total_cost,
2985
+ cost_of
2986
+ };
2987
+
2988
+ // scripts/cost-runtime/context-cut-payback.ts
2989
+ var POST_CUT_CONTEXT = 6e4;
2990
+ var EXPECTED_REMAINING_REQUESTS = 23;
2991
+ function write_over_read(model) {
2992
+ return cost_pricing.CACHE_WRITE_5M_MULTIPLIER / cost_pricing.cache_read_multiplier_of(model);
1941
2993
  }
1942
- function measurement(target, default_home, environment = process.env) {
1943
- const thread_id = environment["CODEX_THREAD_ID"];
1944
- if (thread_id === void 0) return void 0;
1945
- const home = environment["CODEX_HOME"] ?? path.join(default_home, ".codex");
1946
- return latest_measurement(rollout_files(home, thread_id), target, thread_id);
2994
+ function break_even_context(model, remaining = EXPECTED_REMAINING_REQUESTS) {
2995
+ if (remaining <= 0) return Infinity;
2996
+ return Math.round(POST_CUT_CONTEXT * (1 + write_over_read(model) / remaining));
1947
2997
  }
1948
- var codex_usage = { measurement };
2998
+ function break_even_requests(current_context, model) {
2999
+ const saved_per_request = current_context - POST_CUT_CONTEXT;
3000
+ if (saved_per_request <= 0) return Infinity;
3001
+ return POST_CUT_CONTEXT * write_over_read(model) / saved_per_request;
3002
+ }
3003
+ function pays_back(current_context, model, remaining = EXPECTED_REMAINING_REQUESTS) {
3004
+ return current_context > break_even_context(model, remaining);
3005
+ }
3006
+ var context_cut_payback = {
3007
+ POST_CUT_CONTEXT,
3008
+ EXPECTED_REMAINING_REQUESTS,
3009
+ break_even_context,
3010
+ break_even_requests,
3011
+ pays_back
3012
+ };
1949
3013
 
1950
3014
  // scripts/cost-runtime/cost-corpus.ts
1951
3015
  function accumulate_missing(sessions) {
@@ -1987,8 +3051,8 @@ function owner_sets(corpus, meta) {
1987
3051
  }
1988
3052
  function pairs_with_inherited(session, inherited, is_delegated) {
1989
3053
  return cost_attribute.group_by_issue(session.records).flatMap(
1990
- (group) => group.records.map((record2) => ({
1991
- record: record2,
3054
+ (group) => group.records.map((record3) => ({
3055
+ record: record3,
1992
3056
  issue: group.issue === cost_attribute.UNATTRIBUTED_KEY ? inherited : group.issue,
1993
3057
  baseline_tokens: session.baseline_tokens,
1994
3058
  session_id: session.session_id,
@@ -2070,7 +3134,7 @@ var UNDER_VERDICT = "under";
2070
3134
  function per_request_cost(measurement2) {
2071
3135
  return measurement2.billed_input_per_request.at(-1) ?? 0;
2072
3136
  }
2073
- function classify2(measurement2, limit) {
3137
+ function classify3(measurement2, limit) {
2074
3138
  return per_request_cost(measurement2) > limit ? OVER_VERDICT : UNDER_VERDICT;
2075
3139
  }
2076
3140
  function over_summary(measurement2, limit) {
@@ -2082,22 +3146,22 @@ function report_over(measurement2, limit) {
2082
3146
  console.error("No requests in this session; there is nothing to hand off.");
2083
3147
  return FAILURE_EXIT_CODE;
2084
3148
  }
2085
- console.info(classify2(measurement2, limit));
3149
+ console.info(classify3(measurement2, limit));
2086
3150
  console.error(over_summary(measurement2, limit));
2087
3151
  return 0;
2088
3152
  }
2089
3153
  var cost_verdict = {
2090
3154
  OVER_VERDICT,
2091
3155
  UNDER_VERDICT,
2092
- classify: classify2,
3156
+ classify: classify3,
2093
3157
  per_request_cost,
2094
3158
  report_over
2095
3159
  };
2096
3160
 
2097
3161
  // scripts/cost-runtime/transcript-cwd.ts
2098
- import path2 from "node:path";
3162
+ import path6 from "node:path";
2099
3163
  function resolve(target, cwd) {
2100
- return target === void 0 ? cwd : path2.resolve(target);
3164
+ return target === void 0 ? cwd : path6.resolve(target);
2101
3165
  }
2102
3166
  var transcript_cwd = { resolve };
2103
3167
 
@@ -2105,6 +3169,7 @@ var transcript_cwd = { resolve };
2105
3169
  var FAILURE_EXIT_CODE2 = 1;
2106
3170
  var USAGE = "Usage: josh cost (--cut | --over <tokens-per-request>) [--path <dir>]";
2107
3171
  var UNMEASURABLE_VERDICT = "unmeasurable";
3172
+ var CUT_LIMIT = "cut";
2108
3173
  function to_threshold(raw) {
2109
3174
  if (raw === void 0 || raw.trim() === "") return void 0;
2110
3175
  const parsed = Number(raw);
@@ -2113,19 +3178,24 @@ function to_threshold(raw) {
2113
3178
  function is_unparsed(raw, parsed) {
2114
3179
  return parsed === void 0 && raw !== void 0;
2115
3180
  }
2116
- function selected_threshold(values) {
2117
- if (values.cut === true) return CONTEXT_CUT_THRESHOLD;
2118
- return to_threshold(values.over);
3181
+ function cut_threshold(measurement2) {
3182
+ return context_cut_payback.break_even_context(measurement2?.model);
3183
+ }
3184
+ function limit_of(limit, measurement2) {
3185
+ return limit === CUT_LIMIT ? cut_threshold(measurement2) : limit;
2119
3186
  }
2120
3187
  function threshold_option(over) {
2121
3188
  return over === void 0 ? {} : { over };
2122
3189
  }
2123
- function path_option(path15) {
2124
- return path15 === void 0 ? {} : { path: path15 };
3190
+ function path_option(path19) {
3191
+ return path19 === void 0 ? {} : { path: path19 };
3192
+ }
3193
+ function cut_options(values) {
3194
+ return values.over === void 0 ? { over: CUT_LIMIT, ...path_option(values.path) } : void 0;
2125
3195
  }
2126
3196
  function to_options(values) {
2127
- if (values.cut === true && values.over !== void 0) return void 0;
2128
- const over = selected_threshold(values);
3197
+ if (values.cut === true) return cut_options(values);
3198
+ const over = to_threshold(values.over);
2129
3199
  if (is_unparsed(values.over, over)) return void 0;
2130
3200
  return { ...threshold_option(over), ...path_option(values.path) };
2131
3201
  }
@@ -2144,15 +3214,16 @@ function report_empty(cwd, session_id) {
2144
3214
  return FAILURE_EXIT_CODE2;
2145
3215
  }
2146
3216
  function provider_of(environment) {
2147
- const resolved = agent_role_profile.resolve_provider(environment);
2148
- if (resolved.kind !== "provider" || resolved.is_default === true) return void 0;
2149
- return resolved.provider;
3217
+ const resolved2 = agent_role_profile.resolve_provider(environment);
3218
+ if (resolved2.kind !== "provider" || resolved2.is_default === true) return void 0;
3219
+ return resolved2.provider;
2150
3220
  }
2151
3221
  function measurement_of(session) {
2152
3222
  return {
2153
3223
  billed_input_per_request: session.records.map(
2154
- (record2) => cost_usage.billed_input(record2.totals)
2155
- )
3224
+ (record3) => cost_usage.billed_input(record3.totals)
3225
+ ),
3226
+ model: session.records.at(-1)?.model
2156
3227
  };
2157
3228
  }
2158
3229
  function anthropic_own(target, environment) {
@@ -2183,12 +3254,16 @@ function measure(target, provider, environment) {
2183
3254
  return provider === "openai" ? codex_usage.measurement(target, cost_transcript.home_directory(), environment) : anthropic_measurement(target, environment);
2184
3255
  }
2185
3256
  function run_over_anthropic(own, target, limit) {
2186
- if (own.kind === "measured") return cost_verdict.report_over(own.measurement, limit);
3257
+ if (own.kind === "measured") {
3258
+ return cost_verdict.report_over(own.measurement, limit_of(limit, own.measurement));
3259
+ }
2187
3260
  return report_empty(target, own.kind === "absent" ? own.session_id : void 0);
2188
3261
  }
2189
3262
  function run_over_codex(target, limit, environment) {
2190
3263
  const measurement2 = measure(target, "openai", environment);
2191
- if (measurement2 !== void 0) return cost_verdict.report_over(measurement2, limit);
3264
+ if (measurement2 !== void 0) {
3265
+ return cost_verdict.report_over(measurement2, limit_of(limit, measurement2));
3266
+ }
2192
3267
  console.error(`No Codex usage found for the current OpenAI thread under ${target}.`);
2193
3268
  return FAILURE_EXIT_CODE2;
2194
3269
  }
@@ -2202,13 +3277,13 @@ function verdict_of(measurement2) {
2202
3277
  if (measurement2 === void 0 || measurement2.billed_input_per_request.length === 0) {
2203
3278
  return UNMEASURABLE_VERDICT;
2204
3279
  }
2205
- return cost_verdict.classify(measurement2, CONTEXT_CUT_THRESHOLD);
3280
+ return cost_verdict.classify(measurement2, cut_threshold(measurement2));
2206
3281
  }
2207
3282
  function figures_of(measurement2) {
2208
3283
  const verdict = verdict_of(measurement2);
2209
3284
  const is_priced = measurement2 !== void 0 && verdict !== UNMEASURABLE_VERDICT;
2210
3285
  const cost = is_priced ? cost_verdict.per_request_cost(measurement2) : void 0;
2211
- return { verdict, cost, threshold: CONTEXT_CUT_THRESHOLD };
3286
+ return { verdict, cost, threshold: cut_threshold(measurement2) };
2212
3287
  }
2213
3288
  function session_figures(cwd = process.cwd(), environment = process.env) {
2214
3289
  const provider = provider_of(environment);
@@ -2291,7 +3366,7 @@ function is_uncut_gate(command, state = current_state2()) {
2291
3366
  if (!runs_the_gate(command) || uncut_lane_issue(state) === void 0) return false;
2292
3367
  return warrants_the_cut(state.context_verdict());
2293
3368
  }
2294
- var PRE_GATE_CUT_REASON = "\u26D4 pre-gate cut: this checkout is a lane dispatched for this issue and the gate is the boundary to cut at. Issue `pnpm josh run:cut <N>`: on `cut`, end the turn immediately \u2014 no Telegram is owed; on any other verdict (`not-a-lane`, `busy`, \u2026), reissue the gate \u2014 this fires once per run (`.claude/skills/workflow-commands/pre-gate-cut.md`).";
3369
+ var PRE_GATE_CUT_REASON = "\u26D4 pre-gate cut: this checkout is a lane dispatched for this issue and the gate is the boundary to cut at. Issue `pnpm josh run:cut <N>`: on `cut`, end the turn immediately \u2014 no Telegram is owed; on `split-unresolved`, answer its notice and rerun it; on any other verdict (`not-a-lane`, `busy`, \u2026), reissue the gate \u2014 this fires once per run (`.claude/skills/workflow-commands/pre-gate-cut.md`).";
2295
3370
  function reaches_the_pre_gate_boundary(call, _turn, run2) {
2296
3371
  if (!bash_triggers.on_bash_command(asks_about_the_cut)(call)) return false;
2297
3372
  return run2.some((issued) => bash_triggers.on_bash_command(claims_the_hold)(issued));
@@ -2307,42 +3382,6 @@ var pre_gate_cut = {
2307
3382
  uncut_lane_issue
2308
3383
  };
2309
3384
 
2310
- // scripts/discovery/repo-party.ts
2311
- var FIRST_PARTY = "first-party";
2312
- var THIRD_PARTY = "third-party";
2313
- var UNKNOWN = "unknown";
2314
- var GH_REPO_ENV_KEY = "GH_REPO";
2315
- var GH_REPO_PATH_SEGMENTS = 2;
2316
- function classify3(session_owner, target) {
2317
- if (session_owner === void 0 || target === void 0) return UNKNOWN;
2318
- return session_owner.toLowerCase() === target.toLowerCase() ? FIRST_PARTY : THIRD_PARTY;
2319
- }
2320
- function target_owner(owner_repo) {
2321
- return repo_origin.parse_origin_url(`https://${repo_origin.GITHUB_HOST}/${owner_repo}`)?.owner;
2322
- }
2323
- function current_owner(repository_path = process.cwd()) {
2324
- return repo_discovery.resolve_current_owner(repository_path);
2325
- }
2326
- function gh_repo_owners(gh_repo) {
2327
- return [target_owner(gh_repo.split("/").slice(-GH_REPO_PATH_SEGMENTS).join("/"))];
2328
- }
2329
- function remote_owners(repository_path) {
2330
- return repo_discovery.read_remote_urls(repository_path).map((url) => repo_origin.parse_origin_url(url)?.owner);
2331
- }
2332
- function placeholder_owners(repository_path = process.cwd(), environment = process.env) {
2333
- const gh_repo = environment[GH_REPO_ENV_KEY];
2334
- return gh_repo === void 0 || gh_repo === "" ? remote_owners(repository_path) : gh_repo_owners(gh_repo);
2335
- }
2336
- var repo_party = {
2337
- FIRST_PARTY,
2338
- THIRD_PARTY,
2339
- UNKNOWN,
2340
- classify: classify3,
2341
- current_owner,
2342
- placeholder_owners,
2343
- target_owner
2344
- };
2345
-
2346
3385
  // scripts/gh/git-gh-repo.ts
2347
3386
  async function repo_get_name_with_owner() {
2348
3387
  try {
@@ -2393,8 +3432,8 @@ function is_selected(rest, request2) {
2393
3432
  return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request2.body_term);
2394
3433
  }
2395
3434
  async function fetch_page(request2, page) {
2396
- const path15 = listing_path(request2, page);
2397
- const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path15 }));
3435
+ const path19 = listing_path(request2, page);
3436
+ const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path19 }));
2398
3437
  return {
2399
3438
  selected: rows.filter((row) => is_selected(row, request2)),
2400
3439
  is_full: rows.length >= PER_PAGE
@@ -2489,8 +3528,8 @@ ${issue_watch_diff.RUN_MARKER}` }),
2489
3528
  jq_filter: HTML_URL_FILTER
2490
3529
  });
2491
3530
  }
2492
- async function issue_try_comment(issue_number, body) {
2493
- return await did_write_succeed(async () => await issue_comment(issue_number, body));
3531
+ async function issue_try_comment(issue_number, body, repo) {
3532
+ return await did_write_succeed(async () => await issue_comment(issue_number, body, repo));
2494
3533
  }
2495
3534
  async function close_with(issue_number, comment, state) {
2496
3535
  return await did_write_succeed(async () => {
@@ -2537,7 +3576,9 @@ function issue_create_request(input) {
2537
3576
  ...labels !== void 0 && { labels },
2538
3577
  body: input.body
2539
3578
  }),
2540
- jq_filter: HTML_URL_FILTER
3579
+ jq_filter: HTML_URL_FILTER,
3580
+ // A 502 can follow an issue GitHub already opened, and a retry would open a second one.
3581
+ should_retry: false
2541
3582
  };
2542
3583
  }
2543
3584
  async function issue_create_with_label(input) {
@@ -2647,10 +3688,10 @@ var LABELLED_FIELDS = "number,body,stateReason";
2647
3688
  var BACKLOG_SCAN_FIELDS = `number,title,body,labels,${BLOCKED_BY_COUNT_FIELD}`;
2648
3689
  var NUMBER_TITLE_AND_LABELS_FIELDS = "number,title,labels";
2649
3690
  var SUMMARY_FIELDS = "number,title,labels,createdAt";
2650
- var PICKUP_FIELDS = `${SUMMARY_FIELDS},blockedBy,body`;
3691
+ var PICKUP_FIELDS = `${SUMMARY_FIELDS},blockedBy,body,${gh_author_association.AUTHOR_FIELDS}`;
2651
3692
  var { issue_list: issue_list2 } = git_gh_issue_list;
2652
3693
  async function issue_list_recent(limit) {
2653
- return await issue_list2({ json_fields: SUMMARY_FIELDS, limit });
3694
+ return await issue_list2({ json_fields: `${SUMMARY_FIELDS},body`, limit });
2654
3695
  }
2655
3696
  async function issue_list_open_bodies(limit) {
2656
3697
  return await issue_list2({ json_fields: BACKLOG_SCAN_FIELDS, limit });
@@ -2702,18 +3743,18 @@ var git_gh_issue = {
2702
3743
 
2703
3744
  // scripts/ci/pr-classification.ts
2704
3745
  import { readFileSync as readFileSync3 } from "node:fs";
2705
- import { z as z7 } from "zod";
3746
+ import { z as z9 } from "zod";
2706
3747
  var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
2707
3748
  var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
2708
3749
  var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
2709
3750
  var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
2710
3751
  var LABEL_NAMES_FILTER2 = ".[].name";
2711
- var LABEL = z7.object({ name: z7.string() });
2712
- var USER = z7.object({ login: z7.string() });
2713
- var PULL_REQUEST = z7.object({ number: z7.number(), user: USER });
2714
- var EVENT = z7.object({
3752
+ var LABEL = z9.object({ name: z9.string() });
3753
+ var USER = z9.object({ login: z9.string() });
3754
+ var PULL_REQUEST = z9.object({ number: z9.number(), user: USER });
3755
+ var EVENT = z9.object({
2715
3756
  pull_request: PULL_REQUEST,
2716
- repository: z7.object({ full_name: z7.string() })
3757
+ repository: z9.object({ full_name: z9.string() })
2717
3758
  });
2718
3759
  function classification_error(labels, author) {
2719
3760
  const legacy = labels.filter((label) => LEGACY_LABELS.has(label.toLowerCase()));
@@ -2736,7 +3777,7 @@ function release_candidates(candidates) {
2736
3777
  return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL);
2737
3778
  }
2738
3779
  function issue_candidates(issue_json) {
2739
- const issue = z7.object({ labels: z7.array(LABEL), body: z7.string().nullable() }).parse(JSON.parse(issue_json));
3780
+ const issue = z9.object({ labels: z9.array(LABEL), body: z9.string().nullable() }).parse(JSON.parse(issue_json));
2740
3781
  const names = issue.labels.map((label) => label.name.toLowerCase());
2741
3782
  const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
2742
3783
  const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
@@ -2759,10 +3800,10 @@ function select_issue_classification(issue_json) {
2759
3800
  return label;
2760
3801
  }
2761
3802
  function fetch_current_labels(repository, pull_number) {
2762
- const path15 = git_gh_api_path.issue_labels_api_path(String(pull_number), repository);
3803
+ const path19 = git_gh_api_path.issue_labels_api_path(String(pull_number), repository);
2763
3804
  try {
2764
3805
  const names = git_gh_exec.exec_gh_api_sync({
2765
- path: path15,
3806
+ path: path19,
2766
3807
  should_paginate: true,
2767
3808
  jq_filter: LABEL_NAMES_FILTER2
2768
3809
  });
@@ -2795,25 +3836,35 @@ async function sleep2(duration_ms) {
2795
3836
  async function wait_between(wait, interval_ms, attempt, attempts) {
2796
3837
  if (attempt < attempts - 1) await wait(interval_ms);
2797
3838
  }
2798
- async function poll_until(is_done, options) {
3839
+ async function poll_for(read4, options) {
2799
3840
  const wait = options.sleeper ?? sleep2;
2800
3841
  for (let attempt = 0; attempt < options.attempts; attempt += 1) {
2801
- if (await is_done()) return true;
3842
+ const value = await read4();
3843
+ if (value !== void 0) return value;
2802
3844
  await wait_between(wait, options.interval_ms, attempt, options.attempts);
2803
3845
  }
2804
- return false;
3846
+ return void 0;
3847
+ }
3848
+ async function poll_until(is_done, options) {
3849
+ const is_reached = await poll_for(async () => await is_done() ? true : void 0, options);
3850
+ return is_reached === true;
3851
+ }
3852
+ async function wait_for(read4, options) {
3853
+ const value = await poll_for(read4, { ...options, attempts: Infinity });
3854
+ if (value === void 0) throw new Error("an unbounded poll ran out of attempts");
3855
+ return value;
2805
3856
  }
2806
- var poll = { poll_until, sleep: sleep2 };
3857
+ var poll = { poll_for, poll_until, sleep: sleep2, wait_for };
2807
3858
 
2808
3859
  // scripts/gh/git-gh-branch-rules.ts
2809
- import { z as z8 } from "zod";
3860
+ import { z as z10 } from "zod";
2810
3861
  var REQUIRED_STATUS_CHECKS_RULE = "required_status_checks";
2811
- var required_check_schema = z8.looseObject({ context: z8.string() });
2812
- var rule_parameters_schema = z8.looseObject({
2813
- required_status_checks: z8.array(required_check_schema).optional()
3862
+ var required_check_schema = z10.looseObject({ context: z10.string() });
3863
+ var rule_parameters_schema = z10.looseObject({
3864
+ required_status_checks: z10.array(required_check_schema).optional()
2814
3865
  });
2815
- var branch_rule_schema = z8.looseObject({
2816
- type: z8.string(),
3866
+ var branch_rule_schema = z10.looseObject({
3867
+ type: z10.string(),
2817
3868
  parameters: rule_parameters_schema.nullish()
2818
3869
  });
2819
3870
  var required_contexts_by_branch = /* @__PURE__ */ new Map();
@@ -2844,33 +3895,33 @@ async function read_required_contexts(branch_name) {
2844
3895
  var git_gh_branch_rules = { read_required_contexts };
2845
3896
 
2846
3897
  // scripts/gh/git-gh-poll-read.ts
2847
- import { z as z9 } from "zod";
3898
+ import { z as z11 } from "zod";
2848
3899
  var UNREADABLE_POLL_MESSAGE = "gh api could not read a merge-gate poll";
2849
3900
  var PAGE_SIZE = 100;
2850
3901
  var kept_by_path = /* @__PURE__ */ new Map();
2851
- var page_schema = z9.object({ total_count: z9.number() });
2852
- function unreadable(path15, answer) {
3902
+ var page_schema = z11.object({ total_count: z11.number() });
3903
+ function unreadable(path19, answer) {
2853
3904
  const cause = answer.kind === "unreadable" ? answer.cause : void 0;
2854
3905
  const reason = cause === void 0 ? "" : `: ${error_text.message_of(cause)}`;
2855
- return new Error(`${UNREADABLE_POLL_MESSAGE}: ${path15}${reason}`, { cause });
3906
+ return new Error(`${UNREADABLE_POLL_MESSAGE}: ${path19}${reason}`, { cause });
2856
3907
  }
2857
- function to_body(path15, answer) {
2858
- const previous = kept_by_path.get(path15);
3908
+ function to_body(path19, answer) {
3909
+ const previous = kept_by_path.get(path19);
2859
3910
  if (previous !== void 0 && answer.kind === "unchanged") return previous.body;
2860
- if (answer.kind !== "changed") throw unreadable(path15, answer);
2861
- if (answer.etag !== void 0) kept_by_path.set(path15, { etag: answer.etag, body: answer.body });
3911
+ if (answer.kind !== "changed") throw unreadable(path19, answer);
3912
+ if (answer.etag !== void 0) kept_by_path.set(path19, { etag: answer.etag, body: answer.body });
2862
3913
  return answer.body;
2863
3914
  }
2864
- async function read(path15) {
2865
- const answer = await git_gh_conditional.read_conditional(path15, kept_by_path.get(path15)?.etag);
2866
- return to_body(path15, answer);
3915
+ async function read(path19) {
3916
+ const answer = await git_gh_conditional.read_conditional(path19, kept_by_path.get(path19)?.etag);
3917
+ return to_body(path19, answer);
2867
3918
  }
2868
3919
  function is_whole_listing(page) {
2869
3920
  const total = json_value.parse_with(page, page_schema)?.total_count;
2870
3921
  return total !== void 0 && total <= PAGE_SIZE;
2871
3922
  }
2872
- async function read_object_pages(path15) {
2873
- const full_path = `${path15}${FULL_PAGE_QUERY}`;
3923
+ async function read_object_pages(path19) {
3924
+ const full_path = `${path19}${FULL_PAGE_QUERY}`;
2874
3925
  const page = await read(full_path);
2875
3926
  if (is_whole_listing(page)) return `[${page}]`;
2876
3927
  return await git_gh_exec.exec_gh_api({
@@ -2884,15 +3935,45 @@ function forget_poll_reads() {
2884
3935
  }
2885
3936
  var git_gh_poll_read = { UNREADABLE_POLL_MESSAGE, forget_poll_reads, read, read_object_pages };
2886
3937
 
3938
+ // scripts/gh/git-gh-pr-graphql-state.ts
3939
+ import { z as z12 } from "zod";
3940
+ var GRAPHQL_PATH = "graphql";
3941
+ var MERGE_STATE_QUERY = "query($id: ID!) { node(id: $id) { ... on PullRequest { mergeStateStatus } } }";
3942
+ var NO_MERGE_STATE_MESSAGE = "The merge state cannot be judged: GitHub refuses this pull request as too many files changed, and GraphQL did not answer its mergeStateStatus";
3943
+ var merge_state_node_schema = z12.looseObject({ mergeStateStatus: z12.string() });
3944
+ var merge_state_data_schema = z12.looseObject({ node: merge_state_node_schema });
3945
+ var merge_state_response_schema = z12.looseObject({ data: merge_state_data_schema });
3946
+ async function query_merge_state(node_id) {
3947
+ const json = await git_gh_exec.exec_gh_api({
3948
+ path: GRAPHQL_PATH,
3949
+ body: JSON.stringify({ query: MERGE_STATE_QUERY, variables: { id: node_id } })
3950
+ });
3951
+ return parse_json_object_or_undefined(json, merge_state_response_schema)?.data.node.mergeStateStatus;
3952
+ }
3953
+ async function try_query_merge_state(node_id) {
3954
+ if (node_id === void 0) return void 0;
3955
+ try {
3956
+ return await query_merge_state(node_id);
3957
+ } catch {
3958
+ return void 0;
3959
+ }
3960
+ }
3961
+ async function read_merge_state_status(listed, detail_error) {
3962
+ const state = await try_query_merge_state(listed.node_id);
3963
+ if (state === void 0) throw new Error(NO_MERGE_STATE_MESSAGE, { cause: detail_error });
3964
+ return state;
3965
+ }
3966
+ var git_gh_pr_graphql_state = { read_merge_state_status };
3967
+
2887
3968
  // scripts/gh/git-gh-pr-review.ts
2888
- import { z as z10 } from "zod";
3969
+ import { z as z13 } from "zod";
2889
3970
  var REVIEW_APPROVED = "APPROVED";
2890
3971
  var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
2891
3972
  var REVIEW_NONE = "";
2892
3973
  var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
2893
- var rest_review_schema = z10.looseObject({
2894
- state: z10.string().optional(),
2895
- user: z10.looseObject({ login: z10.string().optional() }).nullish()
3974
+ var rest_review_schema = z13.looseObject({
3975
+ state: z13.string().optional(),
3976
+ user: z13.looseObject({ login: z13.string().optional() }).nullish()
2896
3977
  });
2897
3978
  var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
2898
3979
  function parse_rest_reviews(reviews_json) {
@@ -2925,18 +4006,18 @@ function to_review_decision(reviews_json) {
2925
4006
  var git_gh_pr_review = { to_review_decision };
2926
4007
 
2927
4008
  // scripts/gh/git-gh-pr-rollup.ts
2928
- import { z as z11 } from "zod";
4009
+ import { z as z14 } from "zod";
2929
4010
  var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
2930
4011
  var TYPE_NAME_KEY = "__typename";
2931
- var rollup_element_schema = z11.looseObject({});
2932
- var check_runs_page_schema = z11.looseObject({
4012
+ var rollup_element_schema = z14.looseObject({});
4013
+ var check_runs_page_schema = z14.looseObject({
2933
4014
  // Required, not optional: GitHub always names the key, and accepting a page without one would
2934
4015
  // answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
2935
- check_runs: z11.array(rollup_element_schema)
4016
+ check_runs: z14.array(rollup_element_schema)
2936
4017
  });
2937
- var id_holder_schema = z11.object({ id: z11.number() });
2938
- var status_page_schema = z11.looseObject({
2939
- statuses: z11.array(rollup_element_schema)
4018
+ var id_holder_schema = z14.object({ id: z14.number() });
4019
+ var status_page_schema = z14.looseObject({
4020
+ statuses: z14.array(rollup_element_schema)
2940
4021
  });
2941
4022
  var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
2942
4023
  var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
@@ -3002,9 +4083,9 @@ var git_gh_pr_rollup = {
3002
4083
 
3003
4084
  // scripts/gh/git-gh-pr-snapshot.ts
3004
4085
  var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
3005
- async function read_array_pages(path15) {
4086
+ async function read_array_pages(path19) {
3006
4087
  return await git_gh_exec.exec_gh_api({
3007
- path: `${path15}${FULL_PAGE_QUERY}`,
4088
+ path: `${path19}${FULL_PAGE_QUERY}`,
3008
4089
  should_paginate: true
3009
4090
  });
3010
4091
  }
@@ -3021,8 +4102,8 @@ async function read_status_check_rollup(commit_sha) {
3021
4102
  return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
3022
4103
  }
3023
4104
  async function pr_get_review_decision(pr_number) {
3024
- const path15 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
3025
- return git_gh_pr_review.to_review_decision(await read_array_pages(path15));
4105
+ const path19 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
4106
+ return git_gh_pr_review.to_review_decision(await read_array_pages(path19));
3026
4107
  }
3027
4108
  async function build_checks_fields(pull) {
3028
4109
  const head_sha = read_head_sha(pull);
@@ -3037,12 +4118,19 @@ async function build_checks_fields(pull) {
3037
4118
  requiredContexts: required_contexts
3038
4119
  };
3039
4120
  }
4121
+ async function read_detail_or_listed(branch_name, pr_number) {
4122
+ try {
4123
+ return await read_pull(pr_number);
4124
+ } catch (error) {
4125
+ const listed = await read_listed_pull(error, branch_name, pr_number);
4126
+ if (listed === void 0) throw error;
4127
+ const state = await git_gh_pr_graphql_state.read_merge_state_status(listed, error);
4128
+ return { ...listed, mergeable_state: state };
4129
+ }
4130
+ }
3040
4131
  async function resolve_pull(branch_name) {
3041
4132
  const pr_number = await require_pr_number(branch_name);
3042
- const json = await git_gh_exec.exec_gh_api({
3043
- path: git_gh_api_path.pull_api_path(String(pr_number))
3044
- });
3045
- return { pr_number, pull: git_gh_pr_rest.parse_rest_pull(json) };
4133
+ return { pr_number, pull: await read_detail_or_listed(branch_name, pr_number) };
3046
4134
  }
3047
4135
  async function pr_get_checks_snapshot(branch_name) {
3048
4136
  const { pr_number, pull } = await resolve_pull(branch_name);
@@ -3141,9 +4229,9 @@ async function put_merge(pr_number, head_sha) {
3141
4229
  function is_head_moved(error) {
3142
4230
  return gh_failure.failure_of(error)?.status === HEAD_MOVED_STATUS;
3143
4231
  }
3144
- async function read_merge_state(pr_number) {
4232
+ async function read_merge_state(branch_name, pr_number) {
3145
4233
  try {
3146
- return git_gh_pr_rest.is_merged(await read_pull(pr_number));
4234
+ return git_gh_pr_rest.is_merged(await read_pull_or_listed(branch_name, pr_number, read_pull));
3147
4235
  } catch (error) {
3148
4236
  console.warn(`Could not read the pull request back: ${String(error)}`);
3149
4237
  return void 0;
@@ -3152,9 +4240,9 @@ async function read_merge_state(pr_number) {
3152
4240
  async function sleep_between_read_backs(attempt) {
3153
4241
  if (attempt < MERGE_READ_BACK_ATTEMPTS - 1) await poll.sleep(MERGE_READ_BACK_INTERVAL_MS);
3154
4242
  }
3155
- async function has_merge_landed(pr_number, merge_error) {
4243
+ async function has_merge_landed(branch_name, pr_number, merge_error) {
3156
4244
  for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
3157
- const is_merged = await read_merge_state(pr_number);
4245
+ const is_merged = await read_merge_state(branch_name, pr_number);
3158
4246
  if (is_merged !== void 0) return is_merged;
3159
4247
  await sleep_between_read_backs(attempt);
3160
4248
  }
@@ -3165,7 +4253,7 @@ async function pr_merge(branch_name, head_sha) {
3165
4253
  try {
3166
4254
  await put_merge(pr_number, head_sha);
3167
4255
  } catch (error) {
3168
- if (!await has_merge_landed(pr_number, error)) throw error;
4256
+ if (!await has_merge_landed(branch_name, pr_number, error)) throw error;
3169
4257
  }
3170
4258
  }
3171
4259
  var git_gh_pr = {
@@ -3181,12 +4269,14 @@ var git_gh_pr = {
3181
4269
  };
3182
4270
 
3183
4271
  // scripts/gh/git-gh-pr-auto-merge.ts
3184
- var GRAPHQL_PATH = "graphql";
3185
4272
  var OPEN_PULLS_QUERY = `${git_gh_api_path.FULL_PAGE_QUERY}&state=open`;
3186
4273
  var MISSING_NODE_ID_MESSAGE = "gh api answered a pull request without a node_id";
3187
4274
  var ENABLE_AUTO_MERGE_MUTATION = "mutation($id: ID!, $head: GitObjectID!) { enablePullRequestAutoMerge(input: { pullRequestId: $id, mergeMethod: MERGE, expectedHeadOid: $head }) { clientMutationId } }";
4275
+ async function read_branch_pull(branch_name) {
4276
+ return await read_pull_or_listed(branch_name, await require_pr_number(branch_name), read_pull);
4277
+ }
3188
4278
  async function pr_enable_auto_merge(branch_name, head_sha) {
3189
- const pull = await read_pull(await require_pr_number(branch_name));
4279
+ const pull = await read_branch_pull(branch_name);
3190
4280
  if (pull.node_id === void 0) throw new Error(MISSING_NODE_ID_MESSAGE);
3191
4281
  await git_gh_exec.exec_gh_api({
3192
4282
  path: GRAPHQL_PATH,
@@ -3198,7 +4288,7 @@ async function pr_enable_auto_merge(branch_name, head_sha) {
3198
4288
  }
3199
4289
  async function pr_is_merged(branch_name) {
3200
4290
  try {
3201
- return git_gh_pr_rest.is_merged(await read_pull(await require_pr_number(branch_name)));
4291
+ return git_gh_pr_rest.is_merged(await read_branch_pull(branch_name));
3202
4292
  } catch (error) {
3203
4293
  error_text.trace_swallowed("git_gh_pr_auto_merge.pr_is_merged", error);
3204
4294
  return false;
@@ -3304,16 +4394,16 @@ function to_exhaustion_error(last_read_failure) {
3304
4394
  }
3305
4395
  async function attempt_pr_success_poll(input) {
3306
4396
  const evaluator = input.options.evaluator ?? MERGE_GATE_EVALUATOR;
3307
- const read3 = await read_pr_state(input, evaluator);
3308
- if (read3.kind === "unreadable") return await retry_after_unreadable(input, read3.cause);
4397
+ const read4 = await read_pr_state(input, evaluator);
4398
+ if (read4.kind === "unreadable") return await retry_after_unreadable(input, read4.cause);
3309
4399
  const classification = classify_poll_result({
3310
- snapshot: read3.snapshot,
4400
+ snapshot: read4.snapshot,
3311
4401
  stable_count: input.stable_count,
3312
4402
  required_stable_reads: input.options.required_stable_reads,
3313
4403
  evaluator
3314
4404
  });
3315
4405
  if (classification.is_done) {
3316
- return { snapshot: read3.snapshot, next_stable_count: 0, next_failure_count: 0 };
4406
+ return { snapshot: read4.snapshot, next_stable_count: 0, next_failure_count: 0 };
3317
4407
  }
3318
4408
  await sleep_before_next_attempt(input);
3319
4409
  return { next_stable_count: classification.next_stable_count, next_failure_count: 0 };
@@ -3437,7 +4527,7 @@ function get_repo_name_with_owner_within(timeout_ms) {
3437
4527
  var gh_spawn = { get_repo_name_with_owner, get_repo_name_with_owner_within };
3438
4528
 
3439
4529
  // scripts/issue/issue-state.ts
3440
- import { z as z12 } from "zod";
4530
+ import { z as z15 } from "zod";
3441
4531
  var NO_LABELS = "(none)";
3442
4532
  var LABEL_SEPARATOR = ", ";
3443
4533
  var STATE_LABEL = "state: ";
@@ -3447,9 +4537,9 @@ var YES = "yes";
3447
4537
  var NO = "no";
3448
4538
  var ISSUE_LABEL = "issue: ";
3449
4539
  var HUMAN_REVIEW_LABELS = /* @__PURE__ */ new Set([NEEDS_HUMAN_REVIEW_LABEL]);
3450
- var label_schema = z12.looseObject({ name: z12.string() });
3451
- var issue_state_schema = z12.looseObject({
3452
- state: z12.string(),
4540
+ var label_schema = z15.looseObject({ name: z15.string() });
4541
+ var issue_state_schema = z15.looseObject({
4542
+ state: z15.string(),
3453
4543
  labels: label_schema.array().optional()
3454
4544
  });
3455
4545
  function parse_issue_state(json) {
@@ -3479,7 +4569,11 @@ function format_attributed_issue_state(issue_number, state) {
3479
4569
  return `${ISSUE_LABEL}${issue_number}
3480
4570
  ${format_issue_state(state)}`;
3481
4571
  }
4572
+ function is_planned(read4) {
4573
+ return read4.kind === "state" && has_label_name(read4.state.labels, PLANNED_LABEL);
4574
+ }
3482
4575
  var issue_state = {
4576
+ is_planned,
3483
4577
  NO_LABELS,
3484
4578
  parse_issue_state,
3485
4579
  format_issue_state,
@@ -3487,32 +4581,32 @@ var issue_state = {
3487
4581
  };
3488
4582
 
3489
4583
  // scripts/run/run-liveness.ts
3490
- import { readFileSync as readFileSync4, realpathSync, statSync } from "node:fs";
4584
+ import { readFileSync as readFileSync4, realpathSync, statSync as statSync2 } from "node:fs";
3491
4585
  import { homedir, tmpdir } from "node:os";
3492
- import path3 from "node:path";
4586
+ import path7 from "node:path";
3493
4587
  import { setTimeout as sleep3 } from "node:timers/promises";
3494
4588
 
3495
4589
  // scripts/agent/agent-event.ts
3496
- import { z as z15 } from "zod";
4590
+ import { z as z18 } from "zod";
3497
4591
 
3498
4592
  // scripts/agent/claude-result-event.ts
3499
- import { z as z14 } from "zod";
4593
+ import { z as z17 } from "zod";
3500
4594
 
3501
4595
  // scripts/agent/interactive-ask.ts
3502
- import { z as z13 } from "zod";
4596
+ import { z as z16 } from "zod";
3503
4597
  var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion"]);
3504
4598
  function is_interactive_tool(name) {
3505
4599
  return INTERACTIVE_TOOLS.has(name);
3506
4600
  }
3507
- var OPTION_SCHEMA = z13.looseObject({ label: z13.string().optional() });
3508
- var QUESTION_SCHEMA = z13.looseObject({
3509
- question: z13.string().optional(),
3510
- options: z13.array(OPTION_SCHEMA).optional()
4601
+ var OPTION_SCHEMA = z16.looseObject({ label: z16.string().optional() });
4602
+ var QUESTION_SCHEMA = z16.looseObject({
4603
+ question: z16.string().optional(),
4604
+ options: z16.array(OPTION_SCHEMA).optional()
3511
4605
  });
3512
- var ASK_INPUT_SCHEMA = z13.looseObject({ questions: z13.array(QUESTION_SCHEMA).optional() });
3513
- var DENIAL_SCHEMA = z13.looseObject({
3514
- tool_name: z13.string().optional(),
3515
- tool_input: z13.unknown().optional()
4606
+ var ASK_INPUT_SCHEMA = z16.looseObject({ questions: z16.array(QUESTION_SCHEMA).optional() });
4607
+ var DENIAL_SCHEMA = z16.looseObject({
4608
+ tool_name: z16.string().optional(),
4609
+ tool_input: z16.unknown().optional()
3516
4610
  });
3517
4611
  var UNKNOWN_QUESTION = "(question text unavailable)";
3518
4612
  var OPTION_SEPARATOR = " / ";
@@ -3541,20 +4635,20 @@ function refused_ask_of(permission_denials) {
3541
4635
  var interactive_ask = { is_interactive_tool, refused_ask_of };
3542
4636
 
3543
4637
  // scripts/agent/claude-result-event.ts
3544
- var RESULT_EVENT_SCHEMA = z14.looseObject({
3545
- type: z14.literal("result"),
3546
- is_error: z14.unknown().optional(),
3547
- subtype: z14.unknown().optional(),
3548
- num_turns: z14.unknown().optional(),
3549
- permission_denials: z14.unknown().optional(),
3550
- result: z14.unknown().optional(),
3551
- session_id: z14.unknown().optional(),
3552
- usage: z14.unknown().optional()
4638
+ var RESULT_EVENT_SCHEMA = z17.looseObject({
4639
+ type: z17.literal("result"),
4640
+ is_error: z17.unknown().optional(),
4641
+ subtype: z17.unknown().optional(),
4642
+ num_turns: z17.unknown().optional(),
4643
+ permission_denials: z17.unknown().optional(),
4644
+ result: z17.unknown().optional(),
4645
+ session_id: z17.unknown().optional(),
4646
+ usage: z17.unknown().optional()
3553
4647
  });
3554
- var USAGE_SCHEMA = z14.looseObject({
3555
- input_tokens: z14.unknown().optional(),
3556
- cache_read_input_tokens: z14.unknown().optional(),
3557
- output_tokens: z14.unknown().optional()
4648
+ var USAGE_SCHEMA = z17.looseObject({
4649
+ input_tokens: z17.unknown().optional(),
4650
+ cache_read_input_tokens: z17.unknown().optional(),
4651
+ output_tokens: z17.unknown().optional()
3558
4652
  });
3559
4653
  function usable_reason(value) {
3560
4654
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
@@ -3594,25 +4688,25 @@ var claude_result_event = { decode };
3594
4688
 
3595
4689
  // scripts/agent/agent-event.ts
3596
4690
  var ASSISTANT_TYPE = "assistant";
3597
- var CLAUDE_INIT_SCHEMA = z15.looseObject({ type: z15.literal("system"), subtype: z15.literal("init") });
3598
- var USAGE_SCHEMA2 = z15.looseObject({
3599
- input_tokens: z15.number().optional(),
3600
- cached_input_tokens: z15.number().optional(),
3601
- output_tokens: z15.number().optional()
4691
+ var CLAUDE_INIT_SCHEMA = z18.looseObject({ type: z18.literal("system"), subtype: z18.literal("init") });
4692
+ var USAGE_SCHEMA2 = z18.looseObject({
4693
+ input_tokens: z18.number().optional(),
4694
+ cached_input_tokens: z18.number().optional(),
4695
+ output_tokens: z18.number().optional()
3602
4696
  });
3603
- var CODEX_START_SCHEMA = z15.looseObject({ type: z15.enum(["thread.started", "turn.started"]) });
3604
- var CODEX_PROGRESS_SCHEMA = z15.looseObject({
3605
- type: z15.enum(["item.started", "item.updated", "item.completed"]),
3606
- item: z15.looseObject({ type: z15.string(), text: z15.string().optional() }).optional()
4697
+ var CODEX_START_SCHEMA = z18.looseObject({ type: z18.enum(["thread.started", "turn.started"]) });
4698
+ var CODEX_PROGRESS_SCHEMA = z18.looseObject({
4699
+ type: z18.enum(["item.started", "item.updated", "item.completed"]),
4700
+ item: z18.looseObject({ type: z18.string(), text: z18.string().optional() }).optional()
3607
4701
  });
3608
- var CODEX_COMPLETE_SCHEMA = z15.looseObject({
3609
- type: z15.literal("turn.completed"),
4702
+ var CODEX_COMPLETE_SCHEMA = z18.looseObject({
4703
+ type: z18.literal("turn.completed"),
3610
4704
  usage: USAGE_SCHEMA2.optional()
3611
4705
  });
3612
- var CODEX_FAILURE_SCHEMA = z15.looseObject({
3613
- type: z15.enum(["turn.failed", "error"]),
3614
- error: z15.looseObject({ message: z15.string().optional() }).optional(),
3615
- message: z15.string().optional()
4706
+ var CODEX_FAILURE_SCHEMA = z18.looseObject({
4707
+ type: z18.enum(["turn.failed", "error"]),
4708
+ error: z18.looseObject({ message: z18.string().optional() }).optional(),
4709
+ message: z18.string().optional()
3616
4710
  });
3617
4711
  var EMPTY_STATE = {
3618
4712
  provider: void 0,
@@ -3627,11 +4721,11 @@ var LAUNCH_HEADER_MARKER = " \xB7 started by process ";
3627
4721
  function usable_text(value) {
3628
4722
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
3629
4723
  }
3630
- function claude_progress(record2) {
3631
- return transcript_record.blocks_of(record2).filter((block) => block.type === transcript_record.TEXT_TYPE).map((block) => usable_text(block.text)).findLast((text2) => text2 !== void 0);
4724
+ function claude_progress(record3) {
4725
+ return transcript_record.blocks_of(record3).filter((block) => block.type === transcript_record.TEXT_TYPE).map((block) => usable_text(block.text)).findLast((text2) => text2 !== void 0);
3632
4726
  }
3633
- function is_claude_assistant(record2) {
3634
- return record2?.type === ASSISTANT_TYPE && record2.message !== void 0 && record2.message !== null;
4727
+ function is_claude_assistant(record3) {
4728
+ return record3?.type === ASSISTANT_TYPE && record3.message !== void 0 && record3.message !== null;
3635
4729
  }
3636
4730
  function claude_result(value, state) {
3637
4731
  const result = claude_result_event.decode(value);
@@ -3645,9 +4739,9 @@ function claude_result(value, state) {
3645
4739
  };
3646
4740
  }
3647
4741
  function claude_assistant(value, state) {
3648
- const record2 = transcript_record.read(value);
3649
- if (!is_claude_assistant(record2)) return void 0;
3650
- const progress = claude_progress(record2) ?? state.progress;
4742
+ const record3 = transcript_record.read(value);
4743
+ if (!is_claude_assistant(record3)) return void 0;
4744
+ const progress = claude_progress(record3) ?? state.progress;
3651
4745
  return { ...state, provider: "anthropic", progress };
3652
4746
  }
3653
4747
  function claude_terminal(value, state) {
@@ -3774,7 +4868,7 @@ function to_advice(verdict, has_work_to_stash) {
3774
4868
  if (verdict !== STOPPED_VERDICT) return ADVICE[verdict];
3775
4869
  return has_work_to_stash ? STOPPED_DIRTY_ADVICE : STOPPED_CLEAN_ADVICE;
3776
4870
  }
3777
- function decide4(traces) {
4871
+ function decide3(traces) {
3778
4872
  const verdict = to_verdict(traces);
3779
4873
  const has_work_to_stash = verdict === STOPPED_VERDICT && traces.is_tree_dirty;
3780
4874
  return {
@@ -3797,18 +4891,18 @@ var ALLOWED_ROOTS = [
3797
4891
  )
3798
4892
  ];
3799
4893
  function is_within(candidate, root) {
3800
- const relative = path3.relative(root, candidate);
3801
- return relative !== "" && !relative.startsWith("..") && !path3.isAbsolute(relative);
4894
+ const relative = path7.relative(root, candidate);
4895
+ return relative !== "" && !relative.startsWith("..") && !path7.isAbsolute(relative);
3802
4896
  }
3803
4897
  function to_safe_path(output_path) {
3804
- const normalized2 = path3.normalize(output_path);
3805
- if (!path3.isAbsolute(normalized2)) return void 0;
4898
+ const normalized2 = path7.normalize(output_path);
4899
+ if (!path7.isAbsolute(normalized2)) return void 0;
3806
4900
  return ALLOWED_ROOTS.some((root) => is_within(normalized2, root)) ? normalized2 : void 0;
3807
4901
  }
3808
4902
  function sample_output(output_path) {
3809
4903
  const safe_path = to_safe_path(output_path);
3810
4904
  if (safe_path === void 0) return void 0;
3811
- const stats = statSync(safe_path, { throwIfNoEntry: false });
4905
+ const stats = statSync2(safe_path, { throwIfNoEntry: false });
3812
4906
  if (!stats?.isFile()) return void 0;
3813
4907
  return { mtime_ms: stats.mtimeMs, size: stats.size };
3814
4908
  }
@@ -3854,7 +4948,7 @@ function freshness_of(request2) {
3854
4948
  };
3855
4949
  }
3856
4950
  async function read_traces(request2) {
3857
- const [is_initial_child_settled, is_output_frozen, is_tree_dirty, record2] = await Promise.all([
4951
+ const [is_initial_child_settled, is_output_frozen, is_tree_dirty, record3] = await Promise.all([
3858
4952
  read_child_settled(request2.issue, request2.repo),
3859
4953
  read_output_frozen(freshness_of(request2)),
3860
4954
  run_hold.is_tree_dirty(),
@@ -3868,17 +4962,17 @@ async function read_traces(request2) {
3868
4962
  is_child_settled,
3869
4963
  is_output_frozen,
3870
4964
  is_tree_dirty,
3871
- lane_state: record2?.state,
4965
+ lane_state: record3?.state,
3872
4966
  process_trace: request2.process_trace
3873
4967
  };
3874
4968
  }
3875
4969
  async function check(request2) {
3876
4970
  issue_number_shape.require_issue_number(request2.issue);
3877
- return decide4(await read_traces(request2));
4971
+ return decide3(await read_traces(request2));
3878
4972
  }
3879
4973
  var run_liveness = {
3880
4974
  check,
3881
- decide: decide4,
4975
+ decide: decide3,
3882
4976
  describe_agent_state,
3883
4977
  read_child_settled,
3884
4978
  read_output_frozen,
@@ -3886,6 +4980,199 @@ var run_liveness = {
3886
4980
  to_safe_path
3887
4981
  };
3888
4982
 
4983
+ // scripts/backlog/backlog-overlap.ts
4984
+ var CODE_SPAN = /`([^`\s]+)`/gu;
4985
+ var LINE_SUFFIX = /:\d+(?:[-:]\d+)?$/u;
4986
+ var FILE_EXTENSION = /\.[a-z]\w*$/iu;
4987
+ var ROOT_FILE_EXTENSION = /\.(?:[cm]?[jt]sx?|svelte|jsonc?|md|ya?ml|css|html|sh|toml|txt|properties)$/u;
4988
+ var GLOB = "*";
4989
+ var ISSUE_MARK = "#";
4990
+ var SEPARATOR = "/";
4991
+ var MIN_DIRECTORY_DEPTH = 2;
4992
+ var DOCS_DIRECTORY = "docs/";
4993
+ var MARKDOWN = ".md";
4994
+ function without_glob(path19) {
4995
+ const glob = path19.indexOf(GLOB);
4996
+ return glob === -1 ? path19 : path19.slice(0, path19.lastIndexOf(SEPARATOR, glob) + 1);
4997
+ }
4998
+ function is_narrow(directory) {
4999
+ return directory.split(SEPARATOR).filter(Boolean).length >= MIN_DIRECTORY_DEPTH;
5000
+ }
5001
+ function is_file(path19) {
5002
+ return (path19.includes(SEPARATOR) ? FILE_EXTENSION : ROOT_FILE_EXTENSION).test(path19);
5003
+ }
5004
+ function claim_of(path19) {
5005
+ if (path19.endsWith(SEPARATOR)) return is_narrow(path19) ? path19 : void 0;
5006
+ return is_file(path19) ? path19 : void 0;
5007
+ }
5008
+ function path_claims(tokens) {
5009
+ const paths = tokens.filter((token) => !token.includes(ISSUE_MARK)).map((token) => claim_of(without_glob(token.replace(LINE_SUFFIX, ""))));
5010
+ return new Set(paths.filter((path19) => path19 !== void 0));
5011
+ }
5012
+ function declared_paths(body) {
5013
+ return path_claims([...body.matchAll(CODE_SPAN)].map((match) => match[1] ?? ""));
5014
+ }
5015
+ function declared_of(issues) {
5016
+ return new Map(issues.map((issue) => [issue.number, declared_paths(issue.body ?? "")]));
5017
+ }
5018
+ function contains(outer, inner) {
5019
+ return outer.endsWith(SEPARATOR) ? inner.startsWith(outer) : outer === inner;
5020
+ }
5021
+ function is_shared_document(path19) {
5022
+ return path19.startsWith(DOCS_DIRECTORY) && (path19.endsWith(SEPARATOR) || path19.endsWith(MARKDOWN));
5023
+ }
5024
+ function overlaps(left, right) {
5025
+ if (is_shared_document(left) || is_shared_document(right)) return false;
5026
+ return contains(left, right) || contains(right, left);
5027
+ }
5028
+ function claims_of(declared, holders) {
5029
+ const pairs = holders.flatMap(
5030
+ (number) => [...declared.get(number) ?? []].map((path19) => [path19, number])
5031
+ );
5032
+ return new Map(pairs);
5033
+ }
5034
+ function running_claims(declared, read4) {
5035
+ return claims_of(declared, read4.kind === "busy" ? read4.issues.map((issue) => issue.number) : []);
5036
+ }
5037
+ function paths_of(child, declared, repo) {
5038
+ if (child.repo !== repo) return /* @__PURE__ */ new Set();
5039
+ return declared.get(child.number) ?? /* @__PURE__ */ new Set();
5040
+ }
5041
+ function clash_of(claims, paths) {
5042
+ const clashes = [...paths].flatMap(
5043
+ (path19) => [...claims].filter(([claimed]) => overlaps(claimed, path19)).map(([claimed, holder]) => ({ path: path19, claimed, holder }))
5044
+ );
5045
+ return clashes[0];
5046
+ }
5047
+ function path_clash(paths, declared, holders) {
5048
+ return clash_of(claims_of(declared, holders), paths);
5049
+ }
5050
+ function running_clash(child, declared, holding) {
5051
+ return path_clash(paths_of(child, declared, holding.repo), declared, holding.holders);
5052
+ }
5053
+ function holds(declared, holder, waiting) {
5054
+ return clash_of(claims_of(declared, [holder]), declared.get(waiting) ?? /* @__PURE__ */ new Set()) !== void 0;
5055
+ }
5056
+ function paused_tail(clash, lanes) {
5057
+ const note = lane_state.paused_note(lanes.get(String(clash.holder)));
5058
+ return note === void 0 ? "" : ` \u2014 ${note}`;
5059
+ }
5060
+ function clash_note(child, clash, lanes) {
5061
+ const waiting = session_cite.issue(child.number, void 0, child.repo);
5062
+ const holder = session_cite.issue(clash.holder, void 0, child.repo);
5063
+ return `${waiting} waits: its \`${clash.path}\` overlaps \`${clash.claimed}\`, which ${holder} declares${paused_tail(clash, lanes)}.`;
5064
+ }
5065
+ function visit(walk, child, paths) {
5066
+ const clash = clash_of(walk.claims, paths);
5067
+ if (clash === void 0) {
5068
+ walk.offered.push(child);
5069
+ for (const path19 of paths) walk.claims.set(path19, child.number);
5070
+ return;
5071
+ }
5072
+ walk.withheld.push(child);
5073
+ walk.notes.push(clash_note(child, clash, walk.lanes));
5074
+ }
5075
+ function merged_notice(notice, notes) {
5076
+ const parts = notice === void 0 ? notes : [notice, ...notes];
5077
+ return parts.length === 0 ? void 0 : parts.join("\n");
5078
+ }
5079
+ function separate(selection, declared, context) {
5080
+ const walk = {
5081
+ claims: running_claims(declared, context.read),
5082
+ lanes: context.lanes ?? /* @__PURE__ */ new Map(),
5083
+ offered: [],
5084
+ withheld: [],
5085
+ notes: []
5086
+ };
5087
+ for (const child of selection.offered) visit(walk, child, paths_of(child, declared, context.repo));
5088
+ const notice = merged_notice(selection.notice, walk.notes);
5089
+ const withheld = [...walk.withheld, ...selection.withheld];
5090
+ return { offered: walk.offered, withheld, ...notice !== void 0 && { notice } };
5091
+ }
5092
+ var backlog_overlap = {
5093
+ declared_of,
5094
+ declared_paths,
5095
+ holds,
5096
+ path_claims,
5097
+ path_clash,
5098
+ running_clash,
5099
+ separate
5100
+ };
5101
+
5102
+ // scripts/lane/lane-plan.ts
5103
+ import { z as z19 } from "zod";
5104
+ var lane_plan_schema = z19.object({
5105
+ issue: z19.number(),
5106
+ paths: z19.array(z19.string()),
5107
+ planned_at: z19.string(),
5108
+ cleared: z19.boolean().optional(),
5109
+ // The lane gave its lane back on this plan (`lane-yield.ts`), so the plan outlives the lane.
5110
+ yielded: z19.boolean().optional()
5111
+ });
5112
+ function plan_path(repository, issue) {
5113
+ return durable_state.path(durable_state.PREFIX.LANE_PLAN, repository, `-${String(issue)}.json`);
5114
+ }
5115
+ function record2(plan, directory) {
5116
+ const repository = git_common_directory.repository(directory);
5117
+ if (repository === void 0) return false;
5118
+ stamp_file.replace_stamp(plan_path(repository, plan.issue), plan);
5119
+ return true;
5120
+ }
5121
+ function remove(issue, directory) {
5122
+ const repository = git_common_directory.repository(directory);
5123
+ if (repository !== void 0) stamp_file.remove_stamp(plan_path(repository, issue));
5124
+ }
5125
+ function read3(issue, directory) {
5126
+ const repository = git_common_directory.repository(directory);
5127
+ const text2 = repository === void 0 ? void 0 : stamp_file.read_stamp_text(plan_path(repository, issue));
5128
+ return text2 === void 0 ? void 0 : json_value.parse_with(text2, lane_plan_schema);
5129
+ }
5130
+ function is_uncleared(issue, directory) {
5131
+ return git_common_directory.repository(directory) !== void 0 && read3(issue, directory)?.cleared !== true;
5132
+ }
5133
+ function release(issue, directory) {
5134
+ if (read3(issue, directory)?.yielded !== true) remove(issue, directory);
5135
+ }
5136
+ function with_plans(declared, directory) {
5137
+ const replaced = [...declared].map(([issue, paths]) => [
5138
+ issue,
5139
+ new Set(read3(issue, directory)?.paths ?? paths)
5140
+ ]);
5141
+ return new Map(replaced);
5142
+ }
5143
+ function goes_before(other, plan) {
5144
+ return other.cleared === true || other.planned_at < plan.planned_at;
5145
+ }
5146
+ function earlier_holders(plan, holders, directory) {
5147
+ return holders.filter((holder) => {
5148
+ const other = holder === plan.issue ? void 0 : read3(holder, directory);
5149
+ return other !== void 0 && goes_before(other, plan);
5150
+ });
5151
+ }
5152
+ function clash_of2(plan, declared, directory) {
5153
+ const holders = earlier_holders(plan, [...declared.keys()], directory);
5154
+ return backlog_overlap.path_clash(new Set(plan.paths), declared, holders);
5155
+ }
5156
+ function is_yielded(issue, directory) {
5157
+ return read3(issue, directory)?.yielded === true;
5158
+ }
5159
+ function is_yield_held(issue, declared, directory) {
5160
+ const plan = read3(issue, directory);
5161
+ return plan?.yielded === true && clash_of2(plan, declared, directory) !== void 0;
5162
+ }
5163
+ var lane_plan = {
5164
+ clash_of: clash_of2,
5165
+ earlier_holders,
5166
+ is_uncleared,
5167
+ is_yield_held,
5168
+ is_yielded,
5169
+ read: read3,
5170
+ record: record2,
5171
+ release,
5172
+ remove,
5173
+ with_plans
5174
+ };
5175
+
3889
5176
  // scripts/run/run-stage.ts
3890
5177
  var KICKOFF = "kickoff";
3891
5178
  var HALFRUN = "halfrun";
@@ -3928,7 +5215,7 @@ function state_of(facts) {
3928
5215
  if (facts.is_halfrun_stopped) return HALFRUN_STOPPED;
3929
5216
  return facts.is_planned ? PLANNED : FRESH;
3930
5217
  }
3931
- function decide5(state, command) {
5218
+ function decide4(state, command) {
3932
5219
  const is_reached = STATES.indexOf(state) > COMMANDS.indexOf(command);
3933
5220
  const start = is_reached ? REACHED : STARTS[state];
3934
5221
  return { state, command, start, end: ENDS[command], is_reached };
@@ -3945,7 +5232,7 @@ function format_decision(issue, decision) {
3945
5232
  return `stage ${issue_cite.plain(issue)} \u2014 ${fields.join(" \xB7 ")}`;
3946
5233
  }
3947
5234
  function cell(state, command) {
3948
- const decision = decide5(state, command);
5235
+ const decision = decide4(state, command);
3949
5236
  return decision.is_reached ? REACHED_CELL : `${decision.start}${CELL_ARROW}${decision.end}`;
3950
5237
  }
3951
5238
  function table_rows() {
@@ -3970,7 +5257,7 @@ var run_stage = {
3970
5257
  REACHED,
3971
5258
  START_TOKENS,
3972
5259
  STATES,
3973
- decide: decide5,
5260
+ decide: decide4,
3974
5261
  format_decision,
3975
5262
  next_commands,
3976
5263
  next_line,
@@ -3990,8 +5277,8 @@ function format_holder(issue, repo, ages) {
3990
5277
  function format_holders(issues, repo, ages) {
3991
5278
  return issues.map((issue) => format_holder(issue, repo, ages)).join(", ");
3992
5279
  }
3993
- function occupied_lanes(read3) {
3994
- return read3.kind === "busy" ? read3.issues.length : 0;
5280
+ function occupied_lanes(read4) {
5281
+ return read4.kind === "busy" ? read4.issues.length : 0;
3995
5282
  }
3996
5283
  function occupancy_message(issues, repo, limit, ages = NO_AGES) {
3997
5284
  return `${String(issues.length)} of ${String(limit)} lanes in use in ${repo}: ${format_holders(issues, repo, ages)}.`;
@@ -4009,18 +5296,18 @@ var BUSY_REASONS = {
4009
5296
  unreadable: unreadable_message2,
4010
5297
  truncated: truncated_message
4011
5298
  };
4012
- function busy_reason(read3, repo, limit, ages = NO_AGES) {
4013
- if (read3.kind === "busy") return lanes_full_message(read3.issues, repo, limit, ages);
4014
- if (read3.kind === "idle") return "";
4015
- return BUSY_REASONS[read3.kind](repo);
5299
+ function busy_reason(read4, repo, limit, ages = NO_AGES) {
5300
+ if (read4.kind === "busy") return lanes_full_message(read4.issues, repo, limit, ages);
5301
+ if (read4.kind === "idle") return "";
5302
+ return BUSY_REASONS[read4.kind](repo);
4016
5303
  }
4017
5304
  function is_parked(issue) {
4018
5305
  return has_any_label(issue.labels, PARKED_LABELS);
4019
5306
  }
4020
5307
  function parse_listing(raw, is_capped) {
4021
- const read3 = read_json_listing(raw, open_issue_schema);
4022
- if (read3.kind !== "read") return { kind: "unreadable" };
4023
- const holders = read3.rows.filter((row) => !is_parked(row));
5308
+ const read4 = read_json_listing(raw, open_issue_schema);
5309
+ if (read4.kind !== "read") return { kind: "unreadable" };
5310
+ const holders = read4.rows.filter((row) => !is_parked(row));
4024
5311
  if (holders.length > 0) return { kind: "busy", issues: holders };
4025
5312
  return { kind: is_capped ? "truncated" : "idle" };
4026
5313
  }
@@ -4047,26 +5334,26 @@ var epic_busy = {
4047
5334
 
4048
5335
  // scripts/doctor/doctor-consumer.ts
4049
5336
  import { existsSync } from "node:fs";
4050
- import path4 from "node:path";
5337
+ import path8 from "node:path";
4051
5338
 
4052
5339
  // scripts/version/kit-descriptor.ts
4053
5340
  var KIT_PACKAGE_NAME = "@joshuafolkken/kit";
4054
5341
 
4055
5342
  // scripts/doctor/doctor-consumer.ts
4056
- import { z as z16 } from "zod";
5343
+ import { z as z20 } from "zod";
4057
5344
  var NODE_MODULES = "node_modules";
4058
- var SETTINGS_PATH = path4.join(".claude", "settings.json");
5345
+ var SETTINGS_PATH = path8.join(".claude", "settings.json");
4059
5346
  var CLAUDE_MD = "CLAUDE.md";
4060
5347
  var PLUGIN_ID = "kit@kit";
4061
5348
  var CLAUDE_MD_POINTER = `@${NODE_MODULES}/${KIT_PACKAGE_NAME}/dist/${CLAUDE_MD}`;
4062
- var SECRETLINT_BIN = path4.join(NODE_MODULES, ".bin", "secretlint");
4063
- var manifest_schema = z16.object({
4064
- name: z16.string().optional(),
4065
- dependencies: z16.record(z16.string(), z16.string()).optional(),
4066
- devDependencies: z16.record(z16.string(), z16.string()).optional()
5349
+ var SECRETLINT_BIN = path8.join(NODE_MODULES, ".bin", "secretlint");
5350
+ var manifest_schema = z20.object({
5351
+ name: z20.string().optional(),
5352
+ dependencies: z20.record(z20.string(), z20.string()).optional(),
5353
+ devDependencies: z20.record(z20.string(), z20.string()).optional()
4067
5354
  });
4068
- var settings_schema = z16.object({
4069
- enabledPlugins: z16.record(z16.string(), z16.boolean()).optional()
5355
+ var settings_schema = z20.object({
5356
+ enabledPlugins: z20.record(z20.string(), z20.boolean()).optional()
4070
5357
  });
4071
5358
  var STATE_OK = "ok";
4072
5359
  var STATE_TARGET_MISSING = "target-missing";
@@ -4081,10 +5368,10 @@ function lists_kit_dependency(manifest) {
4081
5368
  function is_kit_consumer(root) {
4082
5369
  const manifest = parse_manifest(root);
4083
5370
  if (manifest?.name === KIT_PACKAGE_NAME) return false;
4084
- return lists_kit_dependency(manifest) || existsSync(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME));
5371
+ return lists_kit_dependency(manifest) || existsSync(path8.join(root, NODE_MODULES, KIT_PACKAGE_NAME));
4085
5372
  }
4086
5373
  function is_plugin_declared(root) {
4087
- const content = file_reader.read_if_readable(path4.join(root, SETTINGS_PATH));
5374
+ const content = file_reader.read_if_readable(path8.join(root, SETTINGS_PATH));
4088
5375
  if (content === void 0) return false;
4089
5376
  const parsed = settings_schema.safeParse(json_value.parse_or_undefined(content));
4090
5377
  return parsed.success && parsed.data.enabledPlugins?.[PLUGIN_ID] === true;
@@ -4096,13 +5383,13 @@ function configured_hooks_path(root) {
4096
5383
  return value === "" ? void 0 : value;
4097
5384
  }
4098
5385
  function claude_md_state(root) {
4099
- const content = file_reader.read_if_readable(path4.join(root, CLAUDE_MD));
5386
+ const content = file_reader.read_if_readable(path8.join(root, CLAUDE_MD));
4100
5387
  if (content === void 0) return STATE_MISSING;
4101
5388
  if (!content.includes(CLAUDE_MD_POINTER)) return STATE_OK;
4102
- return existsSync(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME, "dist", CLAUDE_MD)) ? STATE_OK : STATE_TARGET_MISSING;
5389
+ return existsSync(path8.join(root, NODE_MODULES, KIT_PACKAGE_NAME, "dist", CLAUDE_MD)) ? STATE_OK : STATE_TARGET_MISSING;
4103
5390
  }
4104
5391
  function is_secretlint_runnable(root) {
4105
- return existsSync(path4.join(root, SECRETLINT_BIN));
5392
+ return existsSync(path8.join(root, SECRETLINT_BIN));
4106
5393
  }
4107
5394
  function status_line(is_ok, ok_text, warn_text) {
4108
5395
  return is_ok ? ` \u2713 ${ok_text}` : ` \u26A0 ${warn_text}`;
@@ -4164,11 +5451,11 @@ var doctor_consumer = {
4164
5451
 
4165
5452
  // scripts/gate/project-checks.ts
4166
5453
  import { existsSync as existsSync6, globSync as globSync2 } from "node:fs";
4167
- import path14 from "node:path";
5454
+ import path18 from "node:path";
4168
5455
 
4169
5456
  // scripts/init/own-formatter.ts
4170
5457
  import { existsSync as existsSync2 } from "node:fs";
4171
- import path5 from "node:path";
5458
+ import path9 from "node:path";
4172
5459
  var OWN_FORMATTER_CONFIGS = [
4173
5460
  "biome.json",
4174
5461
  "biome.jsonc",
@@ -4178,23 +5465,23 @@ var OWN_FORMATTER_CONFIGS = [
4178
5465
  ".dprint.jsonc"
4179
5466
  ];
4180
5467
  function find_config(root) {
4181
- return OWN_FORMATTER_CONFIGS.find((name) => existsSync2(path5.join(root, name)));
5468
+ return OWN_FORMATTER_CONFIGS.find((name) => existsSync2(path9.join(root, name)));
4182
5469
  }
4183
5470
  var own_formatter = { find_config };
4184
5471
 
4185
5472
  // scripts/init/project-profile.ts
4186
5473
  import { existsSync as existsSync5, readdirSync } from "node:fs";
4187
- import path13 from "node:path";
4188
- import { z as z22 } from "zod";
5474
+ import path17 from "node:path";
5475
+ import { z as z26 } from "zod";
4189
5476
 
4190
5477
  // scripts/sync/file-content.ts
4191
- import path6 from "node:path";
4192
- var PATH_SEPARATORS = path6.sep === "\\" ? /[/\\]/u : /\//u;
5478
+ import path10 from "node:path";
5479
+ var PATH_SEPARATORS = path10.sep === "\\" ? /[/\\]/u : /\//u;
4193
5480
 
4194
5481
  // scripts/safe-chain/pack-hook.ts
4195
- import { z as z17 } from "zod";
5482
+ import { z as z21 } from "zod";
4196
5483
  var PACK_HOOK_FILE = ".pnpmfile.mjs";
4197
- var manifest_schema2 = z17.looseObject({ private: z17.boolean().optional() });
5484
+ var manifest_schema2 = z21.looseObject({ private: z21.boolean().optional() });
4198
5485
  var MESSAGES = {
4199
5486
  written: ` \u2714 synced ${PACK_HOOK_FILE} (run \`pnpm install\` to refresh pnpmfileChecksum)`,
4200
5487
  unchanged: ` \u2714 unchanged ${PACK_HOOK_FILE}`,
@@ -4229,17 +5516,17 @@ function parse_yaml_documents(content) {
4229
5516
  var yaml_document = { is_mapping_document, parse_yaml, parse_yaml_documents };
4230
5517
 
4231
5518
  // scripts/version/release-window.ts
4232
- import { z as z19 } from "zod";
5519
+ import { z as z23 } from "zod";
4233
5520
 
4234
5521
  // scripts/version/release-age.ts
4235
5522
  import { readFileSync as readFileSync5 } from "node:fs";
4236
- import path7 from "node:path";
5523
+ import path11 from "node:path";
4237
5524
  import semver from "semver";
4238
- import { z as z18 } from "zod";
5525
+ import { z as z22 } from "zod";
4239
5526
  var NO_QUARANTINE_MINUTES = 0;
4240
5527
  var WORKSPACE_PATH = "pnpm-workspace.yaml";
4241
- var workspace_age_schema = z18.object({ minimumReleaseAge: z18.number().int().nonnegative() });
4242
- var release_times_schema = z18.record(z18.string(), z18.string());
5528
+ var workspace_age_schema = z22.object({ minimumReleaseAge: z22.number().int().nonnegative() });
5529
+ var release_times_schema = z22.record(z22.string(), z22.string());
4243
5530
  function parse_workspace(workspace_content) {
4244
5531
  try {
4245
5532
  return yaml_document.parse_yaml(workspace_content);
@@ -4280,14 +5567,14 @@ function read_minimum_release_age(workspace_path = WORKSPACE_PATH) {
4280
5567
  return read_declared_minimum_release_age(workspace_path) ?? NO_QUARANTINE_MINUTES;
4281
5568
  }
4282
5569
  function find_declared_minimum_release_age(start, boundary) {
4283
- const declared = read_declared_minimum_release_age(path7.join(start, WORKSPACE_PATH));
5570
+ const declared = read_declared_minimum_release_age(path11.join(start, WORKSPACE_PATH));
4284
5571
  if (declared !== void 0) return declared;
4285
5572
  if (start === boundary) return void 0;
4286
- const parent = path7.dirname(start);
5573
+ const parent = path11.dirname(start);
4287
5574
  return parent === start ? void 0 : find_declared_minimum_release_age(parent, boundary);
4288
5575
  }
4289
5576
  function read_nearest_minimum_release_age(start, boundary) {
4290
- return find_declared_minimum_release_age(path7.resolve(start), boundary) ?? NO_QUARANTINE_MINUTES;
5577
+ return find_declared_minimum_release_age(path11.resolve(start), boundary) ?? NO_QUARANTINE_MINUTES;
4291
5578
  }
4292
5579
  var release_age = {
4293
5580
  release_times_schema,
@@ -4299,7 +5586,7 @@ var release_age = {
4299
5586
  };
4300
5587
 
4301
5588
  // scripts/version/release-window.ts
4302
- var exclusions_schema = z19.object({ minimumReleaseAgeExclude: z19.array(z19.string()).optional() });
5589
+ var exclusions_schema = z23.object({ minimumReleaseAgeExclude: z23.array(z23.string()).optional() });
4303
5590
 
4304
5591
  // scripts/safe-chain/project-config.ts
4305
5592
  import { dump } from "js-yaml";
@@ -4323,7 +5610,7 @@ function flatten_elements(elements) {
4323
5610
  const scalars = elements.map((line) => line.trim().replace(/,$/u, ""));
4324
5611
  return `[${scalars.join(", ")}]`;
4325
5612
  }
4326
- function collect_block2(lines, start) {
5613
+ function collect_block(lines, start) {
4327
5614
  const close = lines.findIndex((line, index) => index > start && ARRAY_CLOSE.test(line));
4328
5615
  if (close === -1) return void 0;
4329
5616
  const elements = lines.slice(start + 1, close);
@@ -4337,7 +5624,7 @@ function try_inline(open, block) {
4337
5624
  }
4338
5625
  function rewrite_at(lines, index, line) {
4339
5626
  const verbatim = { lines: [line], next: index + 1 };
4340
- const block = is_array_open(line) ? collect_block2(lines, index) : void 0;
5627
+ const block = is_array_open(line) ? collect_block(lines, index) : void 0;
4341
5628
  if (block === void 0) return verbatim;
4342
5629
  const inlined = try_inline(line, block);
4343
5630
  if (inlined === void 0) return verbatim;
@@ -4425,9 +5712,9 @@ function is_matching(entry, matchers) {
4425
5712
  (matcher) => typeof matcher === "string" ? matcher === entry : matcher.test(entry)
4426
5713
  );
4427
5714
  }
4428
- function apply_remove(existing, remove) {
4429
- if (remove === void 0 || remove.length === 0) return existing;
4430
- return existing.filter((entry) => !is_matching(entry, remove));
5715
+ function apply_remove(existing, remove2) {
5716
+ if (remove2 === void 0 || remove2.length === 0) return existing;
5717
+ return existing.filter((entry) => !is_matching(entry, remove2));
4431
5718
  }
4432
5719
  function collect_additions(kept, ensure) {
4433
5720
  if (ensure === void 0) return [];
@@ -4666,10 +5953,10 @@ function set_json_key(content, key2, value) {
4666
5953
  const indented = fit_value(content, value_text, line_indent(content, existing.offset));
4667
5954
  return with_trailing_newline(replace_range(content, existing, indented));
4668
5955
  }
4669
- function resolve_parent(content, path15) {
5956
+ function resolve_parent(content, path19) {
4670
5957
  const root = parse_root(content);
4671
5958
  if (root === void 0) return void 0;
4672
- const parent = path15.length > 1 ? findNodeAtLocation(root, path15.slice(0, -1)) : root;
5959
+ const parent = path19.length > 1 ? findNodeAtLocation(root, path19.slice(0, -1)) : root;
4673
5960
  return parent?.type === "object" ? parent : void 0;
4674
5961
  }
4675
5962
  function cut_ranges(content, parent, ranges) {
@@ -4680,24 +5967,24 @@ function cut_ranges(content, parent, ranges) {
4680
5967
  }
4681
5968
  return next;
4682
5969
  }
4683
- function rebalance_new_last(content, path15) {
4684
- const parent = resolve_parent(content, path15);
5970
+ function rebalance_new_last(content, path19) {
5971
+ const parent = resolve_parent(content, path19);
4685
5972
  const last = parent?.children?.at(-1);
4686
5973
  if (parent === void 0 || last === void 0) return content;
4687
5974
  return rebalance(content, last, has_separator_after(content, parent.offset, parent.offset));
4688
5975
  }
4689
- function cut_property(content, parent, path15) {
4690
- const index = property_index(parent, path15.at(-1) ?? "");
5976
+ function cut_property(content, parent, path19) {
5977
+ const index = property_index(parent, path19.at(-1) ?? "");
4691
5978
  const ranges = removal_ranges(content, parent, index);
4692
5979
  if (ranges.length === 0) return content;
4693
5980
  const cut = cut_ranges(content, parent, ranges);
4694
5981
  const did_end_object = index === (parent.children ?? []).length - 1;
4695
- return did_end_object ? rebalance_new_last(cut, path15) : cut;
5982
+ return did_end_object ? rebalance_new_last(cut, path19) : cut;
4696
5983
  }
4697
- function remove_json_path(content, path15) {
4698
- const parent = resolve_parent(content, path15);
5984
+ function remove_json_path(content, path19) {
5985
+ const parent = resolve_parent(content, path19);
4699
5986
  if (parent === void 0) return content;
4700
- const next = cut_property(content, parent, path15);
5987
+ const next = cut_property(content, parent, path19);
4701
5988
  return next === content ? content : with_trailing_newline(next);
4702
5989
  }
4703
5990
  function remove_json_key(content, key2) {
@@ -4864,13 +6151,13 @@ var kit_base_preset = {
4864
6151
 
4865
6152
  // scripts/init/tsconfig-preset-migration.ts
4866
6153
  import { existsSync as existsSync3 } from "node:fs";
4867
- import path8 from "node:path";
6154
+ import path12 from "node:path";
4868
6155
  var LEGACY_PRESET_ENTRY = /@joshuafolkken\/[^/]+\/tsconfig\/[^/]+\.jsonc$/u;
4869
6156
  var LEGACY_SUFFIX = ".jsonc";
4870
6157
  var CURRENT_SUFFIX = ".json";
4871
6158
  var EXTENDS_FIELD = "extends";
4872
6159
  function is_migration_target_present(base_directory, entry) {
4873
- return existsSync3(path8.resolve(base_directory, entry));
6160
+ return existsSync3(path12.resolve(base_directory, entry));
4874
6161
  }
4875
6162
  function migrate_entry(base_directory, entry) {
4876
6163
  if (!LEGACY_PRESET_ENTRY.test(entry)) return entry;
@@ -4966,11 +6253,11 @@ function merge_owned_entries(current, update) {
4966
6253
  return { ...owned, ...Object.fromEntries(missing) };
4967
6254
  }
4968
6255
  function collect_applicable_updates(parsed, updates) {
4969
- const resolved = Object.entries(updates).map(([key2, value]) => [
6256
+ const resolved2 = Object.entries(updates).map(([key2, value]) => [
4970
6257
  key2,
4971
6258
  Object.hasOwn(parsed, key2) ? merge_owned_entries(parsed[key2], value) : value
4972
6259
  ]);
4973
- return Object.fromEntries(resolved.filter(([, merged]) => merged !== void 0));
6260
+ return Object.fromEntries(resolved2.filter(([, merged]) => merged !== void 0));
4974
6261
  }
4975
6262
  function merge_json_object(content, updates) {
4976
6263
  const parsed = parse_jsonc(content);
@@ -5102,10 +6389,10 @@ var init_logic_json_merge = {
5102
6389
  };
5103
6390
 
5104
6391
  // scripts/init/kit-development-versions.ts
5105
- import { z as z20 } from "zod";
5106
- var kit_manifest_schema = z20.object({
5107
- peerDependencies: z20.record(z20.string(), z20.string()).default({}),
5108
- devDependencies: z20.record(z20.string(), z20.string())
6392
+ import { z as z24 } from "zod";
6393
+ var kit_manifest_schema = z24.object({
6394
+ peerDependencies: z24.record(z24.string(), z24.string()).default({}),
6395
+ devDependencies: z24.record(z24.string(), z24.string())
5109
6396
  });
5110
6397
  function read_kit_manifest() {
5111
6398
  return package_manifest.read_manifest(PACKAGE_DIR, kit_manifest_schema);
@@ -5236,14 +6523,14 @@ function resolve_multicriteria_conflicts(existing, template) {
5236
6523
  const existing_ids = get_multicriteria_ids(existing);
5237
6524
  const template_ids = get_multicriteria_ids(template);
5238
6525
  const reusable_ids = existing_ids.filter((id) => !template_ids.includes(id));
5239
- let resolved = template;
6526
+ let resolved2 = template;
5240
6527
  for (const id of template_ids) {
5241
- if (!existing_ids.includes(id) || !has_criterion_conflict(existing, resolved, id)) continue;
5242
- const matching_id = find_matching_criterion(existing, resolved, reusable_ids, id);
5243
- const replacement_id = matching_id ?? allocate_criterion_id(/* @__PURE__ */ new Set([...existing_ids, ...get_multicriteria_ids(resolved)]));
5244
- resolved = rename_criterion(resolved, id, replacement_id);
6528
+ if (!existing_ids.includes(id) || !has_criterion_conflict(existing, resolved2, id)) continue;
6529
+ const matching_id = find_matching_criterion(existing, resolved2, reusable_ids, id);
6530
+ const replacement_id = matching_id ?? allocate_criterion_id(/* @__PURE__ */ new Set([...existing_ids, ...get_multicriteria_ids(resolved2)]));
6531
+ resolved2 = rename_criterion(resolved2, id, replacement_id);
5245
6532
  }
5246
- return resolved;
6533
+ return resolved2;
5247
6534
  }
5248
6535
  function merge_multicriteria_ids(existing, template_content) {
5249
6536
  const existing_value = get_multicriteria_value(existing);
@@ -5284,7 +6571,7 @@ var init_logic_sonar = {
5284
6571
 
5285
6572
  // scripts/init/init-logic-templates.ts
5286
6573
  import { existsSync as existsSync4, readFileSync as readFileSync6 } from "node:fs";
5287
- import path9 from "node:path";
6574
+ import path13 from "node:path";
5288
6575
  var ESLINT_VANILLA = `import { create_vanilla_config } from '@joshuafolkken/kit/eslint/vanilla'
5289
6576
 
5290
6577
  export default create_vanilla_config({
@@ -5369,7 +6656,7 @@ function merge_eslint_config(existing) {
5369
6656
  return apply_rules_template(rules_blocks.join(",\n"));
5370
6657
  }
5371
6658
  function detect_tailwind_stylesheet(project_root2) {
5372
- return existsSync4(path9.join(project_root2, DEFAULT_TAILWIND_STYLESHEET)) ? DEFAULT_TAILWIND_STYLESHEET : void 0;
6659
+ return existsSync4(path13.join(project_root2, DEFAULT_TAILWIND_STYLESHEET)) ? DEFAULT_TAILWIND_STYLESHEET : void 0;
5373
6660
  }
5374
6661
  function generate_prettier_config(stylesheet) {
5375
6662
  const stylesheet_line = stylesheet === void 0 ? "" : ` tailwindStylesheet: '${stylesheet}',
@@ -5383,7 +6670,7 @@ ${stylesheet_line}}
5383
6670
  }
5384
6671
  function existing_stylesheet(existing, project_root2) {
5385
6672
  const named = TAILWIND_STYLESHEET_PATTERN.exec(existing)?.[1];
5386
- return named !== void 0 && existsSync4(path9.resolve(project_root2, named)) ? named : void 0;
6673
+ return named !== void 0 && existsSync4(path13.resolve(project_root2, named)) ? named : void 0;
5387
6674
  }
5388
6675
  function merge_prettier_config(existing, project_root2) {
5389
6676
  const stylesheet = existing_stylesheet(existing, project_root2) ?? detect_tailwind_stylesheet(project_root2);
@@ -5899,23 +7186,23 @@ var AI_COPY_DIRECTORIES_LIST = "AI_COPY_DIRECTORIES";
5899
7186
  var SYNCED_PATHS_LIST = "SYNCED_PATHS";
5900
7187
  var PLUGIN_SKILL_DIRECTORIES_LIST = "PLUGIN_SKILL_DIRECTORIES";
5901
7188
  var PATH_SEPARATOR2 = "/";
5902
- function is_inside(path15, directory) {
5903
- return path15 === directory || path15.startsWith(`${directory}${PATH_SEPARATOR2}`);
7189
+ function is_inside(path19, directory) {
7190
+ return path19 === directory || path19.startsWith(`${directory}${PATH_SEPARATOR2}`);
5904
7191
  }
5905
- function matches_file(path15) {
5906
- return init_logic.get_ai_copy_files().includes(path15);
7192
+ function matches_file(path19) {
7193
+ return init_logic.get_ai_copy_files().includes(path19);
5907
7194
  }
5908
- function matches_mapping(path15) {
5909
- return init_logic.get_ai_copy_file_mappings().some((mapping) => mapping.src === path15 || mapping.dest === path15);
7195
+ function matches_mapping(path19) {
7196
+ return init_logic.get_ai_copy_file_mappings().some((mapping) => mapping.src === path19 || mapping.dest === path19);
5910
7197
  }
5911
- function matches_directory(path15) {
5912
- return init_logic.get_ai_copy_directories().some((directory) => is_inside(path15, directory));
7198
+ function matches_directory(path19) {
7199
+ return init_logic.get_ai_copy_directories().some((directory) => is_inside(path19, directory));
5913
7200
  }
5914
- function matches_plugin_skill(path15) {
5915
- return PLUGIN_SKILL_DIRECTORIES.some((directory) => is_inside(path15, directory));
7201
+ function matches_plugin_skill(path19) {
7202
+ return PLUGIN_SKILL_DIRECTORIES.some((directory) => is_inside(path19, directory));
5916
7203
  }
5917
- function matches_synced_path(path15) {
5918
- return synced_paths.get_synced_paths().includes(path15);
7204
+ function matches_synced_path(path19) {
7205
+ return synced_paths.get_synced_paths().includes(path19);
5919
7206
  }
5920
7207
  var MATCHERS = [
5921
7208
  { list: AI_COPY_FILES_LIST, matches: matches_file },
@@ -5924,18 +7211,18 @@ var MATCHERS = [
5924
7211
  { list: PLUGIN_SKILL_DIRECTORIES_LIST, matches: matches_plugin_skill },
5925
7212
  { list: SYNCED_PATHS_LIST, matches: matches_synced_path }
5926
7213
  ];
5927
- function find_list(path15) {
5928
- return MATCHERS.find((matcher) => matcher.matches(path15))?.list;
7214
+ function find_list(path19) {
7215
+ return MATCHERS.find((matcher) => matcher.matches(path19))?.list;
5929
7216
  }
5930
- function to_hit(path15) {
5931
- const list = find_list(path15);
5932
- return list === void 0 ? void 0 : { path: path15, list };
7217
+ function to_hit(path19) {
7218
+ const list = find_list(path19);
7219
+ return list === void 0 ? void 0 : { path: path19, list };
5933
7220
  }
5934
7221
  function find_managed_paths(paths) {
5935
- return paths.flatMap((path15) => to_hit(path15) ?? []);
7222
+ return paths.flatMap((path19) => to_hit(path19) ?? []);
5936
7223
  }
5937
7224
  function has_managed_path(paths) {
5938
- return paths.some((path15) => find_list(path15) !== void 0);
7225
+ return paths.some((path19) => find_list(path19) !== void 0);
5939
7226
  }
5940
7227
  function format_hit(hit) {
5941
7228
  return `${hit.path} (${hit.list})`;
@@ -5951,8 +7238,8 @@ var managed_config_scope = {
5951
7238
  };
5952
7239
 
5953
7240
  // scripts/init/session-language-environment.ts
5954
- import path10 from "node:path";
5955
- var CLAUDE_SETTINGS_PATH = path10.join(".claude", "settings.json");
7241
+ import path14 from "node:path";
7242
+ var CLAUDE_SETTINGS_PATH = path14.join(".claude", "settings.json");
5956
7243
  var KEY_LINE_PATTERN = new RegExp(
5957
7244
  String.raw`^\s*(?:export\s+)?${session_language.ENV_KEY}\s*=`,
5958
7245
  "mu"
@@ -5962,8 +7249,8 @@ var KEY_LINE_PATTERN = new RegExp(
5962
7249
  import { findNodeAtLocation as findNodeAtLocation2, getNodeValue as getNodeValue2, parseTree as parseTree3 } from "jsonc-parser";
5963
7250
 
5964
7251
  // scripts/init/vscode-tasks.ts
5965
- import { z as z21 } from "zod";
5966
- var task_array_schema = z21.array(json_object_schema);
7252
+ import { z as z25 } from "zod";
7253
+ var task_array_schema = z25.array(json_object_schema);
5967
7254
 
5968
7255
  // scripts/init/init-actions.ts
5969
7256
  var BASIC_PRETTIER_CONFIG = "prettier.config.mjs";
@@ -5973,11 +7260,11 @@ var MARKER_TOKEN = "josh-managed-workflow:";
5973
7260
  var MARKER_PREFIX = `# ${MARKER_TOKEN}`;
5974
7261
 
5975
7262
  // scripts/init/claude-plugin-config.ts
5976
- import path11 from "node:path";
7263
+ import path15 from "node:path";
5977
7264
  var MARKETPLACE_NAME = "kit";
5978
7265
  var PLUGIN_ID2 = "kit@kit";
5979
7266
  var MARKETPLACE_PATH = "./node_modules/@joshuafolkken/kit";
5980
- var CLAUDE_SETTINGS_DESTINATION = path11.join(".claude", "settings.json");
7267
+ var CLAUDE_SETTINGS_DESTINATION = path15.join(".claude", "settings.json");
5981
7268
  var PLUGIN_SETTINGS = {
5982
7269
  extraKnownMarketplaces: {
5983
7270
  [MARKETPLACE_NAME]: { source: { source: "directory", path: MARKETPLACE_PATH } }
@@ -6003,7 +7290,7 @@ var HOOK_DIST_DIR = "dist/hooks";
6003
7290
  var RUN_HOOK_SCRIPT = "scripts/hooks/run-hook.sh";
6004
7291
  var ROOT_LOOKUP = "git rev-parse --show-toplevel";
6005
7292
  var CODEX_ROOT = `$( (unset ${GIT_LOCATION_VARIABLES.join(" ")}; ${ROOT_LOOKUP} 2>/dev/null) || ${ROOT_LOOKUP})`;
6006
- var PROJECT_ROOT_PREFIX = `project_root="\${CLAUDE_PROJECT_DIR:-${CODEX_ROOT}}" && cd "$project_root" && `;
7293
+ var PROJECT_ROOT_PREFIX = `export JOSH_HOOK_CWD="$PWD" && project_root="\${CLAUDE_PROJECT_DIR:-${CODEX_ROOT}}" && cd "$project_root" && `;
6007
7294
  function hook_launch_command(name) {
6008
7295
  return `${PROJECT_ROOT_PREFIX}sh ${RUN_HOOK_SCRIPT} ${name}`;
6009
7296
  }
@@ -6039,14 +7326,16 @@ var PLAIN_KIT_COMMANDS = new Set(
6039
7326
  var LOCATION_VARIABLES = "GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_COMMON_DIR GIT_DIR GIT_INDEX_FILE GIT_NAMESPACE GIT_OBJECT_DIRECTORY GIT_WORK_TREE";
6040
7327
  var ROOT_LOOKUP2 = "git rev-parse --show-toplevel";
6041
7328
  var LEGACY_ROOT_PREFIX = `if project_root="$(unset ${LOCATION_VARIABLES}; ${ROOT_LOOKUP2} 2>/dev/null)"; then unset ${LOCATION_VARIABLES}; else project_root="$(${ROOT_LOOKUP2})"; fi && cd "$project_root" && `;
7329
+ var PRE_WORKTREE_ROOT_PREFIX = `project_root="\${CLAUDE_PROJECT_DIR:-$( (unset ${LOCATION_VARIABLES}; ${ROOT_LOOKUP2} 2>/dev/null) || ${ROOT_LOOKUP2})}" && cd "$project_root" && `;
6042
7330
  var ROOT_PREFIXES = [
6043
7331
  hook_launch.PROJECT_ROOT_PREFIX,
7332
+ PRE_WORKTREE_ROOT_PREFIX,
6044
7333
  LEGACY_ROOT_PREFIX
6045
7334
  ].map((prefix) => JSON.stringify(prefix).slice(1, -1));
6046
7335
  var [PROJECT_ROOT_PREFIX2 = ""] = ROOT_PREFIXES;
6047
7336
 
6048
7337
  // scripts/init/security-policy.ts
6049
- import path12 from "node:path";
7338
+ import path16 from "node:path";
6050
7339
  var SECURITY_MD = "SECURITY.md";
6051
7340
  var SECURITY_TEMPLATE = "templates/SECURITY.md";
6052
7341
  var REPOSITORY_PLACEHOLDER = "{{REPOSITORY}}";
@@ -6062,15 +7351,15 @@ function apply_repository(content, repository) {
6062
7351
  return content.replaceAll(REPOSITORY_PLACEHOLDER, () => repository);
6063
7352
  }
6064
7353
  function apply_repository_for_destination(destination_path, content) {
6065
- if (path12.basename(destination_path) !== SECURITY_MD) return content;
6066
- const repository = repository_at(path12.dirname(destination_path));
7354
+ if (path16.basename(destination_path) !== SECURITY_MD) return content;
7355
+ const repository = repository_at(path16.dirname(destination_path));
6067
7356
  if (repository === void 0) {
6068
7357
  throw new Error(`${SECURITY_MD} needs a GitHub origin to name the repository it reports to`);
6069
7358
  }
6070
7359
  return apply_repository(content, repository);
6071
7360
  }
6072
7361
  function does_report_to_kit(root) {
6073
- const existing = file_reader.read_if_readable(path12.join(root, SECURITY_MD)) ?? "";
7362
+ const existing = file_reader.read_if_readable(path16.join(root, SECURITY_MD)) ?? "";
6074
7363
  return new Set(existing.split(LINK_DELIMITER)).has(KIT_ADVISORY_URL);
6075
7364
  }
6076
7365
  function unresolved_warning(root) {
@@ -6190,7 +7479,7 @@ function requested_profile(args, usage = "josh init [--profile basic|full]") {
6190
7479
  }
6191
7480
  function read_manifest(root) {
6192
7481
  if (!existsSync5(package_manifest.manifest_path(root))) return void 0;
6193
- const parsed = package_manifest.read_manifest(root, z22.unknown());
7482
+ const parsed = package_manifest.read_manifest(root, z26.unknown());
6194
7483
  if (!json_value.is_record(parsed)) throw new Error("package.json must contain an object");
6195
7484
  return parsed;
6196
7485
  }
@@ -6231,12 +7520,12 @@ function should_visit(entry) {
6231
7520
  function scan_directory(directory, pending) {
6232
7521
  const entries = readdirSync(directory, { withFileTypes: true });
6233
7522
  pending.push(
6234
- ...entries.filter((entry) => should_visit(entry)).map((entry) => path13.join(directory, entry.name))
7523
+ ...entries.filter((entry) => should_visit(entry)).map((entry) => path17.join(directory, entry.name))
6235
7524
  );
6236
- return entries.filter((entry) => entry.isFile()).map((entry) => path13.join(directory, entry.name));
7525
+ return entries.filter((entry) => entry.isFile()).map((entry) => path17.join(directory, entry.name));
6237
7526
  }
6238
7527
  function is_project_file(root, file) {
6239
- const relative_path = path13.relative(root, file).split(path13.sep).join(path13.posix.sep);
7528
+ const relative_path = path17.relative(root, file).split(path17.sep).join(path17.posix.sep);
6240
7529
  return !kit_written_paths.is_kit_written(relative_path);
6241
7530
  }
6242
7531
  function list_files(root) {
@@ -6248,7 +7537,7 @@ function list_files(root) {
6248
7537
  return files;
6249
7538
  }
6250
7539
  function file_extensions(root) {
6251
- return list_files(root).filter((file) => is_project_file(root, file)).map((file) => path13.extname(file));
7540
+ return list_files(root).filter((file) => is_project_file(root, file)).map((file) => path17.extname(file));
6252
7541
  }
6253
7542
  function scan_files(root) {
6254
7543
  const extensions = file_extensions(root);
@@ -6262,7 +7551,7 @@ function has_github_remote(root) {
6262
7551
  return url !== void 0 && repo_origin.parse_origin_url(url) !== void 0;
6263
7552
  }
6264
7553
  function has_git(root) {
6265
- return existsSync5(path13.join(root, ".git"));
7554
+ return existsSync5(path17.join(root, ".git"));
6266
7555
  }
6267
7556
  function has_workflow(root) {
6268
7557
  return workflow_mark.is_requested() || workflow_mark.is_recorded(read_manifest(root));
@@ -6295,7 +7584,7 @@ var SKIP_MARKER = "\u2014 skipping";
6295
7584
  var NOT_USED_MARKER = "which this project does not use";
6296
7585
 
6297
7586
  // scripts/gate/project-checks.ts
6298
- import { z as z23 } from "zod";
7587
+ import { z as z27 } from "zod";
6299
7588
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
6300
7589
  "node_modules",
6301
7590
  ".git",
@@ -6329,7 +7618,7 @@ var CSPELL_CONFIGS = [
6329
7618
  ];
6330
7619
  var TYPE_CONFIGS = ["tsconfig.json", "jsconfig.json"];
6331
7620
  var DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
6332
- var object_schema = z23.record(z23.string(), z23.unknown());
7621
+ var object_schema = z27.record(z27.string(), z27.unknown());
6333
7622
  function project_root(directory) {
6334
7623
  return ancestor_directories.nearest(
6335
7624
  directory,
@@ -6346,7 +7635,7 @@ function has_files(directory, pattern) {
6346
7635
  return globSync2(pattern, { cwd: project_root(directory), exclude: is_ignored }).length > 0;
6347
7636
  }
6348
7637
  function has_config(directory, names) {
6349
- return names.some((name) => existsSync6(path14.join(project_root(directory), name)));
7638
+ return names.some((name) => existsSync6(path18.join(project_root(directory), name)));
6350
7639
  }
6351
7640
  function has_bin(directory, bin_name) {
6352
7641
  return find_local_bin_upwards(directory, bin_name) !== void 0;
@@ -6360,7 +7649,7 @@ function is_basic_manifest(manifest) {
6360
7649
  return project_profile.parse_profile(josh["profile"]) === "basic";
6361
7650
  }
6362
7651
  function read_manifest2(directory) {
6363
- return package_manifest.read_manifest_if_valid(project_root(directory), z23.unknown());
7652
+ return package_manifest.read_manifest_if_valid(project_root(directory), z27.unknown());
6364
7653
  }
6365
7654
  function is_basic(directory) {
6366
7655
  return is_basic_manifest(read_manifest2(directory));
@@ -6424,24 +7713,31 @@ var project_checks = {
6424
7713
  export {
6425
7714
  hook_decision,
6426
7715
  bounded_pool,
7716
+ lane_guard_policy,
7717
+ delegation_policy,
7718
+ investigation_reads,
7719
+ duplicate_read_outcome,
6427
7720
  git_argv,
6428
7721
  shell_segments,
6429
7722
  bash_triggers,
7723
+ stop_decision,
7724
+ concise_reply,
7725
+ repo_party,
6430
7726
  tail_commands,
6431
- filing_cap,
6432
- CONTEXT_CUT_THRESHOLD,
7727
+ run_cut_handoff,
6433
7728
  cost_verdict,
6434
7729
  cost_cli,
6435
7730
  backlog_budget,
6436
- run_cut_handoff,
6437
7731
  run_cut,
6438
7732
  run_tail_rule,
6439
7733
  lane_background,
6440
7734
  interactive_ask,
7735
+ stop_rules,
6441
7736
  EPIC_LABEL,
6442
7737
  BUG_LABEL,
6443
7738
  ENHANCEMENT_LABEL,
6444
7739
  IN_PROGRESS_LABEL,
7740
+ PLANNED_LABEL,
6445
7741
  RUN_SOLO_LABEL,
6446
7742
  RUN_LANE_LABEL,
6447
7743
  PRIORITY_HIGH_LABEL,
@@ -6467,7 +7763,6 @@ export {
6467
7763
  release_age,
6468
7764
  gh_spawn,
6469
7765
  project_checks,
6470
- repo_party,
6471
7766
  MAX_SCANNED,
6472
7767
  git_gh_issue_write,
6473
7768
  SUMMARY_FIELDS,
@@ -6479,5 +7774,7 @@ export {
6479
7774
  PROCESS_ALIVE,
6480
7775
  PROCESS_NONE,
6481
7776
  PROCESS_UNKNOWN,
6482
- run_liveness
7777
+ run_liveness,
7778
+ backlog_overlap,
7779
+ lane_plan
6483
7780
  };