@joshuafolkken/kit 1.1514.0 → 1.1784.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 (858) hide show
  1. package/.claude/agents/investigator.md +1 -1
  2. package/.claude/settings.json +5 -5
  3. package/.claude/skills/epic-commands/SKILL.md +26 -65
  4. package/.claude/skills/epic-commands/epic-bundle.md +3 -1
  5. package/.claude/skills/workflow-commands/SKILL.md +5 -5
  6. package/.claude/skills/workflow-commands/background-commands.md +5 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +10 -8
  8. package/.claude/skills/workflow-commands/backlogrun-handoff-report.md +7 -25
  9. package/.claude/skills/workflow-commands/backlogrun-lanes.md +17 -13
  10. package/.claude/skills/workflow-commands/backlogrun-park.md +14 -23
  11. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -2
  12. package/.claude/skills/workflow-commands/backlogrun-recovery.md +5 -5
  13. package/.claude/skills/workflow-commands/backlogrun-steps.md +10 -10
  14. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  15. package/.claude/skills/workflow-commands/chain-rule.md +13 -7
  16. package/.claude/skills/workflow-commands/delegation.md +6 -5
  17. package/.claude/skills/workflow-commands/entry-sequence.md +9 -9
  18. package/.claude/skills/workflow-commands/followup.md +9 -31
  19. package/.claude/skills/workflow-commands/fullrun-steps.md +36 -45
  20. package/.claude/skills/workflow-commands/fullrun.md +2 -2
  21. package/.claude/skills/workflow-commands/halfrun.md +17 -25
  22. package/.claude/skills/workflow-commands/issue-comments.md +16 -12
  23. package/.claude/skills/workflow-commands/issue-scout.md +2 -2
  24. package/.claude/skills/workflow-commands/kickoff.md +24 -23
  25. package/.claude/skills/workflow-commands/latest-gate.md +7 -12
  26. package/.claude/skills/workflow-commands/needs-human-review.md +9 -14
  27. package/.claude/skills/workflow-commands/observation-filing.md +4 -36
  28. package/.claude/skills/workflow-commands/observation-ledger.md +1 -1
  29. package/.claude/skills/workflow-commands/pre-gate-cut.md +13 -30
  30. package/.claude/skills/workflow-commands/prerequisite.md +10 -16
  31. package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
  32. package/.claude/skills/workflow-commands/prrun.md +3 -7
  33. package/.claude/skills/workflow-commands/retrospective.md +1 -1
  34. package/.claude/skills/workflow-commands/split-assessment.md +13 -1
  35. package/.claude/skills/workflow-commands/working-tree-hold.md +9 -9
  36. package/.codex/hooks.json +5 -5
  37. package/.github/actions/setup-node/action.yml +9 -0
  38. package/.github/actions/setup-pnpm/action.yml +1 -1
  39. package/.github/dependabot.yml +16 -0
  40. package/.github/release.yml +0 -2
  41. package/.github/workflows/ci.yml +120 -10
  42. package/.github/workflows/publish.yml +8 -1
  43. package/.github/workflows/sonar-qube.yml +1 -0
  44. package/.prettierignore +2 -0
  45. package/.vscode/tasks.json +1 -2
  46. package/CLAUDE.md +3 -7
  47. package/README.md +4 -2
  48. package/SECURITY.md +1 -1
  49. package/dist/CLAUDE.md +3 -7
  50. package/dist/commands/document-read-cli.js +882 -0
  51. package/dist/commands/{document-section-cli.js → document-section-main.js} +79 -37
  52. package/dist/commands/read-set-cli.js +1867 -0
  53. package/dist/commands/run-prep-main.js +21653 -0
  54. package/dist/commands/run-step-cli.js +23880 -0
  55. package/dist/config-merge/index.js +5 -1
  56. package/dist/hooks/audit-provision.js +20 -35
  57. package/dist/hooks/{chunk-DGT54IH5.js → chunk-AZX4FGQ3.js} +63 -17
  58. package/dist/hooks/chunk-CWMBGKYW.js +6483 -0
  59. package/dist/hooks/chunk-O2YBIDSI.js +4306 -0
  60. package/dist/hooks/{chunk-TNUMY6NL.js → chunk-PT5R6UJD.js} +4193 -1900
  61. package/dist/hooks/chunk-TUIFS5E7.js +72 -0
  62. package/dist/hooks/{chunk-723HKZO5.js → chunk-VWWCFGWK.js} +1 -1
  63. package/dist/hooks/{chunk-APUO6U4J.js → chunk-WR2KQAYZ.js} +1843 -440
  64. package/dist/hooks/{chunk-O6GDRNJB.js → chunk-XGBNSWEI.js} +248 -46
  65. package/dist/hooks/chunk-YCDOIR3I.js +3456 -0
  66. package/dist/hooks/codex-hook-adapter.js +10 -22
  67. package/dist/hooks/format-edited.js +5 -6
  68. package/dist/hooks/inputs.json +443 -317
  69. package/dist/hooks/pretool-guard.js +7 -25
  70. package/dist/hooks/session-lang.js +44 -51
  71. package/dist/hooks/stop-guard.js +7716 -2187
  72. package/dist/josh.js +6002 -3583
  73. package/dist/self-sync-guard/index.js +74 -10
  74. package/dist/version/index.js +115 -406
  75. package/eslint/base.js +1 -1
  76. package/lefthook/base.yml +3 -1
  77. package/package.json +10 -48
  78. package/pnpm-workspace.yaml +4 -2
  79. package/prompts/coding-standards.md +1 -1
  80. package/prompts/collaboration-workflow/file-edits.md +1 -7
  81. package/prompts/collaboration-workflow/issue-citation.md +1 -1
  82. package/prompts/collaboration-workflow/issue-template.md +33 -16
  83. package/prompts/collaboration-workflow/lane-instructions.md +1 -2
  84. package/prompts/collaboration-workflow/measurement.md +37 -0
  85. package/prompts/collaboration-workflow/operating-rules.md +4 -6
  86. package/prompts/collaboration-workflow/output-bounds.md +2 -7
  87. package/prompts/collaboration-workflow/plan-comment.md +3 -42
  88. package/prompts/collaboration-workflow/principles.md +5 -0
  89. package/prompts/collaboration-workflow/report-format.md +31 -60
  90. package/prompts/collaboration-workflow/residency.md +2 -2
  91. package/prompts/collaboration-workflow/shell-body.md +6 -16
  92. package/prompts/collaboration-workflow/turn-batching.md +0 -2
  93. package/prompts/collaboration-workflow/upstream-interrupt.md +14 -4
  94. package/prompts/collaboration-workflow/wip-cap.md +1 -1
  95. package/prompts/collaboration-workflow.md +3 -7
  96. package/prompts/refactoring.md +1 -1
  97. package/prompts/review-rubric.md +1 -2
  98. package/prompts/review.md +41 -21
  99. package/prompts/testing-guide.md +8 -43
  100. package/scripts/agent/agent-event.ts +15 -15
  101. package/scripts/agent/agent-exit-record.ts +13 -1
  102. package/scripts/agent/api-auth.ts +41 -0
  103. package/scripts/agent/api-outage.ts +1 -1
  104. package/scripts/auto-ok/auto-ok-cli.ts +14 -16
  105. package/scripts/auto-ok/auto-ok-judge-read.ts +12 -3
  106. package/scripts/auto-ok/auto-ok-judge-write.ts +46 -19
  107. package/scripts/auto-ok/auto-ok-judge.ts +13 -2
  108. package/scripts/auto-ok/auto-ok-main.ts +17 -0
  109. package/scripts/backlog/backlog-arrival.ts +14 -3
  110. package/scripts/backlog/backlog-budget-cli.ts +7 -33
  111. package/scripts/backlog/backlog-budget-main.ts +16 -0
  112. package/scripts/backlog/backlog-budget.ts +2 -3
  113. package/scripts/backlog/backlog-defect-priority.ts +4 -3
  114. package/scripts/backlog/backlog-drive-adopt.ts +67 -0
  115. package/scripts/backlog/backlog-drive-args.ts +4 -5
  116. package/scripts/backlog/backlog-drive-cli.ts +113 -28
  117. package/scripts/backlog/backlog-drive-hold.ts +162 -0
  118. package/scripts/backlog/backlog-drive-idle.ts +151 -0
  119. package/scripts/backlog/backlog-drive-judge.ts +25 -0
  120. package/scripts/backlog/backlog-drive-memory.ts +126 -0
  121. package/scripts/backlog/backlog-drive-named-offer.ts +5 -10
  122. package/scripts/backlog/backlog-drive-named.ts +53 -6
  123. package/scripts/backlog/backlog-drive-offer-argv.ts +3 -8
  124. package/scripts/backlog/backlog-drive-paused.ts +169 -0
  125. package/scripts/backlog/backlog-drive-restore.ts +30 -7
  126. package/scripts/backlog/backlog-drive-seats.ts +18 -8
  127. package/scripts/backlog/backlog-drive-token-handling.ts +38 -0
  128. package/scripts/backlog/backlog-drive.ts +79 -75
  129. package/scripts/backlog/backlog-next-main.ts +16 -0
  130. package/scripts/backlog/backlog-next-shared.ts +73 -0
  131. package/scripts/backlog/backlog-next.ts +48 -16
  132. package/scripts/backlog/backlog-occupied.ts +52 -39
  133. package/scripts/backlog/backlog-offer-cli.ts +6 -21
  134. package/scripts/backlog/backlog-offer-exclude.ts +15 -0
  135. package/scripts/backlog/backlog-offer-main.ts +12 -0
  136. package/scripts/backlog/backlog-offer.ts +36 -3
  137. package/scripts/backlog/backlog-overlap.ts +22 -4
  138. package/scripts/backlog/backlog-plan-read.ts +6 -2
  139. package/scripts/backlog/backlog-plan.ts +18 -4
  140. package/scripts/backlog/backlog-pool.ts +10 -10
  141. package/scripts/backlog/backlog-rank.ts +10 -2
  142. package/scripts/backlog/backlog-ready.ts +112 -30
  143. package/scripts/backlog/backlog-stalled-detect.ts +109 -36
  144. package/scripts/backlog/backlog-stalled.ts +52 -16
  145. package/scripts/backlog/backlog-wait-cause.ts +86 -0
  146. package/scripts/backlog/backlog-waves.ts +39 -13
  147. package/scripts/backlog/backlogrun-args.ts +19 -7
  148. package/scripts/backlog/backlogrun-cli.ts +19 -5
  149. package/scripts/backlog/backlogrun-launch.ts +6 -0
  150. package/scripts/backlog/backlogrun-restart.ts +97 -0
  151. package/scripts/backlog/backlogrun-stop.ts +79 -7
  152. package/scripts/behavior/behavior-cli.ts +2 -2
  153. package/scripts/behavior/behavior-rules.ts +47 -24
  154. package/scripts/build/build-bin.ts +2 -16
  155. package/scripts/build/build-claude-md.ts +0 -7
  156. package/scripts/build/build-commands.ts +0 -13
  157. package/scripts/build/build-hooks.ts +49 -48
  158. package/scripts/build/hook-bundle-stamp.ts +29 -11
  159. package/scripts/build/local-bin.ts +1 -1
  160. package/scripts/cases/cases-cli.ts +25 -13
  161. package/scripts/cases/cases-interaction-scan.ts +134 -0
  162. package/scripts/cases/cases-interaction.ts +183 -0
  163. package/scripts/cases/cases-logic.ts +39 -6
  164. package/scripts/ci/pr-classification-main.ts +18 -0
  165. package/scripts/ci/pr-classification.ts +10 -17
  166. package/scripts/clone/clone-scan.ts +1 -1
  167. package/scripts/cost-runtime/cost-blocks.ts +22 -37
  168. package/scripts/cost-runtime/cost-cli.ts +31 -14
  169. package/scripts/cost-runtime/cost-main.ts +12 -0
  170. package/scripts/cost-runtime/cost-transcript.ts +1 -0
  171. package/scripts/delegation/delegation-policy.ts +17 -4
  172. package/scripts/delegation/duplicate-read-guard.ts +0 -8
  173. package/scripts/delegation/investigation-guard.ts +0 -8
  174. package/scripts/delegation/investigation-reads.ts +3 -4
  175. package/scripts/dependabot/dependabot-cooldown.ts +194 -0
  176. package/scripts/discovery/repo-discovery.ts +24 -6
  177. package/scripts/doctor/doctor-consumer.ts +2 -5
  178. package/scripts/document/document-byte-budget.ts +8 -6
  179. package/scripts/document/document-scan.ts +1 -1
  180. package/scripts/document/document-section-cli.ts +0 -8
  181. package/scripts/document/document-section-main.ts +12 -0
  182. package/scripts/epic/epic-audit-cli.ts +12 -6
  183. package/scripts/epic/epic-bundle-apply.ts +137 -0
  184. package/scripts/epic/epic-bundle-cli.ts +32 -121
  185. package/scripts/epic/epic-bundle-format.ts +116 -0
  186. package/scripts/epic/epic-bundle-main.ts +17 -0
  187. package/scripts/epic/epic-busy.ts +5 -12
  188. package/scripts/epic/epic-classify.ts +24 -15
  189. package/scripts/epic/epic-cli-argv.ts +31 -0
  190. package/scripts/epic/epic-cli-decide.ts +42 -0
  191. package/scripts/epic/epic-cli.ts +6 -38
  192. package/scripts/epic/epic-close.ts +112 -55
  193. package/scripts/epic/epic-cross-repo.ts +4 -7
  194. package/scripts/epic/epic-decide.ts +119 -0
  195. package/scripts/epic/epic-decision.ts +9 -0
  196. package/scripts/epic/epic-fetch.ts +4 -0
  197. package/scripts/epic/epic-graph.ts +2 -0
  198. package/scripts/epic/epic-issue.ts +5 -1
  199. package/scripts/epic/epic-label-age.ts +1 -1
  200. package/scripts/epic/epic-lane-offer.ts +39 -4
  201. package/scripts/epic/epic-next-main.ts +24 -0
  202. package/scripts/epic/epic-next.ts +6 -24
  203. package/scripts/epic/epic-outside-blocker.ts +6 -2
  204. package/scripts/epic/epic-parse.ts +9 -3
  205. package/scripts/epic/epic-read.ts +1 -0
  206. package/scripts/epic/epic-reconcile.ts +39 -0
  207. package/scripts/epic/epic-run.ts +10 -1
  208. package/scripts/epic/epic-solo.ts +50 -14
  209. package/scripts/epic/epic.ts +41 -7
  210. package/scripts/exports/unused-members-cli.ts +2 -2
  211. package/scripts/followup/followup-checks-event.ts +8 -3
  212. package/scripts/followup/followup-coderabbit-wait.ts +82 -0
  213. package/scripts/followup/followup-review-gates.ts +81 -0
  214. package/scripts/followup/git-followup-stages.ts +13 -3
  215. package/scripts/followup/git-followup-workflow.ts +19 -66
  216. package/scripts/followup/git-pr-followup-wrapup.ts +9 -14
  217. package/scripts/followup/git-pr-followup.ts +36 -8
  218. package/scripts/fresh-machine/fresh-machine-cli.ts +1 -1
  219. package/scripts/fresh-machine/fresh-machine-run.ts +8 -8
  220. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +1 -1
  221. package/scripts/gate/busy-average.ts +58 -0
  222. package/scripts/gate/check-cpu.ts +60 -0
  223. package/scripts/gate/check-inputs.ts +39 -95
  224. package/scripts/gate/check-reuse.ts +33 -54
  225. package/scripts/gate/command-memory.ts +192 -0
  226. package/scripts/gate/core-admission.ts +218 -28
  227. package/scripts/gate/core-budget-timing.ts +37 -0
  228. package/scripts/gate/core-budget.ts +80 -98
  229. package/scripts/gate/core-wait-record.ts +48 -0
  230. package/scripts/gate/core-wait.ts +37 -5
  231. package/scripts/gate/core-weights.ts +86 -0
  232. package/scripts/gate/free-cores.ts +27 -4
  233. package/scripts/gate/gate-ledger.ts +32 -4
  234. package/scripts/gate/gate-plan.ts +41 -34
  235. package/scripts/gate/gate-report.ts +14 -2
  236. package/scripts/gate/gate-run-steps.ts +53 -8
  237. package/scripts/gate/gate-skip.ts +12 -2
  238. package/scripts/gate/gate-steps.ts +12 -1
  239. package/scripts/gate/input-fingerprint.ts +42 -0
  240. package/scripts/gate/machine-capacity.ts +65 -31
  241. package/scripts/gate/moved-paths.ts +67 -0
  242. package/scripts/gate/project-checks.ts +5 -12
  243. package/scripts/gate/scoped-green.ts +12 -8
  244. package/scripts/gate/since-green.ts +62 -0
  245. package/scripts/gate/toolchain-inputs.ts +115 -0
  246. package/scripts/gate/verification-gate.ts +27 -24
  247. package/scripts/gate/verification-inputs.ts +250 -0
  248. package/scripts/gh/gh-author-association.ts +96 -9
  249. package/scripts/gh/git-ai-review-scan.ts +4 -2
  250. package/scripts/gh/git-gh-api-path.ts +15 -0
  251. package/scripts/gh/git-gh-branch-rules.ts +73 -0
  252. package/scripts/gh/git-gh-conditional.ts +5 -2
  253. package/scripts/gh/git-gh-issue-read.ts +5 -1
  254. package/scripts/gh/git-gh-issue-write.ts +67 -29
  255. package/scripts/gh/git-gh-issue.ts +7 -3
  256. package/scripts/gh/git-gh-poll-read.ts +89 -0
  257. package/scripts/gh/git-gh-pr-auto-merge.ts +8 -3
  258. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  259. package/scripts/gh/git-gh-pr-snapshot.ts +33 -20
  260. package/scripts/gh/git-gh-pr.ts +27 -15
  261. package/scripts/gh/git-pr-checks-eval.ts +39 -7
  262. package/scripts/gh/git-pr-checks-parse.ts +39 -9
  263. package/scripts/gh/git-pr-merge-checked.ts +80 -0
  264. package/scripts/git/base-tree.ts +19 -2
  265. package/scripts/git/changed-file-scope.ts +6 -4
  266. package/scripts/git/changed-paths.ts +18 -1
  267. package/scripts/git/git-diff-reads.ts +27 -2
  268. package/scripts/git/git-grep-hits.ts +85 -0
  269. package/scripts/git/git-location-environment.ts +1 -1
  270. package/scripts/git/git-merge-conflicts.ts +83 -0
  271. package/scripts/git/git-preflight.ts +37 -12
  272. package/scripts/git/git-push-transport.ts +1 -2
  273. package/scripts/git/git-schemas.ts +17 -7
  274. package/scripts/git/git-spawn.ts +1 -2
  275. package/scripts/git/git-status.ts +4 -4
  276. package/scripts/git/main-merge-main.ts +12 -0
  277. package/scripts/git/main-merge-regenerate.ts +2 -2
  278. package/scripts/git/main-merge.ts +25 -9
  279. package/scripts/git/main-sync-main.ts +12 -0
  280. package/scripts/git/main-sync.ts +0 -9
  281. package/scripts/git/stash/git-stash.ts +27 -4
  282. package/scripts/git/stash/{stash-pop-args.ts → stash-args.ts} +13 -10
  283. package/scripts/git/stash/stash-pop-cli.ts +6 -12
  284. package/scripts/git/stash/stash-pop-main.ts +12 -0
  285. package/scripts/git/stash/stash-push-cli.ts +76 -0
  286. package/scripts/git/stash/stash-push-main.ts +12 -0
  287. package/scripts/hooks/batch-guard.ts +0 -8
  288. package/scripts/hooks/check-commit-message-main.ts +8 -0
  289. package/scripts/hooks/check-commit-message.ts +4 -7
  290. package/scripts/hooks/format-edited-file.ts +8 -24
  291. package/scripts/hooks/pre-commit-lint.ts +30 -3
  292. package/scripts/hooks/pre-push-unit.ts +4 -2
  293. package/scripts/hooks/pretool-guard-cli.ts +1 -8
  294. package/scripts/hooks/pretool-guard.ts +32 -19
  295. package/scripts/hooks/reserved-run.ts +67 -9
  296. package/scripts/hooks/run-hook.sh +13 -4
  297. package/scripts/hooks/shell-rules-notice.ts +37 -0
  298. package/scripts/hooks/stop-actions.ts +2 -21
  299. package/scripts/hooks/stop-guard.ts +13 -9
  300. package/scripts/init/hook-command-rewrite.ts +19 -5
  301. package/scripts/init/hook-launch.ts +15 -17
  302. package/scripts/init/init-actions.ts +0 -6
  303. package/scripts/init/init-ai-copy.ts +10 -36
  304. package/scripts/init/init-bootstrap.ts +9 -8
  305. package/scripts/init/init-dry-run-report.ts +1 -1
  306. package/scripts/init/init-dry-run.ts +1 -1
  307. package/scripts/init/init-file-action.ts +7 -9
  308. package/scripts/init/init-logic-json-merge.ts +2 -2
  309. package/scripts/init/init-logic-migrate.ts +9 -82
  310. package/scripts/init/init-logic.ts +3 -2
  311. package/scripts/init/init-main.ts +12 -0
  312. package/scripts/init/init.ts +5 -12
  313. package/scripts/init/kit-development-versions.ts +3 -7
  314. package/scripts/init/kit-written-paths.ts +2 -2
  315. package/scripts/init/project-profile.ts +5 -4
  316. package/scripts/init/tsconfig-preset-migration.ts +2 -1
  317. package/scripts/init/vscode-tasks.ts +1 -1
  318. package/scripts/issue/defect-origin-cli.ts +106 -0
  319. package/scripts/issue/defect-origin-line.ts +60 -0
  320. package/scripts/issue/defect-origin-main.ts +11 -0
  321. package/scripts/issue/defect-origin.ts +114 -0
  322. package/scripts/issue/defect-rate-cli.ts +34 -78
  323. package/scripts/issue/defect-rate.ts +0 -16
  324. package/scripts/issue/defect-report-cli.ts +128 -0
  325. package/scripts/issue/defect-report-format.ts +56 -0
  326. package/scripts/issue/defect-report-main.ts +11 -0
  327. package/scripts/issue/defect-report.ts +160 -0
  328. package/scripts/issue/expected-effect-lint.ts +1 -0
  329. package/scripts/issue/issue-already-done-cli.ts +62 -0
  330. package/scripts/issue/issue-body-or-comment.ts +40 -0
  331. package/scripts/issue/issue-cite-cli.ts +0 -9
  332. package/scripts/issue/issue-cite-main.ts +12 -0
  333. package/scripts/issue/issue-classification.ts +12 -7
  334. package/scripts/issue/issue-comment-cli.ts +10 -24
  335. package/scripts/issue/issue-comment-main.ts +12 -0
  336. package/scripts/issue/issue-evidence-exit.ts +100 -0
  337. package/scripts/issue/issue-file-cli.ts +29 -7
  338. package/scripts/issue/issue-file.ts +19 -19
  339. package/scripts/issue/issue-fold-cli.ts +2 -11
  340. package/scripts/issue/issue-fold-main.ts +12 -0
  341. package/scripts/issue/issue-labels.ts +81 -90
  342. package/scripts/issue/issue-lint-cli.ts +2 -10
  343. package/scripts/issue/issue-lint-main.ts +12 -0
  344. package/scripts/issue/issue-measured-cli.ts +56 -0
  345. package/scripts/issue/issue-merged.ts +19 -2
  346. package/scripts/issue/issue-not-before-label.ts +135 -0
  347. package/scripts/issue/issue-not-before.ts +111 -0
  348. package/scripts/issue/issue-number-shape.ts +15 -1
  349. package/scripts/issue/issue-park.ts +28 -0
  350. package/scripts/issue/issue-plan-cli.ts +151 -0
  351. package/scripts/issue/issue-priority.ts +151 -0
  352. package/scripts/issue/issue-rank.ts +7 -1
  353. package/scripts/issue/issue-read-cli.ts +3 -10
  354. package/scripts/issue/issue-read-main.ts +12 -0
  355. package/scripts/issue/issue-read.ts +29 -5
  356. package/scripts/issue/issue-release-cli.ts +0 -10
  357. package/scripts/issue/issue-release-main.ts +13 -0
  358. package/scripts/issue/issue-release.ts +7 -12
  359. package/scripts/issue/issue-sample-gate.ts +192 -0
  360. package/scripts/issue/issue-sample-source.ts +76 -0
  361. package/scripts/issue/issue-scout-cli.ts +0 -11
  362. package/scripts/issue/issue-scout-main.ts +14 -0
  363. package/scripts/issue/issue-state-cli.ts +0 -10
  364. package/scripts/issue/issue-state-main.ts +12 -0
  365. package/scripts/issue/issue-watch-diff.ts +56 -15
  366. package/scripts/issue/issue-watch.ts +2 -1
  367. package/scripts/issue/issue-wip.ts +2 -7
  368. package/scripts/issue/measure-rerun-cli.ts +13 -8
  369. package/scripts/issue/plan-consumers-cli.ts +39 -0
  370. package/scripts/issue/plan-consumers-main.ts +11 -0
  371. package/scripts/issue/plan-consumers-section.ts +55 -0
  372. package/scripts/issue/plan-consumers.ts +127 -0
  373. package/scripts/josh/agent-session-environment.ts +1 -1
  374. package/scripts/josh/command-usage.ts +15 -43
  375. package/scripts/josh/file-map-stamp.ts +49 -16
  376. package/scripts/josh/hook-decision.ts +32 -20
  377. package/scripts/josh/josh-command-map.ts +4 -1
  378. package/scripts/josh/josh-command-types.ts +12 -6
  379. package/scripts/josh/josh-commands-agent-hooks.ts +83 -0
  380. package/scripts/josh/josh-commands-ai.ts +157 -158
  381. package/scripts/josh/josh-commands-backlog.ts +82 -16
  382. package/scripts/josh/josh-commands-clone.ts +1 -0
  383. package/scripts/josh/josh-commands-development.ts +111 -71
  384. package/scripts/josh/josh-commands-document.ts +43 -1
  385. package/scripts/josh/josh-commands-epic.ts +171 -0
  386. package/scripts/josh/josh-commands-hooks.ts +25 -2
  387. package/scripts/josh/josh-commands-issue-write.ts +181 -0
  388. package/scripts/josh/josh-commands-issue.ts +124 -24
  389. package/scripts/josh/josh-commands-lane.ts +103 -2
  390. package/scripts/josh/josh-commands-lint.ts +19 -8
  391. package/scripts/josh/josh-commands-maintenance.ts +112 -4
  392. package/scripts/josh/josh-commands-pause.ts +29 -0
  393. package/scripts/josh/josh-commands-progress.ts +87 -0
  394. package/scripts/josh/josh-commands-project.ts +138 -9
  395. package/scripts/josh/josh-commands-review.ts +124 -0
  396. package/scripts/josh/josh-commands-run.ts +211 -23
  397. package/scripts/josh/josh-commands-ship.ts +169 -0
  398. package/scripts/josh/josh-commands-split.ts +9 -0
  399. package/scripts/josh/josh-commands-versioning.ts +49 -2
  400. package/scripts/josh/josh-commands-workflow.ts +144 -6
  401. package/scripts/josh/josh-logic.ts +6 -13
  402. package/scripts/josh/package-directory.ts +2 -4
  403. package/scripts/josh/process-identity.ts +13 -4
  404. package/scripts/josh/session-language-cli.ts +4 -8
  405. package/scripts/josh/session-language-main.ts +13 -0
  406. package/scripts/josh/session-language.ts +6 -1
  407. package/scripts/josh/shared-cache.ts +90 -0
  408. package/scripts/josh/stamp-file.ts +9 -15
  409. package/scripts/lane/lane-await.ts +51 -12
  410. package/scripts/lane/lane-cache.ts +5 -30
  411. package/scripts/lane/lane-capacity.ts +14 -23
  412. package/scripts/lane/lane-check-stats.ts +62 -21
  413. package/scripts/lane/lane-child-invocation.ts +8 -0
  414. package/scripts/lane/lane-cli.ts +15 -5
  415. package/scripts/lane/lane-close.ts +86 -18
  416. package/scripts/lane/lane-core-wait-stats.ts +66 -0
  417. package/scripts/lane/lane-dispatch.ts +24 -3
  418. package/scripts/lane/lane-gap-stats.ts +156 -0
  419. package/scripts/lane/lane-handoff.ts +69 -1
  420. package/scripts/lane/lane-held-stats.ts +129 -0
  421. package/scripts/lane/lane-install.ts +4 -11
  422. package/scripts/lane/lane-launch-cli.ts +0 -9
  423. package/scripts/lane/lane-launch-main.ts +12 -0
  424. package/scripts/lane/lane-ledger-schema.ts +266 -0
  425. package/scripts/lane/lane-ledger.ts +92 -123
  426. package/scripts/lane/lane-limit-cli.ts +12 -1
  427. package/scripts/lane/lane-measure-cli.ts +57 -11
  428. package/scripts/lane/lane-open.ts +9 -1
  429. package/scripts/lane/lane-parked.ts +3 -3
  430. package/scripts/lane/lane-pause-cli.ts +130 -0
  431. package/scripts/lane/lane-pause.ts +158 -0
  432. package/scripts/lane/lane-process-table.ts +166 -0
  433. package/scripts/lane/lane-reap-settle.ts +124 -0
  434. package/scripts/lane/lane-reap.ts +50 -15
  435. package/scripts/lane/lane-registry.ts +24 -3
  436. package/scripts/lane/lane-residents.ts +162 -0
  437. package/scripts/lane/lane-restart.ts +22 -2
  438. package/scripts/lane/lane-round-two-stats.ts +49 -0
  439. package/scripts/lane/lane-sampler.ts +4 -0
  440. package/scripts/lane/lane-session-action.ts +19 -5
  441. package/scripts/lane/lane-session-broker.ts +102 -21
  442. package/scripts/lane/lane-snapshot-trace.ts +84 -0
  443. package/scripts/lane/lane-spans.ts +51 -0
  444. package/scripts/lane/lane-spent.ts +6 -3
  445. package/scripts/lane/lane-stage-stats.ts +7 -2
  446. package/scripts/lane/lane-state.ts +302 -0
  447. package/scripts/lane/lane-stats.ts +58 -4
  448. package/scripts/lane/lane-stop-stats.ts +113 -37
  449. package/scripts/lane/lane-test-related-stats.ts +127 -0
  450. package/scripts/lane/lane-transcript-calls.ts +145 -0
  451. package/scripts/lane/lane-transcript-stats.ts +168 -0
  452. package/scripts/lane/lane-vacant.ts +13 -3
  453. package/scripts/lane/lane-withdraw-cli.ts +5 -4
  454. package/scripts/lane/lane-withdraw.ts +50 -49
  455. package/scripts/lane/process-directories.ts +70 -0
  456. package/scripts/lane/process-lane-marks.ts +94 -0
  457. package/scripts/lib/atomic-write.ts +62 -0
  458. package/scripts/lib/buffered-process.ts +12 -5
  459. package/scripts/lib/cli-flags.ts +53 -0
  460. package/scripts/lib/figure-format.ts +63 -0
  461. package/scripts/lib/json-value.ts +9 -1
  462. package/scripts/lib/package-manifest.ts +35 -0
  463. package/scripts/lib/read-file.ts +3 -1
  464. package/scripts/lib/stamped-memo.ts +63 -0
  465. package/scripts/lib/stdout-on-stderr.ts +24 -0
  466. package/scripts/lib/timeouts.ts +9 -0
  467. package/scripts/lib/transcript-record.ts +91 -0
  468. package/scripts/lines/effective-limit.ts +1 -1
  469. package/scripts/lint/eslint-cache-dependencies.ts +145 -0
  470. package/scripts/lint/eslint-daemon.ts +265 -0
  471. package/scripts/lint/format.ts +11 -2
  472. package/scripts/lint/import-graph.ts +279 -0
  473. package/scripts/lint/lint-gate-scope.ts +102 -0
  474. package/scripts/lint/lint-parallel-main.ts +19 -0
  475. package/scripts/lint/lint-parallel.ts +62 -30
  476. package/scripts/lint/lint-related-scope.ts +30 -24
  477. package/scripts/lint/lint-related.ts +38 -6
  478. package/scripts/notify/confirmation-options.ts +1 -2
  479. package/scripts/notify/telegram-notify.ts +20 -20
  480. package/scripts/notify/telegram-test-logic.ts +0 -1
  481. package/scripts/notify/telegram-test-main.ts +8 -0
  482. package/scripts/notify/telegram-test.ts +2 -6
  483. package/scripts/observations/observation-ledger.ts +13 -0
  484. package/scripts/observations/observations-flush-landing.ts +5 -3
  485. package/scripts/observations/observations-flush.ts +6 -1
  486. package/scripts/overrides/overrides-check.ts +2 -5
  487. package/scripts/overrides/overrides-files.ts +2 -1
  488. package/scripts/overrides/overrides-logic.ts +3 -6
  489. package/scripts/package/verify-optional-eslint-install.ts +8 -6
  490. package/scripts/propagate/propagate-publish.ts +2 -7
  491. package/scripts/propagate/propagate-targets.ts +4 -12
  492. package/scripts/refactor/import-graph.ts +9 -7
  493. package/scripts/release/github-release-runs.ts +17 -6
  494. package/scripts/release/release-approval-prune.ts +18 -0
  495. package/scripts/release/release-history.ts +1 -3
  496. package/scripts/release/release-progress.ts +42 -22
  497. package/scripts/release/release-publish-run.ts +39 -0
  498. package/scripts/release/release-publish.ts +6 -6
  499. package/scripts/release/release-scope-cli.ts +0 -10
  500. package/scripts/release/release-scope-main.ts +12 -0
  501. package/scripts/repo/repo-setting.ts +3 -6
  502. package/scripts/repo/required-checks-logic.ts +6 -15
  503. package/scripts/report/report-format-reference.ts +2 -0
  504. package/scripts/report/report-labels.ts +155 -0
  505. package/scripts/report/report-lint-cli.ts +3 -0
  506. package/scripts/report/report-lint.ts +85 -110
  507. package/scripts/report/report-template-cli.ts +59 -0
  508. package/scripts/report/report-template.ts +112 -0
  509. package/scripts/review/acceptance-map.ts +1 -0
  510. package/scripts/review/removed-tests-check.ts +86 -0
  511. package/scripts/review/removed-tests.ts +149 -0
  512. package/scripts/review/review-brief-cli.ts +81 -15
  513. package/scripts/review/review-brief-main.ts +12 -0
  514. package/scripts/review/review-brief.ts +31 -3
  515. package/scripts/review/review-code-tree.ts +10 -6
  516. package/scripts/review/review-comment-round.ts +26 -8
  517. package/scripts/review/review-finding-ledger.ts +30 -15
  518. package/scripts/review/review-interaction.ts +94 -0
  519. package/scripts/review/review-readers-scan.ts +163 -0
  520. package/scripts/review/review-record-cli.ts +69 -30
  521. package/scripts/review/review-record-main.ts +12 -0
  522. package/scripts/review/review-round-one-blobs.ts +221 -0
  523. package/scripts/review/review-round2-cli.ts +49 -12
  524. package/scripts/review/review-round2.ts +80 -12
  525. package/scripts/review/review-stamps.ts +19 -1
  526. package/scripts/review/review-stop-log.ts +96 -0
  527. package/scripts/review/review-stops-cli.ts +85 -0
  528. package/scripts/review/review-tree.ts +6 -17
  529. package/scripts/review/review-untouched-readers.ts +146 -0
  530. package/scripts/review/test-titles.ts +118 -0
  531. package/scripts/rules/agent-model.ts +8 -0
  532. package/scripts/rules/bash-triggers.ts +46 -3
  533. package/scripts/rules/decision-oracle-batch.ts +9 -1
  534. package/scripts/rules/decision-oracle.ts +2 -2
  535. package/scripts/rules/delivered-rules.ts +80 -2
  536. package/scripts/rules/destructive-command.ts +8 -1
  537. package/scripts/rules/direct-filing.ts +10 -2
  538. package/scripts/rules/direct-issue-comment.ts +117 -0
  539. package/scripts/rules/direct-pr-create.ts +69 -3
  540. package/scripts/rules/early-heartbeat.ts +2 -1
  541. package/scripts/rules/file-body.ts +7 -0
  542. package/scripts/rules/filing-cap.ts +7 -0
  543. package/scripts/rules/git-argv.ts +72 -14
  544. package/scripts/rules/git-force.ts +12 -2
  545. package/scripts/rules/guard-remedy.ts +4 -0
  546. package/scripts/rules/implementation-cut-verdict.ts +1 -1
  547. package/scripts/rules/implementation-cut.ts +11 -3
  548. package/scripts/rules/index-guard.ts +8 -1
  549. package/scripts/rules/issue-comments.ts +9 -1
  550. package/scripts/rules/josh-git-bare.ts +7 -0
  551. package/scripts/rules/lane-background.ts +67 -25
  552. package/scripts/rules/lane-carry-conflict.ts +6 -0
  553. package/scripts/rules/lane-direct-tool.ts +74 -0
  554. package/scripts/rules/lane-interactive-ask.ts +9 -2
  555. package/scripts/rules/lane-park.ts +45 -23
  556. package/scripts/rules/lane-split-park.ts +10 -2
  557. package/scripts/rules/lane-switch-main.ts +7 -0
  558. package/scripts/rules/oracle-consulted.ts +10 -0
  559. package/scripts/rules/oracle-firing.ts +1 -0
  560. package/scripts/rules/permission-guards.ts +3 -0
  561. package/scripts/rules/piped-verification.ts +1 -1
  562. package/scripts/rules/poll-loop.ts +7 -0
  563. package/scripts/rules/pr-merge.ts +123 -0
  564. package/scripts/rules/protected-files.ts +32 -5
  565. package/scripts/rules/raw-field-body.ts +6 -0
  566. package/scripts/rules/rule-body-guard.ts +8 -0
  567. package/scripts/rules/rule-list.ts +9 -28
  568. package/scripts/rules/rule-value-cache.ts +4 -9
  569. package/scripts/rules/rule-value-cli.ts +51 -32
  570. package/scripts/rules/rule-value.ts +1 -1
  571. package/scripts/rules/shell-segments.ts +35 -0
  572. package/scripts/rules/stop-rule-rows.ts +64 -0
  573. package/scripts/rules/stop-rules.ts +3 -4
  574. package/scripts/rules/test-declared-commit.ts +11 -1
  575. package/scripts/rules/third-party-write.ts +7 -0
  576. package/scripts/rules/timed-rerun.ts +69 -0
  577. package/scripts/rules/worktree-guard.ts +13 -3
  578. package/scripts/run/board/run-board-app-name.ts +23 -0
  579. package/scripts/run/board/run-board-cli.ts +65 -32
  580. package/scripts/run/board/run-board-earlier.ts +94 -0
  581. package/scripts/run/board/run-board-ending-reason.ts +67 -0
  582. package/scripts/run/board/run-board-estimate-schedule.ts +103 -0
  583. package/scripts/run/board/run-board-estimate.ts +98 -0
  584. package/scripts/run/board/run-board-fit.ts +153 -36
  585. package/scripts/run/board/run-board-fold.ts +123 -0
  586. package/scripts/run/board/run-board-fresh.ts +2 -2
  587. package/scripts/run/board/run-board-gather-live.ts +29 -0
  588. package/scripts/run/board/run-board-gather-thread.ts +189 -0
  589. package/scripts/run/board/run-board-gather-worker.ts +29 -0
  590. package/scripts/run/board/run-board-gather.ts +187 -0
  591. package/scripts/run/board/run-board-github.ts +118 -38
  592. package/scripts/run/board/run-board-header.ts +82 -20
  593. package/scripts/run/board/run-board-keys.ts +58 -0
  594. package/scripts/run/board/run-board-kind.ts +20 -6
  595. package/scripts/run/board/run-board-labels.ts +116 -71
  596. package/scripts/run/board/run-board-lane-state.ts +109 -0
  597. package/scripts/run/board/run-board-lane-time.ts +122 -0
  598. package/scripts/run/board/run-board-layout.ts +239 -60
  599. package/scripts/run/board/run-board-length.ts +52 -0
  600. package/scripts/run/board/run-board-machine.ts +8 -4
  601. package/scripts/run/board/run-board-main.ts +12 -0
  602. package/scripts/run/board/run-board-notes.ts +52 -31
  603. package/scripts/run/board/run-board-now.ts +1 -1
  604. package/scripts/run/board/run-board-operation.ts +15 -33
  605. package/scripts/run/board/run-board-phase.ts +1 -1
  606. package/scripts/run/board/run-board-plan-shared.ts +115 -0
  607. package/scripts/run/board/run-board-plan.ts +55 -3
  608. package/scripts/run/board/run-board-planning.ts +45 -0
  609. package/scripts/run/board/run-board-priority.ts +44 -0
  610. package/scripts/run/board/run-board-process-groups.ts +195 -0
  611. package/scripts/run/board/run-board-read.ts +151 -27
  612. package/scripts/run/board/run-board-reason.ts +235 -51
  613. package/scripts/run/board/run-board-released.ts +35 -0
  614. package/scripts/run/board/run-board-render-checks.ts +39 -5
  615. package/scripts/run/board/run-board-render-condition.ts +96 -0
  616. package/scripts/run/board/run-board-render-drawn.ts +67 -0
  617. package/scripts/run/board/run-board-render-foot.ts +59 -0
  618. package/scripts/run/board/run-board-render-icon.ts +35 -0
  619. package/scripts/run/board/run-board-render-lanes.ts +145 -0
  620. package/scripts/run/board/run-board-render-lead.ts +80 -0
  621. package/scripts/run/board/run-board-render-legend.ts +117 -48
  622. package/scripts/run/board/run-board-render-notes.ts +156 -29
  623. package/scripts/run/board/run-board-render-processes.ts +196 -0
  624. package/scripts/run/board/run-board-render-section.ts +119 -0
  625. package/scripts/run/board/run-board-render-slot.ts +3 -36
  626. package/scripts/run/board/run-board-render-tail.ts +115 -48
  627. package/scripts/run/board/run-board-render-time.ts +56 -0
  628. package/scripts/run/board/run-board-render-title.ts +117 -24
  629. package/scripts/run/board/run-board-render-waits.ts +104 -20
  630. package/scripts/run/board/run-board-render.ts +207 -265
  631. package/scripts/run/board/run-board-roots.ts +54 -0
  632. package/scripts/run/board/run-board-row-yield.ts +9 -4
  633. package/scripts/run/board/run-board-setup.ts +1 -12
  634. package/scripts/run/board/run-board-snapshot.ts +111 -0
  635. package/scripts/run/board/run-board-spin.ts +2 -2
  636. package/scripts/run/board/run-board-state.ts +22 -33
  637. package/scripts/run/board/run-board-status.ts +147 -71
  638. package/scripts/run/board/run-board-thanks-read.ts +2 -2
  639. package/scripts/run/board/run-board-tick.ts +122 -107
  640. package/scripts/run/board/run-board-usage-board.ts +78 -0
  641. package/scripts/run/board/run-board-usage-lanes.ts +113 -0
  642. package/scripts/run/board/run-board-usage-listing.ts +93 -0
  643. package/scripts/run/board/run-board-usage-read.ts +120 -150
  644. package/scripts/run/board/run-board-usage-rusage.ts +19 -7
  645. package/scripts/run/board/run-board-usage-text.ts +139 -51
  646. package/scripts/run/board/run-board-usage-top.ts +102 -0
  647. package/scripts/run/board/run-board-usage.ts +89 -72
  648. package/scripts/run/board/run-board-usual.ts +20 -9
  649. package/scripts/run/carry/run-carry-args.ts +4 -7
  650. package/scripts/run/carry/run-carry-conversation.ts +1 -1
  651. package/scripts/run/carry/run-carry.ts +9 -4
  652. package/scripts/run/cut/run-cut-handoff.ts +2 -2
  653. package/scripts/run/event/run-event-cli.ts +1 -5
  654. package/scripts/run/event/run-event-filed.ts +12 -6
  655. package/scripts/run/event/run-event-follow.ts +2 -3
  656. package/scripts/run/event/run-event-render.ts +6 -10
  657. package/scripts/run/event/run-event-scope.ts +2 -2
  658. package/scripts/run/event/run-event-stream-emit.ts +82 -22
  659. package/scripts/run/event/run-event-stream.ts +34 -8
  660. package/scripts/run/hold/run-hold-adopt.ts +65 -0
  661. package/scripts/run/hold/run-hold-cli.ts +93 -22
  662. package/scripts/run/hold/run-hold-main.ts +12 -0
  663. package/scripts/run/hold/run-hold-member.ts +37 -0
  664. package/scripts/run/hold/run-stop-notify.ts +183 -0
  665. package/scripts/run/merge/run-merge-auth.ts +31 -0
  666. package/scripts/run/merge/run-merge-cli.ts +62 -48
  667. package/scripts/run/merge/run-merge-close.ts +74 -0
  668. package/scripts/run/merge/run-merge-collect.ts +6 -13
  669. package/scripts/run/merge/run-merge-driver.ts +71 -0
  670. package/scripts/run/merge/run-merge-main.ts +12 -0
  671. package/scripts/run/merge/run-merge-outcome.ts +23 -0
  672. package/scripts/run/merge/run-merge-restart.ts +39 -0
  673. package/scripts/run/merge/run-merge-ship.ts +20 -4
  674. package/scripts/run/merge/run-merge-signals.ts +33 -9
  675. package/scripts/run/merge/run-merge-steps.ts +120 -85
  676. package/scripts/run/merge/run-merge.ts +38 -37
  677. package/scripts/run/park/run-park-cli.ts +91 -0
  678. package/scripts/run/park/run-park.ts +88 -0
  679. package/scripts/run/park/run-stalled.ts +68 -0
  680. package/scripts/run/pause/run-pause-cli.ts +73 -0
  681. package/scripts/run/pause/run-pause.ts +251 -0
  682. package/scripts/run/progress/run-progress-cli.ts +4 -12
  683. package/scripts/run/progress/run-progress-clock.ts +1 -1
  684. package/scripts/run/progress/run-progress-config.ts +6 -14
  685. package/scripts/run/progress/run-progress-main.ts +12 -0
  686. package/scripts/run/progress/run-progress.ts +1 -4
  687. package/scripts/run/review/run-review.ts +2 -1
  688. package/scripts/run/run-ending.ts +1 -0
  689. package/scripts/run/run-invocation.ts +5 -6
  690. package/scripts/run/run-liveness-cli.ts +6 -8
  691. package/scripts/run/run-liveness.ts +60 -29
  692. package/scripts/run/run-next.ts +1 -1
  693. package/scripts/run/run-parent-cut-hook.ts +2 -2
  694. package/scripts/run/run-preflight.ts +18 -3
  695. package/scripts/run/run-prep-cli.ts +1 -10
  696. package/scripts/run/run-prep-locate.ts +7 -50
  697. package/scripts/run/run-prep-main.ts +12 -0
  698. package/scripts/run/run-prep.ts +6 -0
  699. package/scripts/run/run-report-cli.ts +101 -13
  700. package/scripts/run/run-report-handoff.ts +114 -0
  701. package/scripts/run/run-step.ts +1 -1
  702. package/scripts/run/run-stranded-detect.ts +13 -0
  703. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  704. package/scripts/run/run-watcher-guard.ts +1 -1
  705. package/scripts/run/ship/run-ship-args.ts +121 -0
  706. package/scripts/run/ship/run-ship-body-draft.ts +110 -0
  707. package/scripts/run/ship/run-ship-cli.ts +63 -140
  708. package/scripts/run/ship/run-ship-detach.ts +19 -8
  709. package/scripts/run/ship/run-ship-infra-retry.ts +59 -0
  710. package/scripts/run/ship/run-ship-next.ts +18 -2
  711. package/scripts/run/ship/run-ship-overlap.ts +42 -5
  712. package/scripts/run/ship/run-ship-place.ts +96 -0
  713. package/scripts/run/ship/run-ship-pre-detach-timed.ts +43 -0
  714. package/scripts/run/ship/run-ship-pre-detach.ts +81 -24
  715. package/scripts/run/ship/run-ship-preflight.ts +137 -32
  716. package/scripts/run/ship/run-ship-probe.ts +11 -3
  717. package/scripts/run/ship/run-ship-relaunch-cap.ts +70 -0
  718. package/scripts/run/ship/run-ship-removed-rows.ts +61 -0
  719. package/scripts/run/ship/run-ship-return.ts +107 -23
  720. package/scripts/run/ship/run-ship-review-steps.ts +105 -151
  721. package/scripts/run/ship/run-ship-review.ts +73 -6
  722. package/scripts/run/ship/run-ship-reviewer.ts +207 -0
  723. package/scripts/run/ship/run-ship-stage.ts +55 -6
  724. package/scripts/run/ship/run-ship-steps.ts +46 -15
  725. package/scripts/run/ship/run-ship-stop-cause.ts +135 -0
  726. package/scripts/run/ship/run-ship-stop-text.ts +11 -6
  727. package/scripts/run/ship/run-ship-sync.ts +43 -6
  728. package/scripts/run/ship/run-ship.ts +30 -2
  729. package/scripts/run/tidy/run-tidy-cli.ts +0 -4
  730. package/scripts/run/tidy/run-tidy-lanes.ts +3 -0
  731. package/scripts/run/tidy/run-tidy-main.ts +8 -0
  732. package/scripts/run/wake/run-wake-cli.ts +119 -100
  733. package/scripts/run/wake/run-wake-code.ts +83 -0
  734. package/scripts/run/wake/run-wake-driver.ts +19 -8
  735. package/scripts/run/wake/run-wake-loop.ts +34 -3
  736. package/scripts/run/wake/run-wake-main.ts +13 -0
  737. package/scripts/run/wake/run-wake-ports.ts +90 -0
  738. package/scripts/run/wake/run-wake-takeover.ts +66 -0
  739. package/scripts/run/wake/run-wake.ts +2 -1
  740. package/scripts/safe-chain/pack-hook.ts +2 -2
  741. package/scripts/safe-chain/preinstall-command.ts +2 -1
  742. package/scripts/safe-chain/project-config.ts +3 -9
  743. package/scripts/security/security-audit-main.ts +8 -0
  744. package/scripts/security/security-audit-provision-logic.ts +0 -11
  745. package/scripts/security/security-audit-provision-main.ts +12 -0
  746. package/scripts/security/security-audit-provision.ts +11 -26
  747. package/scripts/security/security-audit.ts +6 -7
  748. package/scripts/self-sync-guard/self-sync-guard-logic.ts +6 -13
  749. package/scripts/split/split-assess-cli.ts +2 -2
  750. package/scripts/split/split-assess.ts +15 -0
  751. package/scripts/split/split-measure.ts +65 -0
  752. package/scripts/sync/claude-settings-merge.ts +8 -15
  753. package/scripts/sync/directory-copy-guard.ts +4 -9
  754. package/scripts/sync/file-content.ts +78 -8
  755. package/scripts/sync/sync-ai-files.ts +3 -5
  756. package/scripts/sync/sync-configs.ts +11 -23
  757. package/scripts/sync/sync-hook-safety.ts +4 -10
  758. package/scripts/sync/sync-main.ts +11 -0
  759. package/scripts/sync/sync.ts +25 -37
  760. package/scripts/test/test-declared-changed.ts +2 -2
  761. package/scripts/test/test-declared.ts +1 -1
  762. package/scripts/test/test-e2e-guard.ts +25 -1
  763. package/scripts/test/test-e2e-stamp.ts +38 -0
  764. package/scripts/test/test-pinned-logic.ts +145 -0
  765. package/scripts/test/test-pinned-main.ts +78 -0
  766. package/scripts/test/test-pinned.ts +100 -0
  767. package/scripts/test/test-read-map.ts +264 -0
  768. package/scripts/test/test-read-recording.ts +91 -0
  769. package/scripts/test/test-read-trace.ts +148 -0
  770. package/scripts/test/test-red-main.ts +12 -0
  771. package/scripts/test/test-red.ts +12 -30
  772. package/scripts/test/test-related-main.ts +18 -0
  773. package/scripts/test/test-related-record.ts +119 -0
  774. package/scripts/test/test-related-scope.ts +229 -43
  775. package/scripts/test/test-related.ts +300 -22
  776. package/scripts/test/test-unit-guard-main.ts +15 -0
  777. package/scripts/test/test-unit-guard.ts +42 -24
  778. package/scripts/test/unit-count-file.ts +37 -0
  779. package/scripts/test/unit-run-marker.ts +162 -0
  780. package/scripts/test/unit-setup-files.ts +51 -0
  781. package/scripts/test/unit-worker-share.ts +61 -149
  782. package/scripts/test/whole-suite-files.ts +63 -0
  783. package/scripts/time-runtime/time-background.ts +20 -2
  784. package/scripts/time-runtime/time-bundles.ts +4 -3
  785. package/scripts/time-runtime/time-density-hook.ts +8 -6
  786. package/scripts/time-runtime/time-followup-stage.ts +1 -3
  787. package/scripts/time-runtime/time-format.ts +5 -28
  788. package/scripts/time-runtime/time-markers.ts +10 -1
  789. package/scripts/time-runtime/time-shell.ts +97 -10
  790. package/scripts/time-runtime/time-transcript-line.ts +49 -62
  791. package/scripts/uninstall/uninstall.ts +1 -1
  792. package/scripts/version/bump-version-main.ts +12 -0
  793. package/scripts/version/bump-version.ts +25 -25
  794. package/scripts/version/effective-upstream.ts +6 -12
  795. package/scripts/version/latest-corepack.ts +17 -32
  796. package/scripts/version/latest-if-required-cli.ts +93 -0
  797. package/scripts/version/latest-lane-guard-main.ts +8 -0
  798. package/scripts/version/latest-lane-guard.ts +0 -4
  799. package/scripts/version/latest-scope-cli.ts +0 -9
  800. package/scripts/version/latest-scope-main.ts +12 -0
  801. package/scripts/version/latest-update.ts +5 -5
  802. package/scripts/version/npm-registry.ts +44 -6
  803. package/scripts/version/publishable-range-check.ts +5 -16
  804. package/scripts/version/release-age.ts +1 -1
  805. package/scripts/version/release-hold.ts +1 -1
  806. package/scripts/version/release-window.ts +38 -0
  807. package/scripts/version/running-binary.ts +1 -1
  808. package/scripts/version/safe-chain-version-update.ts +3 -8
  809. package/scripts/version/version-targets.ts +6 -28
  810. package/templates/dependabot.yml +29 -0
  811. package/templates/gitignore +4 -1
  812. package/templates/pnpm-workspace.basic.yaml +1 -1
  813. package/templates/sonar-project.properties +3 -2
  814. package/templates/workflows/ci.yml +12 -4
  815. package/templates/workflows/github-release.yml +4 -0
  816. package/tsconfig.sonar.json +1 -1
  817. package/.ncurc.json +0 -3
  818. package/dist/hooks/batch-guard.js +0 -29
  819. package/dist/hooks/chunk-4KKD2UTK.js +0 -66
  820. package/dist/hooks/chunk-6TS2JD6D.js +0 -237
  821. package/dist/hooks/chunk-7DSBJVFH.js +0 -50
  822. package/dist/hooks/chunk-A65Y4YOR.js +0 -79
  823. package/dist/hooks/chunk-CDY4NFQG.js +0 -15
  824. package/dist/hooks/chunk-EFXE6C37.js +0 -81
  825. package/dist/hooks/chunk-GP4IEC4O.js +0 -20
  826. package/dist/hooks/chunk-JP45SOCJ.js +0 -56
  827. package/dist/hooks/chunk-LAOV7NLM.js +0 -45
  828. package/dist/hooks/chunk-M4HGZV7U.js +0 -83
  829. package/dist/hooks/chunk-PF2NZL2A.js +0 -131
  830. package/dist/hooks/chunk-PTICX4OL.js +0 -358
  831. package/dist/hooks/chunk-RT27QRJW.js +0 -6247
  832. package/dist/hooks/chunk-SYOTDYCI.js +0 -3533
  833. package/dist/hooks/chunk-UPUZF3AN.js +0 -279
  834. package/dist/hooks/chunk-WK6SMBIC.js +0 -3180
  835. package/dist/hooks/duplicate-read-guard.js +0 -30
  836. package/dist/hooks/investigation-guard.js +0 -29
  837. package/scripts/backlog/backlog-stalled-cli.ts +0 -30
  838. package/scripts/cost-runtime/cost-format.ts +0 -29
  839. package/scripts/init/basic-path-migration.ts +0 -24
  840. package/scripts/issue/issue-backlinks-cli.ts +0 -93
  841. package/scripts/josh/josh-commands-guard.ts +0 -38
  842. package/scripts/josh/josh-usage-automation.ts +0 -121
  843. package/scripts/josh/josh-usage-backlog.ts +0 -180
  844. package/scripts/josh/josh-usage-developer.ts +0 -230
  845. package/scripts/josh/josh-usage-hooks.ts +0 -38
  846. package/scripts/josh/josh-usage-issue.ts +0 -205
  847. package/scripts/josh/josh-usage-review.ts +0 -101
  848. package/scripts/josh/josh-usage-run.ts +0 -252
  849. package/scripts/josh/josh-usage-session.ts +0 -238
  850. package/scripts/josh/josh-usage-workflow.ts +0 -212
  851. package/scripts/lint/cross-file-recheck.ts +0 -155
  852. package/scripts/registry-migration/migrate-logic.ts +0 -237
  853. package/scripts/registry-migration/migrate.ts +0 -292
  854. package/scripts/registry-migration/user-npmrc.ts +0 -15
  855. package/scripts/rules/rule-guard.ts +0 -23
  856. package/scripts/rules/rule-registry-rows.ts +0 -292
  857. package/scripts/rules/rule-registry.ts +0 -112
  858. package/scripts/run/run-stranded-cli.ts +0 -27
@@ -1,5 +1,10 @@
1
1
  import { git_gh_command } from '#scripts/gh/git-gh-command'
2
- import { epic_child_schema, epic_issue_schema, type EpicChildData } from '#scripts/git/git-schemas'
2
+ import {
3
+ epic_child_schema,
4
+ epic_issue_schema,
5
+ type EpicChildData,
6
+ type EpicIssueData,
7
+ } from '#scripts/git/git-schemas'
3
8
  import { cutoff_cause, cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
4
9
  import {
5
10
  parse_json_array_or_undefined,
@@ -37,6 +42,7 @@ interface EpicIssue {
37
42
  children: Array<number>
38
43
  external_children: Array<ExternalChild>
39
44
  has_declared_order: boolean
45
+ is_reopened: boolean
40
46
  }
41
47
 
42
48
  interface SiblingState {
@@ -44,8 +50,12 @@ interface SiblingState {
44
50
  has_blocked_by: boolean
45
51
  }
46
52
 
47
- function to_epic_issue(raw: { number: number; body?: string | undefined }): EpicIssue {
53
+ // GitHub's `state_reason` for an open issue that was closed once and reopened.
54
+ const REOPENED = 'reopened'
55
+
56
+ function to_epic_issue(raw: EpicIssueData): EpicIssue {
48
57
  return {
58
+ is_reopened: raw.stateReason === REOPENED,
49
59
  number: raw.number,
50
60
  children: epic_parse.parse_task_list_issue_numbers(raw.body),
51
61
  external_children: epic_parse.parse_external_task_list_children(raw.body),
@@ -98,30 +108,32 @@ function to_sibling_state(raw_json: string | undefined): SiblingState {
98
108
  }
99
109
  }
100
110
 
101
- async function inspect_sibling(sibling: number, repo?: string): Promise<SiblingState> {
102
- return to_sibling_state(await git_gh_command.issue_get_state_and_relations(String(sibling), repo))
103
- }
111
+ type ChildRead = SiblingState & { is_readable: boolean }
104
112
 
105
- // A cross-repository child, read by naming its repository in the read's REST path. `is_readable`
106
- // is what decides whether the epic may close at all: closing while a child's state is unknown is
107
- // exactly what the old refusal prevented, and reading them is the only thing that changed.
108
- async function inspect_external(
109
- child: ExternalChild,
110
- ): Promise<SiblingState & { is_readable: boolean }> {
113
+ // A same-repository child carries no `repo`; a cross-repository one names it.
114
+ type ChildReference = Omit<ExternalChild, 'repo'> & { repo?: string | undefined }
115
+
116
+ // A child read by number, naming its repository in the REST path when it lives in another one.
117
+ // `is_readable` is what decides whether the epic may close at all: closing while a child's state is
118
+ // unknown is exactly what the refusal prevents. A failed read is told apart from an open child
119
+ // rather than read as one, which left a finished epic open with nothing in the output (#4560). A
120
+ // read whose answer carries no state is a failed read too.
121
+ async function inspect_child(child: ChildReference): Promise<ChildRead> {
111
122
  const raw = await git_gh_command.issue_get_state_and_relations(String(child.number), child.repo)
112
123
 
113
- return { ...to_sibling_state(raw), is_readable: raw !== undefined }
124
+ return { ...to_sibling_state(raw), is_readable: parse_child(raw)?.state !== undefined }
114
125
  }
115
126
 
116
127
  // The merged Issue is excluded rather than queried: GitHub applies `closes #N` asynchronously, so
117
128
  // reading its state immediately after the merge races with that propagation.
118
- async function inspect_siblings(
119
- epic: EpicIssue,
120
- merged_number: number,
121
- ): Promise<Array<SiblingState>> {
122
- const siblings = epic.children.filter((child) => child !== merged_number)
129
+ async function inspect_children(epic: EpicIssue, merged_number: number): Promise<Array<ChildRead>> {
130
+ const siblings: Array<ChildReference> = epic.children
131
+ .filter((child) => child !== merged_number)
132
+ .map((number) => ({ number }))
123
133
 
124
- return await Promise.all(siblings.map(async (sibling) => await inspect_sibling(sibling)))
134
+ return await Promise.all(
135
+ [...siblings, ...epic.external_children].map(async (child) => await inspect_child(child)),
136
+ )
125
137
  }
126
138
 
127
139
  // `pnpm josh epic --ordered` and `--add` record the declared chain themselves, through the REST
@@ -165,9 +177,8 @@ const CASCADE_DEPTH_NOTE = `⚠️ Epic auto-close cascade reached its ${String
165
177
 
166
178
  // **An unreadable comment listing does not stop the close.** It is announced and closed exactly as
167
179
  // it was before the duplicate check existed, because refusing here would strand the epic: nothing
168
- // re-triggers the auto-close once every child is closed — `resolve_and_close` only evaluates epics
169
- // holding the issue that just merged, and that merge has already happened. One rate-limited read
170
- // would leave a finished epic open until somebody noticed.
180
+ // re-triggers the auto-close until some later child close sweeps the open epics, and that close may
181
+ // never come. One rate-limited read would leave a finished epic open until somebody noticed.
171
182
  //
172
183
  // The direction is chosen on what each mistake costs, and they are not symmetric. Announcing twice
173
184
  // costs one redundant comment and nothing else, because the epic *closes* — which is what ends a loop
@@ -209,38 +220,26 @@ async function close_epic(epic: EpicIssue): Promise<boolean> {
209
220
  return await close_epic_with(epic_number, comment)
210
221
  }
211
222
 
212
- // The cross-repository children's states, and whether every one of them could be read. An epic with
213
- // a child whose state is unknown keeps the old handling — left open for manual closing.
214
- async function inspect_external_children(
215
- epic: EpicIssue,
216
- ): Promise<{ states: Array<SiblingState>; is_complete: boolean }> {
217
- if (epic.external_children.length === 0) return { states: [], is_complete: true }
218
- const results = await Promise.all(
219
- epic.external_children.map(async (child) => await inspect_external(child)),
220
- )
223
+ // The manual-close request is made only when every child that was read is closed: while one is still
224
+ // open the batch is unfinished anyway, and asking for a manual close would invite closing it early.
225
+ function warn_when_unread_blocks_close(epic: EpicIssue, states: ReadonlyArray<ChildRead>): void {
226
+ if (states.some((state) => state.is_readable && !state.is_closed)) return
221
227
 
222
- return {
223
- states: results.map((result) => ({
224
- is_closed: result.is_closed,
225
- has_blocked_by: result.has_blocked_by,
226
- })),
227
- is_complete: results.every((result) => result.is_readable),
228
- }
228
+ console.info(
229
+ `⚠️ Epic ${session_cite.issue(epic.number)} has a child whose state could not be read; ${CLOSE_MANUALLY}`,
230
+ )
229
231
  }
230
232
 
233
+ // An epic with a child whose state is unknown is left open for manual closing, and says so.
231
234
  async function close_epic_when_complete(epic: EpicIssue, merged_number: number): Promise<boolean> {
232
- const external = await inspect_external_children(epic)
235
+ const states = await inspect_children(epic, merged_number)
233
236
 
234
- if (!external.is_complete) {
235
- console.info(
236
- `ℹ️ Epic ${session_cite.issue(epic.number)} has a child in another repository whose state could not be read; ${CLOSE_MANUALLY}`,
237
- )
237
+ if (states.some((state) => !state.is_readable)) {
238
+ warn_when_unread_blocks_close(epic, states)
238
239
 
239
240
  return false
240
241
  }
241
242
 
242
- const states = [...(await inspect_siblings(epic, merged_number)), ...external.states]
243
-
244
243
  warn_when_order_unrecorded(epic, states)
245
244
 
246
245
  if (states.some((state) => !state.is_closed)) return false
@@ -315,22 +314,74 @@ async function close_next_level(
315
314
  return next
316
315
  }
317
316
 
318
- // Breadth-first up the nesting: level 0 closes the epics holding the merged issue, and each closed
319
- // epic becomes the next level's completed child, so a fully-finished parent closes in the same pass.
320
- async function cascade_close(
317
+ // Breadth-first up the nesting: each closed epic becomes the next level's completed child, so a
318
+ // fully-finished parent closes in the same pass. `false` when the depth cap stopped the walk.
319
+ async function cascade_from(
321
320
  open_epics: ReadonlyArray<EpicIssue>,
322
- merged_number: number,
323
- ): Promise<void> {
324
- const evaluated = new Set<number>()
325
- let frontier: ReadonlyArray<number> = [merged_number]
321
+ start: ReadonlyArray<number>,
322
+ evaluated: Set<number>,
323
+ ): Promise<boolean> {
324
+ let frontier = start
326
325
 
327
326
  for (let depth = 0; depth < MAX_CASCADE_DEPTH; depth++) {
328
- if (frontier.length === 0) return
327
+ if (frontier.length === 0) return true
329
328
  // eslint-disable-next-line no-await-in-loop -- the cascade closes one level before reading the next
330
329
  frontier = await close_next_level(open_epics, frontier, evaluated)
331
330
  }
332
331
 
333
332
  if (frontier.length > 0) console.info(CASCADE_DEPTH_NOTE)
333
+
334
+ return frontier.length === 0
335
+ }
336
+
337
+ // An epic listing no child has nothing to be complete: with no state to read, the completion check
338
+ // would close it — a prose epic, or one whose task list is not written yet.
339
+ function tracks_children(epic: EpicIssue): boolean {
340
+ return epic.children.length + epic.external_children.length > 0
341
+ }
342
+
343
+ // Every open epic the cascade did not reach, closed when each child is: an epic whose last child
344
+ // closed outside every josh path — an `already-done` child `run:merge` parked and a person closed by
345
+ // hand (#4564) — is found by the next close of any child. An epic is marked evaluated only once it
346
+ // closes, so a parent read before its child epic closed here is evaluated again by that cascade.
347
+ //
348
+ // A reopened epic is left out: a person reopening a finished epic has said it is not done, and only
349
+ // a close of one of its own children — the cascade above — may close it again.
350
+ function is_sweep_target(epic: EpicIssue, evaluated: ReadonlySet<number>): boolean {
351
+ return !evaluated.has(epic.number) && !epic.is_reopened && tracks_children(epic)
352
+ }
353
+
354
+ async function sweep_unreached(
355
+ open_epics: ReadonlyArray<EpicIssue>,
356
+ merged_number: number,
357
+ evaluated: Set<number>,
358
+ ): Promise<Array<number>> {
359
+ const closed: Array<number> = []
360
+ const unreached = open_epics.filter((epic) => is_sweep_target(epic, evaluated))
361
+
362
+ for (const epic of unreached) {
363
+ // eslint-disable-next-line no-await-in-loop -- a closed epic joins the evaluated set the cascade reads
364
+ if (await close_epic_isolated(epic, merged_number)) closed.push(epic.number)
365
+ }
366
+
367
+ for (const number of closed) evaluated.add(number)
368
+
369
+ return closed
370
+ }
371
+
372
+ // Level 0 closes the epics holding the merged issue; the sweep then closes any other finished epic.
373
+ // A cascade stopped at the depth cap asks for a manual close, so the sweep does not run past it.
374
+ async function cascade_close(
375
+ open_epics: ReadonlyArray<EpicIssue>,
376
+ merged_number: number,
377
+ ): Promise<void> {
378
+ const evaluated = new Set<number>()
379
+
380
+ if (!(await cascade_from(open_epics, [merged_number], evaluated))) return
381
+
382
+ const swept = await sweep_unreached(open_epics, merged_number, evaluated)
383
+
384
+ await cascade_from(open_epics, swept, evaluated)
334
385
  }
335
386
 
336
387
  const UNREADABLE_EPIC_LIST_MESSAGE =
@@ -363,9 +414,15 @@ async function resolve_and_close_safely(merged_number: number): Promise<void> {
363
414
  /**
364
415
  * Close every open epic whose task list is fully complete once `issue_number` closed.
365
416
  *
366
- * Requires `is_merged`: the linked Issue is treated as closed without being queried, which only
367
- * holds once the PR actually merged. On a `--no-merge` run that Issue is still open, so skipping
368
- * this guard would close an epic whose batch is not finished.
417
+ * **The one entry every child close goes through** (#4560): `followup` after its merge,
418
+ * `josh issue:measured` after its own close, and `run:merge` for every child it counts as merged —
419
+ * which also catches a merge GitHub left OPEN. Running it
420
+ * twice for one child is harmless: a closed epic is no longer in the open listing. Every other open
421
+ * epic is swept too, so one whose last child closed outside these paths closes here (#4564).
422
+ *
423
+ * Requires `is_merged` — the child is known closed: the linked Issue is treated as closed without
424
+ * being queried. On a `--no-merge` run that Issue is still open, so skipping this guard would close
425
+ * an epic whose batch is not finished.
369
426
  */
370
427
  async function close_completed_epics(input: {
371
428
  issue_number: string | undefined
@@ -2,6 +2,7 @@ import { repo_map_logic } from '#scripts/discovery/repo-map-logic'
2
2
  import { repo_origin } from '#scripts/discovery/repo-origin'
3
3
  import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
4
4
  import { git_gh_exec } from '#scripts/gh/git-gh-exec'
5
+ import { json_value } from '#scripts/lib/json-value'
5
6
  import { GH_API_TIMEOUT_MS } from '#scripts/lib/timeouts'
6
7
  import { propagate_publish } from '#scripts/propagate/propagate-publish'
7
8
  import { derive_versions_endpoint } from '#scripts/version/version-command-config'
@@ -209,14 +210,10 @@ function to_answer(repo: string, fields: ManifestFields): ManifestAnswer {
209
210
  }
210
211
 
211
212
  function to_manifest(repo: string, stdout: string): ManifestAnswer {
212
- try {
213
- const parsed: unknown = JSON.parse(stdout.trim())
214
- if (typeof parsed !== 'object' || parsed === null) return { kind: 'unreadable' }
213
+ const parsed = json_value.parse_or_undefined(stdout.trim())
214
+ if (typeof parsed !== 'object' || parsed === null) return { kind: 'unreadable' }
215
215
 
216
- return to_answer(repo, parsed)
217
- } catch {
218
- return { kind: 'unreadable' }
219
- }
216
+ return to_answer(repo, parsed)
220
217
  }
221
218
 
222
219
  // Why the read failed, asked only when it did. The status line is the protocol rather than prose, so
@@ -0,0 +1,119 @@
1
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
2
+ import { NEEDS_DECISION_LABEL } from '#scripts/issue/issue-labels'
3
+ import { issue_read } from '#scripts/issue/issue-read'
4
+ import { session_cite } from '#scripts/issue/session-cite'
5
+ import { epic_decision } from './epic-decision'
6
+ import { epic_parse } from './epic-parse'
7
+ import { epic_read } from './epic-read'
8
+ import type { EpicSubject } from './epic-validate'
9
+
10
+ // `josh epic --decide <E> <N...> --decision-file <path|->` — record a decision about children the epic
11
+ // already tracks: the epic's `## Decisions` entry, a comment on each child, and each child's
12
+ // `needs-decision` label removed. The text is the caller's; placing it is this command's.
13
+ //
14
+ // **A rerun writes nothing twice.** The entry and each comment are looked for before they are
15
+ // written, so a run that stopped half way is finished by running it again.
16
+
17
+ const SUCCESS_EXIT_CODE = 0
18
+ const FAILURE_EXIT_CODE = 1
19
+
20
+ interface DecideInput {
21
+ epic_number: number
22
+ children: ReadonlyArray<number>
23
+ decision: string
24
+ }
25
+
26
+ function format_list(numbers: ReadonlyArray<number>): string {
27
+ return numbers.map((issue_number) => session_cite.issue(issue_number)).join(', ')
28
+ }
29
+
30
+ // A child the epic does not track is refused before anything is written: placing it is `--add`'s,
31
+ // which records the same decision in the same call.
32
+ function untracked_error(epic_number: number, untracked: ReadonlyArray<number>): string {
33
+ return `${format_list(untracked)} not tracked by ${session_cite.issue(epic_number)}; place it with \`pnpm josh epic --add ${String(epic_number)} <N> --decision-file <path>\`. Nothing was written.`
34
+ }
35
+
36
+ // The epic the decision goes to, or why nothing is written: a record `append_decision` would refuse
37
+ // (checked before any read), an unreadable epic, or a child it does not track.
38
+ async function read_decision_target(input: DecideInput): Promise<EpicSubject | { error: string }> {
39
+ const record_error = epic_decision.find_decision_error(input.decision)
40
+ if (record_error !== undefined) return { error: record_error }
41
+ const subject = await epic_read.read_subject(input.epic_number)
42
+
43
+ if (subject === undefined) {
44
+ return { error: `Could not read issue ${session_cite.issue(input.epic_number)}.` }
45
+ }
46
+
47
+ const tracked = epic_parse.parse_task_list_issue_numbers(subject.body)
48
+ const untracked = input.children.filter((child) => !tracked.includes(child))
49
+
50
+ return untracked.length === 0 ? subject : { error: untracked_error(input.epic_number, untracked) }
51
+ }
52
+
53
+ async function record_on_epic(subject: EpicSubject, decision: string): Promise<string> {
54
+ const epic = session_cite.issue(subject.number)
55
+
56
+ if (epic_decision.has_decision(subject.body, decision)) {
57
+ return `📝 ${epic} already records this decision.`
58
+ }
59
+
60
+ const body = epic_decision.append_decision(subject.body ?? '', decision)
61
+
62
+ await git_gh_command.issue_edit_body(String(subject.number), body)
63
+
64
+ return `📝 Decision recorded in ${epic}'s \`## Decisions\`.`
65
+ }
66
+
67
+ // `undefined` when the comments could not be read: posting then risks the duplicate this exists to
68
+ // prevent, so the child is reported as not recorded and a rerun finishes it.
69
+ async function has_comment(child: number, decision: string): Promise<boolean | undefined> {
70
+ const comments = await issue_read.parse_comments(
71
+ await git_gh_command.issue_list_comments(String(child)),
72
+ )
73
+
74
+ return comments?.some((comment) => comment.body.trim() === decision.trim())
75
+ }
76
+
77
+ // The label goes only once the child carries the record, so a failed comment leaves it parked.
78
+ async function record_on_child(child: number, decision: string): Promise<boolean> {
79
+ const has_record = await has_comment(child, decision)
80
+ if (has_record === undefined) return false
81
+ const is_commented =
82
+ has_record || (await git_gh_command.issue_try_comment(String(child), decision))
83
+ if (!is_commented) return false
84
+
85
+ return await git_gh_command.issue_clear_label(String(child), NEEDS_DECISION_LABEL)
86
+ }
87
+
88
+ function format_child_report(total: number, failures: number): string {
89
+ if (failures === 0) return epic_decision.format_decision_report({ total, failures })
90
+
91
+ return `⚠️ ${String(failures)} of ${String(total)} child issue(s) could not be given the comment or lose \`${NEEDS_DECISION_LABEL}\`; rerun the same command — nothing is written twice.`
92
+ }
93
+
94
+ async function decide(input: DecideInput): Promise<number> {
95
+ const epic = await read_decision_target(input)
96
+
97
+ if ('error' in epic) {
98
+ console.error(`✖ ${epic.error}`)
99
+
100
+ return FAILURE_EXIT_CODE
101
+ }
102
+
103
+ console.info(await record_on_epic(epic, input.decision))
104
+ const recorded = await Promise.all(
105
+ input.children.map(async (child) => await record_on_child(child, input.decision)),
106
+ )
107
+ const failures = recorded.filter((is_recorded) => !is_recorded).length
108
+
109
+ console.info(format_child_report(input.children.length, failures))
110
+
111
+ return failures === 0 ? SUCCESS_EXIT_CODE : FAILURE_EXIT_CODE
112
+ }
113
+
114
+ const epic_decide = {
115
+ decide,
116
+ }
117
+
118
+ export { epic_decide }
119
+ export type { DecideInput }
@@ -153,8 +153,17 @@ function read_recorded_reasons(body: string | undefined): string {
153
153
  ].join('\n')
154
154
  }
155
155
 
156
+ // Whether `## Decisions` already carries this record, compared as `append_decision` writes it — so a
157
+ // rerun of `epic --decide` after a partial failure appends nothing a second time.
158
+ function has_decision(body: string | undefined, record: string): boolean {
159
+ if (body === undefined) return false
160
+
161
+ return read_section(body, DECISIONS_HEADING_PATTERN).includes(record.trimEnd())
162
+ }
163
+
156
164
  const epic_decision = {
157
165
  append_decision,
166
+ has_decision,
158
167
  append_replacements,
159
168
  read_recorded_reasons,
160
169
  find_decision_error,
@@ -1,5 +1,6 @@
1
1
  import { git_gh_command } from '#scripts/gh/git-gh-command'
2
2
  import type { IssueReadFailure } from '#scripts/gh/git-gh-issue-read'
3
+ import { issue_sample_gate } from '#scripts/issue/issue-sample-gate'
3
4
  import { epic_cross_repo } from './epic-cross-repo'
4
5
  import type { EpicChild, IssueReference } from './epic-graph'
5
6
  import { epic_issue, type EpicIssue } from './epic-issue'
@@ -21,6 +22,7 @@ function to_child(parsed: EpicIssue, repo: string): EpicChild {
21
22
  state: epic_issue.normalize_state(parsed.state),
22
23
  labels: epic_issue.label_names(parsed),
23
24
  blocked_by: epic_issue.blocker_references_of(parsed, repo),
25
+ body: parsed.body,
24
26
  }
25
27
  }
26
28
 
@@ -132,6 +134,8 @@ async function fetch_child_read(
132
134
 
133
135
  const parsed = epic_issue.parse_epic_issue(read.json)
134
136
 
137
+ await issue_sample_gate.prime([parsed?.body])
138
+
135
139
  return { child: parsed === undefined ? undefined : to_child(parsed, repo), is_unreachable: false }
136
140
  }
137
141
 
@@ -29,6 +29,8 @@ interface EpicChild {
29
29
  state: 'OPEN' | 'CLOSED'
30
30
  labels: ReadonlyArray<string>
31
31
  blocked_by: ReadonlyArray<IssueReference>
32
+ // Read only to hold a child until its declared start time; absent on rows built without a read.
33
+ body?: string | undefined
32
34
  }
33
35
 
34
36
  // What is wrong with the graph. Both stop the run — neither is something to pick a winner for.
@@ -2,6 +2,7 @@ import { git_gh_issue_rest } from '#scripts/gh/git-gh-issue-rest'
2
2
  import { blocked_by_schema } from '#scripts/git/git-schemas'
3
3
  import { parse_json_object_or_undefined } from '#scripts/git/parse-json-array'
4
4
  import { z } from 'zod'
5
+ import { REPOSITORY_SEGMENT_SOURCE } from './epic-parse'
5
6
  import type { IssueReference } from './epic-reference'
6
7
 
7
8
  // The shapes one issue is read as, and the argument every epic command takes.
@@ -116,7 +117,10 @@ interface EpicReference {
116
117
  number: number
117
118
  }
118
119
 
119
- const QUALIFIED_REFERENCE = /^([\w.-]+\/[\w.-]+)#(\d+)$/u
120
+ const QUALIFIED_REFERENCE = new RegExp(
121
+ String.raw`^(${REPOSITORY_SEGMENT_SOURCE}\/${REPOSITORY_SEGMENT_SOURCE})#(\d+)$`,
122
+ 'u',
123
+ )
120
124
  const QUALIFIED_REPO_GROUP = 1
121
125
  const QUALIFIED_NUMBER_GROUP = 2
122
126
 
@@ -1,6 +1,7 @@
1
1
  import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
2
2
  import { git_gh_exec } from '#scripts/gh/git-gh-exec'
3
3
  import { IN_PROGRESS_LABEL } from '#scripts/issue/issue-labels'
4
+ import { MS_PER_MINUTE } from '#scripts/lib/timeouts'
4
5
 
5
6
  // How long each lane holder has carried `in-progress`. The stale-label rule
6
7
  // in `backlogrun-progress.md` is applied by age, and until this read the age was a hand-written
@@ -12,7 +13,6 @@ import { IN_PROGRESS_LABEL } from '#scripts/issue/issue-labels'
12
13
 
13
14
  // Matches the `Stale \`in-progress\`` row of the table in `backlogrun-progress.md`.
14
15
  const STALE_MINUTES = 90
15
- const MS_PER_MINUTE = 60_000
16
16
  const TIMELINE_PAGE = 100
17
17
 
18
18
  const LABELED_FILTER = `.[] | select(.event == "labeled" and (.label.name | ascii_downcase) == "${IN_PROGRESS_LABEL}") | .created_at`
@@ -1,4 +1,5 @@
1
1
  import { lane_capacity } from '#scripts/lane/lane-capacity'
2
+ import { lane_snapshot_trace } from '#scripts/lane/lane-snapshot-trace'
2
3
  import { epic_busy, type BusyRead } from './epic-busy'
3
4
  import {
4
5
  epic_candidate_confirm,
@@ -39,11 +40,13 @@ interface RepoPool {
39
40
 
40
41
  // What is being asked of one repository. `is_all_lanes` is the caller's appetite rather than the
41
42
  // repository's capacity: `epic:next --repo` prints a single token unless `--lanes` was passed, so a
42
- // caller written against the one-child contract keeps getting exactly one child.
43
+ // caller written against the one-child contract keeps getting exactly one child. `is_this_checkout`
44
+ // says whether `repo` is the repository this command runs in, whose ship records are the ones read.
43
45
  interface LaneRequest {
44
46
  repo: string
45
47
  limit: number
46
48
  is_all_lanes: boolean
49
+ is_this_checkout: boolean
47
50
  }
48
51
 
49
52
  // The answer for one repository: the children to start, the verdict standing in their place when
@@ -62,8 +65,27 @@ function is_held(read: BusyRead): boolean {
62
65
  return read.kind === 'unreadable' || read.kind === 'truncated'
63
66
  }
64
67
 
65
- function free_lanes(read: BusyRead, limit: number): number {
66
- return is_held(read) ? NO_LANES : lane_capacity.free_lanes(limit, epic_busy.occupied_lanes(read))
68
+ function free_lanes(read: BusyRead, limit: number, merge_waiting: number): number {
69
+ if (is_held(read)) return NO_LANES
70
+
71
+ const holders = epic_busy.occupied_lanes(read)
72
+
73
+ // A merge-waiting holder frees its place under the limit but keeps its port seat
74
+ // (`lane_capacity.free_lanes`).
75
+ return lane_capacity.free_lanes(limit, holders - merge_waiting, holders)
76
+ }
77
+
78
+ // **A holder whose detached ship waits on its merge holds no lane** — the count `backlog:drive` makes
79
+ // for the pool (`backlog-occupied.ts`), so a named epic's lanes are offered the place too
80
+ // (joshuafolkken/kit#4466). It keeps `in-progress` until the merge. The ship records read are this
81
+ // checkout's, so a `--repo` naming another repository is discounted nothing: its holder numbers
82
+ // would be matched against this repository's unrelated ships.
83
+ async function merge_waiting_holders(read: BusyRead, request: LaneRequest): Promise<number> {
84
+ if (read.kind !== 'busy' || !request.is_this_checkout) return NO_LANES
85
+
86
+ return await lane_snapshot_trace.count_merge_waiting(
87
+ read.issues.map((issue) => String(issue.number)),
88
+ )
67
89
  }
68
90
 
69
91
  function wanted_of(request: LaneRequest, free: number): number {
@@ -247,7 +269,7 @@ async function offer_for_repo(
247
269
 
248
270
  if (notice !== undefined) console.error(notice)
249
271
 
250
- const free = free_lanes(read, request.limit)
272
+ const free = free_lanes(read, request.limit, await merge_waiting_holders(read, request))
251
273
  const ages = await holder_ages(read, request.repo)
252
274
 
253
275
  if (free === NO_LANES) {
@@ -263,9 +285,22 @@ async function offer_for_repo(
263
285
  return solo_offer(await collect(for_repo, wanted_of(request, free)), read, request, ages)
264
286
  }
265
287
 
288
+ // A `--repo` compared against the repository this command runs in, case-insensitively as GitHub
289
+ // names it.
290
+ function lane_request(
291
+ asked: Pick<LaneRequest, 'repo' | 'is_all_lanes'>,
292
+ limit: number,
293
+ current_repo: string,
294
+ ): LaneRequest {
295
+ const is_this_checkout = asked.repo.toLowerCase() === current_repo.toLowerCase()
296
+
297
+ return { ...asked, limit, is_this_checkout }
298
+ }
299
+
266
300
  const epic_lane_offer = {
267
301
  combine_verdicts,
268
302
  dedupe_pools,
303
+ lane_request,
269
304
  offer_for_repo,
270
305
  }
271
306
 
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env tsx
2
+ import { fileURLToPath } from 'node:url'
3
+ import { josh_environment_file } from '#scripts/josh/josh-environment-file'
4
+ import { epic_next } from './epic-next'
5
+
6
+ // The `josh epic:next` entry, kept apart from `epic-next.ts` because other modules import that one: a
7
+ // bundle is one file, so a main guard there would fire for every command that carries it
8
+ // (joshuafolkken/kit#4186).
9
+ //
10
+ // `.env` is read here rather than through the dispatcher's `tsx_arguments`: declaring any would
11
+ // disqualify this command from in-process dispatch, and an unattended `epicrun` asks it every sixty
12
+ // seconds for the whole life of a run (`josh-environment-file.ts` records the measurement). Inside the
13
+ // guard, so a developer's own `.env` cannot decide what the unit tests see.
14
+ //
15
+ // `process.exitCode` rather than `process.exit()`, for the reason `scripts/cost-runtime/cost-main.ts`
16
+ // records: the answer goes to standard output and a write to a pipe is asynchronous on macOS, so
17
+ // exiting can tear the process down before it drains. This command's contract is
18
+ // `answer=$(pnpm josh epic:next <E>)`, which is exactly that pipe.
19
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
20
+ const ARGV_OFFSET = 2
21
+
22
+ josh_environment_file.load_environment_file()
23
+ process.exitCode = await epic_next.run(process.argv.slice(ARGV_OFFSET))
24
+ }
@@ -1,9 +1,6 @@
1
- #!/usr/bin/env tsx
2
- import { fileURLToPath } from 'node:url'
3
1
  import { repo_discovery } from '#scripts/discovery/repo-discovery'
4
2
  import { git_gh_command } from '#scripts/gh/git-gh-command'
5
3
  import { PROJECT_ROOT } from '#scripts/init/init-paths'
6
- import { josh_environment_file } from '#scripts/josh/josh-environment-file'
7
4
  import { lane_capacity } from '#scripts/lane/lane-capacity'
8
5
  import { cli_flags } from '#scripts/lib/cli-flags'
9
6
  import { issue_citation } from '#scripts/rules/issue-citation'
@@ -31,7 +28,6 @@ import { epic_report, type EpicNextResult } from './epic-report'
31
28
 
32
29
  const SUCCESS_EXIT_CODE = 0
33
30
  const FAILURE_EXIT_CODE = 1
34
- const ARGV_OFFSET = 2
35
31
  // Asking for every free lane (`--lanes`) is opt-in, so the answer stays one token for a caller that
36
32
  // has not been changed. `--repo` alone prints exactly one number.
37
33
  const OPTIONS = { repo: { type: 'string' }, lanes: { type: 'boolean' } } as const
@@ -351,11 +347,13 @@ async function report_epics(
351
347
  const choice = await lane_capacity.lane_limit()
352
348
  if (choice.kind === 'problem') return refuse(choice.problem)
353
349
 
354
- return await report(
355
- views,
356
- { repo: options.repo, limit: choice.limit, is_all_lanes: options.is_all_lanes ?? false },
350
+ const request = epic_lane_offer.lane_request(
351
+ { repo: options.repo, is_all_lanes: options.is_all_lanes ?? false },
352
+ choice.limit,
357
353
  current_repo,
358
354
  )
355
+
356
+ return await report(views, request, current_repo)
359
357
  }
360
358
 
361
359
  // One view per named epic, classified against one shared registry read. `running` is every named
@@ -416,14 +414,6 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
416
414
  return await run_epics(options)
417
415
  }
418
416
 
419
- // `process.exitCode` rather than `process.exit()`, for the reason `scripts/cost-runtime/cost-cli.ts` records:
420
- // the answer goes to standard output and a write to a pipe is asynchronous on macOS, so exiting can
421
- // tear the process down before it drains. This command's contract is `answer=$(pnpm josh epic:next
422
- // <E>)`, which is exactly that pipe.
423
- async function main(argv: ReadonlyArray<string>): Promise<void> {
424
- process.exitCode = await run(argv)
425
- }
426
-
427
417
  const epic_next = {
428
418
  EXTERNAL_NOTICE,
429
419
  FOREIGN_EPIC,
@@ -433,19 +423,11 @@ const epic_next = {
433
423
  parse_options,
434
424
  refuse_reads,
435
425
  views_of,
426
+ run,
436
427
  run_epics,
437
428
  decide,
438
429
  report,
439
430
  }
440
431
 
441
- // `.env` is read here rather than through the dispatcher's `tsx_arguments`: declaring any would
442
- // disqualify this command from in-process dispatch, and an unattended `epicrun` asks it every sixty
443
- // seconds for the whole life of a run (`josh-environment-file.ts` records the measurement). Inside the guard
444
- // rather than in `main`, so a developer's own `.env` cannot decide what the unit tests see.
445
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
446
- josh_environment_file.load_environment_file()
447
- await main(process.argv.slice(ARGV_OFFSET))
448
- }
449
-
450
432
  export type { NextOptions }
451
433
  export { epic_next }