@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
@@ -1,4 +1,5 @@
1
1
  import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
2
+ import { run_ship_planned } from '#scripts/run/ship/run-ship-planned'
2
3
  import { run_ship_stage } from '#scripts/run/ship/run-ship-stage'
3
4
  import { lane_ledger, type LedgerEntry, type StopEntry } from './lane-ledger'
4
5
  import type { IssueAt } from './lane-spans'
@@ -44,6 +45,7 @@ const EVENT_ISSUE = /#([1-9]\d*)\b/u
44
45
  // A stop recorded before it carried a cause (joshuafolkken/kit#3998) is counted under this one.
45
46
  const UNRECORDED_CAUSE = 'unrecorded'
46
47
  const NO_CAUSES = '-'
48
+ const NOTICED = ' (noticed)'
47
49
 
48
50
  // `causes` is a preflight stop's unmet preconditions or a review stop's blocking finding kinds, every
49
51
  // one of them, else the stop's one cause.
@@ -57,20 +59,28 @@ function is_stop(entry: LedgerEntry): entry is StopEntry {
57
59
  return entry.kind === lane_ledger.KIND.STOP
58
60
  }
59
61
 
62
+ // The issue a run event's text names first, `NaN` where it names none.
63
+ function issue_of(event: RunEvent): number {
64
+ return Number(EVENT_ISSUE.exec(event.text)?.[1])
65
+ }
66
+
60
67
  // Every run event of `kind`, with the issue its text names first.
61
68
  function events_of(events: ReadonlyArray<RunEvent>, kind: string): Array<IssueAt> {
62
69
  return events
63
70
  .filter((event) => event.kind === kind)
64
- .map((event) => ({
65
- issue: Number(EVENT_ISSUE.exec(event.text)?.[1]),
66
- at_ms: Date.parse(event.at),
67
- }))
71
+ .map((event) => ({ issue: issue_of(event), at_ms: Date.parse(event.at) }))
68
72
  }
69
73
 
70
74
  function parks_of(events: ReadonlyArray<RunEvent>): Array<IssueAt> {
71
75
  return events_of(events, run_event_stream.EVENT_KIND.PARK)
72
76
  }
73
77
 
78
+ function dispatches_of(entries: ReadonlyArray<LedgerEntry>): Array<IssueAt> {
79
+ return entries
80
+ .filter((entry) => is_dispatch(entry))
81
+ .map((entry) => ({ issue: entry.issue, at_ms: Date.parse(entry.at) }))
82
+ }
83
+
74
84
  function ends_of(
75
85
  entries: ReadonlyArray<LedgerEntry>,
76
86
  parks: ReadonlyArray<IssueAt>,
@@ -78,41 +88,63 @@ function ends_of(
78
88
  const resumes = entries
79
89
  .filter((entry) => is_stage(entry))
80
90
  .map((stage) => ({ issue: stage.issue, at_ms: lane_stage_stats.started_ms(stage) }))
81
- const dispatches = entries
82
- .filter((entry) => is_dispatch(entry))
83
- .map((entry) => ({ issue: entry.issue, at_ms: Date.parse(entry.at) }))
84
91
 
85
- return [...resumes, ...dispatches, ...parks]
92
+ return [...resumes, ...dispatches_of(entries), ...parks]
86
93
  }
87
94
 
88
95
  // A stage's start counts from the stop's own instant, so a ship relaunched within the same
89
- // millisecond still ends the wait; the stage that stopped started before it.
90
- function wait_ms(stop: StopEntry, ends: ReadonlyArray<IssueAt>): number | undefined {
91
- const from_ms = Date.parse(stop.at)
96
+ // millisecond still ends the wait; the stage that stopped started before it. `josh lane:net` derives
97
+ // a park's and an outage's wait the same way (`lane-net-time.ts`).
98
+ function wait_ms(stop: IssueAt, ends: ReadonlyArray<IssueAt>): number | undefined {
92
99
  const later = ends
93
- .filter((end) => end.issue === stop.issue && end.at_ms >= from_ms)
94
- .map((end) => end.at_ms - from_ms)
100
+ .filter((end) => end.issue === stop.issue && end.at_ms >= stop.at_ms)
101
+ .map((end) => end.at_ms - stop.at_ms)
95
102
 
96
103
  return later.length === 0 ? undefined : Math.min(...later)
97
104
  }
98
105
 
106
+ function instant_of(stop: StopEntry): IssueAt {
107
+ return { issue: stop.issue, at_ms: Date.parse(stop.at) }
108
+ }
109
+
110
+ // The `josh check:related` notices of the ship's `unplanned` answer (joshuafolkken/kit#5203).
111
+ function notices_of(entries: ReadonlyArray<LedgerEntry>): Array<IssueAt> {
112
+ return entries
113
+ .filter((entry) => entry.kind === lane_ledger.KIND.UNPLANNED_NOTICE)
114
+ .map((entry) => ({ issue: entry.issue, at_ms: Date.parse(entry.at) }))
115
+ }
116
+
117
+ // An `unplanned` stop the same issue's `check:related` already printed counts as `unplanned (noticed)`,
118
+ // so the stops the earlier notice did not prevent are a number of their own.
119
+ function noticed(cause: string, stop: IssueAt, notices: ReadonlyArray<IssueAt>): string {
120
+ if (cause !== run_ship_planned.PRECONDITION) return cause
121
+ const is_noticed = notices.some(
122
+ (notice) => notice.issue === stop.issue && notice.at_ms <= stop.at_ms,
123
+ )
124
+
125
+ return is_noticed ? `${cause}${NOTICED}` : cause
126
+ }
127
+
99
128
  // A stop recorded before it named its preconditions (joshuafolkken/kit#4166) or its finding kinds
100
129
  // (joshuafolkken/kit#4426) counts under its cause.
101
- function causes_of(stop: StopEntry): ReadonlyArray<string> {
130
+ function causes_of(stop: StopEntry, notices: ReadonlyArray<IssueAt>): ReadonlyArray<string> {
102
131
  const { preconditions = [], findings = [] } = stop
103
132
  const kinds = [...preconditions, ...findings]
104
133
 
105
- return kinds.length > 0 ? kinds : [stop.cause ?? UNRECORDED_CAUSE]
134
+ if (kinds.length === 0) return [stop.cause ?? UNRECORDED_CAUSE]
135
+
136
+ return kinds.map((kind) => noticed(kind, instant_of(stop), notices))
106
137
  }
107
138
 
108
139
  function waits_of(
109
140
  entries: ReadonlyArray<LedgerEntry>,
110
141
  ends: ReadonlyArray<IssueAt>,
142
+ notices: ReadonlyArray<IssueAt>,
111
143
  ): Array<StopWait> {
112
144
  return entries.filter(is_stop).map((stop) => ({
113
145
  reason: stop.reason,
114
- causes: causes_of(stop),
115
- wait_ms: wait_ms(stop, ends),
146
+ causes: causes_of(stop, notices),
147
+ wait_ms: wait_ms(instant_of(stop), ends),
116
148
  }))
117
149
  }
118
150
 
@@ -158,7 +190,7 @@ function table(
158
190
  events: ReadonlyArray<RunEvent> = [],
159
191
  ): string {
160
192
  const ends = ends_of(entries, parks_of(events))
161
- const waits = waits_of(lane_stats.in_window(entries, window), ends)
193
+ const waits = waits_of(lane_stats.in_window(entries, window), ends, notices_of(entries))
162
194
 
163
195
  return [
164
196
  lane_stats.to_row(COLUMNS),
@@ -167,6 +199,6 @@ function table(
167
199
  ].join('\n')
168
200
  }
169
201
 
170
- const lane_stop_stats = { REASONS, events_of, table }
202
+ const lane_stop_stats = { REASONS, dispatches_of, ends_of, events_of, issue_of, table, wait_ms }
171
203
 
172
204
  export { lane_stop_stats }
@@ -0,0 +1,32 @@
1
+ // The tail of the period's lane times beside `lane:stats`'s median (joshuafolkken/kit#4910): a lane's
2
+ // target is a ceiling, so the lanes over the overrun threshold and the 90th percentile say what a median
3
+ // hides. The threshold is the one the overrun investigation launches past (`lane-overrun.ts`), which
4
+ // judges a lane's net time; `lane:stats` counts on its dispatch-to-merge time.
5
+
6
+ // The net minutes a lane may take before its causes are investigated.
7
+ const OVERRUN_LANE_MINUTES = 40
8
+ const TAIL_PERCENTILE = 90
9
+ const WHOLE_PERCENT = 100
10
+
11
+ function is_overrun(minutes: number): boolean {
12
+ return minutes > OVERRUN_LANE_MINUTES
13
+ }
14
+
15
+ // The lanes over the threshold; one exactly at it is not over.
16
+ function overrun_count(minutes: ReadonlyArray<number>): number {
17
+ return minutes.filter((value) => is_overrun(value)).length
18
+ }
19
+
20
+ // The nearest-rank 90th percentile, else `undefined` with no lane.
21
+ function tail_minutes(minutes: ReadonlyArray<number>): number | undefined {
22
+ if (minutes.length === 0) return undefined
23
+
24
+ const sorted = minutes.toSorted((left, right) => left - right)
25
+ const rank = Math.ceil((TAIL_PERCENTILE / WHOLE_PERCENT) * sorted.length)
26
+
27
+ return sorted[rank - 1]
28
+ }
29
+
30
+ const lane_tail_stats = { is_overrun, OVERRUN_LANE_MINUTES, overrun_count, tail_minutes }
31
+
32
+ export { lane_tail_stats }
@@ -8,20 +8,39 @@ import { z } from 'zod'
8
8
  // **A backgrounded call is kept but not timed.** Its result line is the immediate "running in the
9
9
  // background" notice, so the gap measures nothing the run waited on; `duration_ms` is `undefined` for
10
10
  // it, as for a call whose result never arrived (an interrupted turn).
11
+ //
12
+ // **The tool's name is kept** (`Bash`, `Edit`, …), so `josh lane:value edit` can time the edits apart
13
+ // from the shell calls, **and so is its lane** — the transcript directory every session of one lane
14
+ // (its child, its ship reviewer) writes under — so a per-lane value divides by lanes, not sessions
15
+ // (joshuafolkken/kit#4630).
16
+ //
17
+ // **A subagent call keeps its agent type and model** — `subagent_type`, the harness's
18
+ // `general-purpose` when absent, and `model` when named — so the implement's subagent time splits by
19
+ // the agent dispatched (joshuafolkken/kit#5297).
20
+
21
+ interface Subagent {
22
+ type: string
23
+ model: string | undefined
24
+ }
11
25
 
12
26
  interface ToolCall {
13
27
  session_id: string
28
+ lane: string
14
29
  started_ms: number
15
30
  duration_ms: number | undefined
31
+ tool: string | undefined
16
32
  background: boolean
17
33
  command: string | undefined
34
+ subagent: Subagent | undefined
18
35
  output: string
19
36
  }
20
37
 
21
38
  interface ToolUse {
22
39
  started_ms: number
40
+ tool: string | undefined
23
41
  background: boolean
24
42
  command: string | undefined
43
+ subagent: Subagent | undefined
25
44
  }
26
45
 
27
46
  interface ToolResult {
@@ -40,19 +59,37 @@ function join_parts(parts: ReadonlyArray<TextPart>): string {
40
59
  interface RawInput {
41
60
  command?: unknown
42
61
  run_in_background?: unknown
62
+ subagent_type?: unknown
63
+ model?: unknown
43
64
  }
44
65
 
45
66
  interface CallInput {
46
67
  background: boolean
47
68
  command: string | undefined
69
+ subagent: Subagent
48
70
  }
49
71
 
50
- function to_input(input: RawInput | null | undefined): CallInput {
51
- const command = input?.command
72
+ const SUBAGENT_TOOLS: ReadonlySet<string> = new Set(['Agent', 'Task'])
73
+ const GENERAL_PURPOSE = 'general-purpose'
74
+
75
+ function string_or_undefined(value: unknown): string | undefined {
76
+ return typeof value === 'string' ? value : undefined
77
+ }
78
+
79
+ // A named string, `undefined` for an empty or non-string one.
80
+ function named(value: unknown): string | undefined {
81
+ return value === '' ? undefined : string_or_undefined(value)
82
+ }
83
+
84
+ function subagent_of(input: RawInput | null | undefined): Subagent {
85
+ return { type: named(input?.subagent_type) ?? GENERAL_PURPOSE, model: named(input?.model) }
86
+ }
52
87
 
88
+ function to_input(input: RawInput | null | undefined): CallInput {
53
89
  return {
54
90
  background: input?.run_in_background === true,
55
- command: typeof command === 'string' ? command : undefined,
91
+ command: string_or_undefined(input?.command),
92
+ subagent: subagent_of(input),
56
93
  }
57
94
  }
58
95
 
@@ -61,11 +98,19 @@ const TEXT_PARTS_SCHEMA = z.array(z.object({ text: z.string().nullish() }))
61
98
  const RESULT_CONTENT_SCHEMA = z
62
99
  .union([z.string(), TEXT_PARTS_SCHEMA.transform(join_parts)])
63
100
  .catch('')
64
- const INPUT_SCHEMA = z.object({ command: z.unknown(), run_in_background: z.unknown() }).partial()
101
+ const INPUT_SCHEMA = z
102
+ .object({
103
+ command: z.unknown(),
104
+ run_in_background: z.unknown(),
105
+ subagent_type: z.unknown(),
106
+ model: z.unknown(),
107
+ })
108
+ .partial()
65
109
 
66
110
  const BLOCK_SCHEMA = z.object({
67
111
  type: z.string(),
68
112
  id: z.string().nullish(),
113
+ name: z.string().nullish(),
69
114
  input: INPUT_SCHEMA.nullish().transform(to_input),
70
115
  tool_use_id: z.string().nullish(),
71
116
  content: RESULT_CONTENT_SCHEMA.optional(),
@@ -91,10 +136,17 @@ interface Pairs {
91
136
  results: Map<string, ToolResult>
92
137
  }
93
138
 
139
+ // The agent a subagent call dispatched; any other tool's input names none.
140
+ function subagent_in(tool: string | undefined, block: Block): Subagent | undefined {
141
+ return SUBAGENT_TOOLS.has(tool ?? '') ? block.input.subagent : undefined
142
+ }
143
+
94
144
  function record_use(pairs: Pairs, block: Block, started_ms: number): void {
95
145
  if (block.type !== 'tool_use' || !block.id) return
96
146
 
97
- pairs.uses.set(block.id, { started_ms, ...block.input })
147
+ const tool = block.name ?? undefined
148
+
149
+ pairs.uses.set(block.id, { started_ms, tool, ...block.input, subagent: subagent_in(tool, block) })
98
150
  }
99
151
 
100
152
  function record_result(pairs: Pairs, block: Block, ended_ms: number): void {
@@ -116,30 +168,35 @@ function record_line(pairs: Pairs, line: string): void {
116
168
  }
117
169
  }
118
170
 
119
- function to_call(session_id: string, use: ToolUse, result: ToolResult | undefined): ToolCall {
171
+ // The transcript a call was read from: its session, and the lane that session belongs to.
172
+ type CallSource = Pick<ToolCall, 'session_id' | 'lane'>
173
+
174
+ function to_call(source: CallSource, use: ToolUse, result: ToolResult | undefined): ToolCall {
120
175
  const is_timed = result !== undefined && !use.background
121
176
 
122
177
  return {
123
- session_id,
178
+ ...source,
124
179
  started_ms: use.started_ms,
125
180
  duration_ms: is_timed ? result.ended_ms - use.started_ms : undefined,
181
+ tool: use.tool,
126
182
  background: use.background,
127
183
  command: use.command,
184
+ subagent: use.subagent,
128
185
  output: result?.output ?? '',
129
186
  }
130
187
  }
131
188
 
132
189
  // Every tool call `raw` — one transcript's text — records, in the order the calls were made.
133
- function parse(session_id: string, raw: string): Array<ToolCall> {
190
+ function parse(source: CallSource, raw: string): Array<ToolCall> {
134
191
  const pairs: Pairs = { uses: new Map(), results: new Map() }
135
192
  const lines = raw.split('\n')
136
193
 
137
194
  for (const line of lines) record_line(pairs, line)
138
195
 
139
- return [...pairs.uses].map(([id, use]) => to_call(session_id, use, pairs.results.get(id)))
196
+ return [...pairs.uses].map(([id, use]) => to_call(source, use, pairs.results.get(id)))
140
197
  }
141
198
 
142
199
  const lane_transcript_calls = { parse }
143
200
 
144
- export type { ToolCall }
201
+ export type { Subagent, ToolCall }
145
202
  export { lane_transcript_calls }
@@ -1,3 +1,4 @@
1
+ import path from 'node:path'
1
2
  import { cost_transcript } from '#scripts/cost-runtime/cost-transcript'
2
3
  import { MS_PER_SECOND } from '#scripts/lib/timeouts'
3
4
  import { time_shell } from '#scripts/time-runtime/time-shell'
@@ -65,11 +66,16 @@ function segment_command(segment: string): string {
65
66
  return named.slice(time_shell.JOSH_PREFIX.length)
66
67
  }
67
68
 
68
- function verification_commands(command = ''): Array<string> {
69
+ // Every josh command a shell `command` runs, in order.
70
+ function josh_commands(command = ''): Array<string> {
69
71
  const spoken = time_shell.unquoted(command)
70
72
  const named = spoken.split(SEPARATOR_PATTERN).map((segment) => segment_command(segment))
71
73
 
72
- return named.filter((name) => VERIFICATION_COMMANDS.has(name))
74
+ return named.filter((name) => name !== '')
75
+ }
76
+
77
+ function verification_commands(command = ''): Array<string> {
78
+ return josh_commands(command).filter((name) => VERIFICATION_COMMANDS.has(name))
73
79
  }
74
80
 
75
81
  function seconds_cell(ms: number | undefined): string {
@@ -84,11 +90,14 @@ function durations(calls: ReadonlyArray<ToolCall>): Array<number> {
84
90
  return calls.map((call) => call.duration_ms).filter((ms) => is_timed(ms))
85
91
  }
86
92
 
93
+ // The lane sessions that made a call among `calls`, each once.
94
+ function sessions_of(calls: ReadonlyArray<ToolCall>): Array<string> {
95
+ return [...new Set(calls.map((call) => call.session_id))]
96
+ }
97
+
87
98
  // Each session's summed foreground tool time, for the sessions that made a call in the period.
88
99
  function session_times(calls: ReadonlyArray<ToolCall>): Array<number> {
89
- const sessions = [...new Set(calls.map((call) => call.session_id))]
90
-
91
- return sessions.map((session) =>
100
+ return sessions_of(calls).map((session) =>
92
101
  durations(calls.filter((call) => call.session_id === session)).reduce(
93
102
  (sum, value) => sum + value,
94
103
  0,
@@ -102,12 +111,17 @@ function share_cell(part: number, whole: number): string {
102
111
  return `${String(part)} of ${String(whole)} (${((part / whole) * PERCENT).toFixed(1)}%)`
103
112
  }
104
113
 
105
- function solo_median_row(verifying: ReadonlyArray<ToolCall>, name: string): Array<string> {
106
- const solo = verifying.filter((call) => {
114
+ // The calls that ran the verification command `name` and no other, so each one's time is its own.
115
+ function solo_calls(calls: ReadonlyArray<ToolCall>, name: string): Array<ToolCall> {
116
+ return calls.filter((call) => {
107
117
  const named = verification_commands(call.command)
108
118
 
109
119
  return named.length === 1 && named[0] === name
110
120
  })
121
+ }
122
+
123
+ function solo_median_row(verifying: ReadonlyArray<ToolCall>, name: string): Array<string> {
124
+ const solo = solo_calls(verifying, name)
111
125
 
112
126
  return [`${name} median, solo (s)`, seconds_cell(lane_stats.median(durations(solo)))]
113
127
  }
@@ -140,11 +154,15 @@ function rows_of(calls: ReadonlyArray<ToolCall>): Array<Array<string>> {
140
154
  ]
141
155
  }
142
156
 
143
- // The period's table: `calls` is every lane session's calls, filtered here to `window`.
144
- function table(calls: ReadonlyArray<ToolCall>, window: StatsWindow): string {
145
- const period = calls.filter(
157
+ function in_period(calls: ReadonlyArray<ToolCall>, window: StatsWindow): Array<ToolCall> {
158
+ return calls.filter(
146
159
  (call) => call.started_ms >= window.since_ms && call.started_ms <= window.until_ms,
147
160
  )
161
+ }
162
+
163
+ // The period's table: `calls` is every lane session's calls, filtered here to `window`.
164
+ function table(calls: ReadonlyArray<ToolCall>, window: StatsWindow): string {
165
+ const period = in_period(calls, window)
148
166
 
149
167
  return [
150
168
  lane_stats.to_row(COLUMNS),
@@ -156,13 +174,27 @@ function table(calls: ReadonlyArray<ToolCall>, window: StatsWindow): string {
156
174
  // The tool calls of every lane session under `directories` that was still written to in `window` — a
157
175
  // transcript last modified before the period holds no call in it, so it is never read. A delegated
158
176
  // unit's transcript is not a lane session, and its time is already inside the parent's wait for it.
177
+ // A session's own transcript sits directly in its lane's directory, so that directory is its lane.
159
178
  function read_calls(directories: ReadonlyArray<string>, window: StatsWindow): Array<ToolCall> {
160
179
  return cost_transcript
161
180
  .list_sessions_across(directories)
162
181
  .filter((file) => !file.is_delegated && file.modified_ms >= window.since_ms)
163
- .flatMap((file) => lane_transcript_calls.parse(file.session_id, cost_transcript.read_raw(file)))
182
+ .flatMap((file) =>
183
+ lane_transcript_calls.parse(
184
+ { session_id: file.session_id, lane: path.dirname(file.path) },
185
+ cost_transcript.read_raw(file),
186
+ ),
187
+ )
164
188
  }
165
189
 
166
- const lane_transcript_stats = { read_calls, table, verification_commands }
190
+ const lane_transcript_stats = {
191
+ durations,
192
+ in_period,
193
+ josh_commands,
194
+ read_calls,
195
+ solo_calls,
196
+ table,
197
+ verification_commands,
198
+ }
167
199
 
168
200
  export { lane_transcript_stats }
@@ -0,0 +1,72 @@
1
+ // `josh lane:value cell --table <title> --row <label> --column <header>` — one cell of a table
2
+ // `josh lane:stats` prints, as one number (joshuafolkken/kit#5144). The tables are read as printed, so
3
+ // every new `lane:stats` table is declarable without a new metric and no aggregation is copied.
4
+ //
5
+ // A table is named by its first header cell and a row by its first cell. **The address must name one
6
+ // cell** — two tables share the `check` title, so an address that matches more than one numeric cell
7
+ // answers `undefined`, as a missing or non-numeric one does, never a guess.
8
+
9
+ const ROW_OPEN = '| '
10
+ const ROW_CLOSE = ' |'
11
+ const CELL_SEPARATOR = ' | '
12
+ const BLOCK_SEPARATOR = /\n\s*\n/u
13
+ const NUMBER_PATTERN = /^-?\d+(?:\.\d+)?$/u
14
+ const HEADER_ROWS = 2
15
+
16
+ interface CellAddress {
17
+ table: string
18
+ row: string
19
+ column: string
20
+ }
21
+
22
+ type Cells = ReadonlyArray<string>
23
+
24
+ function cells_of(line: string): Cells | undefined {
25
+ const text = line.trim()
26
+
27
+ if (!text.startsWith(ROW_OPEN) || !text.endsWith(ROW_CLOSE)) return undefined
28
+
29
+ return text.slice(ROW_OPEN.length, -ROW_CLOSE.length).split(CELL_SEPARATOR)
30
+ }
31
+
32
+ function rows_of(block: string): ReadonlyArray<Cells> {
33
+ return block
34
+ .split('\n')
35
+ .map((line) => cells_of(line))
36
+ .filter((row) => row !== undefined)
37
+ }
38
+
39
+ // Every printed table as its rows of cells, the header first; a block with no row is dropped.
40
+ function blocks_of(tables: ReadonlyArray<string>): ReadonlyArray<ReadonlyArray<Cells>> {
41
+ return tables
42
+ .flatMap((table) => table.split(BLOCK_SEPARATOR))
43
+ .map((block) => rows_of(block))
44
+ .filter((rows) => rows.length > 0)
45
+ }
46
+
47
+ // The cell texts at the address in one table — none when its title or column differs.
48
+ function texts_in(rows: ReadonlyArray<Cells>, address: CellAddress): ReadonlyArray<string> {
49
+ const [header = []] = rows
50
+ const column = header.indexOf(address.column)
51
+
52
+ if (header[0] !== address.table || column < 1) return []
53
+
54
+ return rows
55
+ .slice(HEADER_ROWS)
56
+ .filter((cells) => cells[0] === address.row)
57
+ .map((cells) => cells[column] ?? '')
58
+ }
59
+
60
+ // The one numeric cell at `address` across `tables`, or `undefined` for none, many or a non-number.
61
+ function value(tables: ReadonlyArray<string>, address: CellAddress): number | undefined {
62
+ const numbers = blocks_of(tables)
63
+ .flatMap((rows) => texts_in(rows, address))
64
+ .filter((text) => NUMBER_PATTERN.test(text))
65
+
66
+ return numbers.length === 1 ? Number(numbers[0]) : undefined
67
+ }
68
+
69
+ const lane_value_cell = { value }
70
+
71
+ export type { CellAddress }
72
+ export { lane_value_cell }
@@ -0,0 +1,118 @@
1
+ import { metrics_totals } from '#scripts/gate/metrics-totals'
2
+ import { since_green } from '#scripts/gate/since-green'
3
+ import { MS_PER_MINUTE, MS_PER_SECOND } from '#scripts/lib/timeouts'
4
+ import { run_ship_pre_detach } from '#scripts/run/ship/run-ship-pre-detach'
5
+ import { lane_ledger, type CoreWaitEntry, type LedgerEntry } from './lane-ledger'
6
+ import { lane_stats, type StatsWindow } from './lane-stats'
7
+ import type { ToolCall } from './lane-transcript-calls'
8
+ import { lane_transcript_stats } from './lane-transcript-stats'
9
+
10
+ // `josh lane:value <metric> --since <instant>` — one lane measurement as one number, so a follow-up
11
+ // measurement Issue can declare it (its measurement declaration line) and `backlog:drive` closes it without a lane
12
+ // (joshuafolkken/kit#4630). Each metric is one an Issue measured by hand from the lane transcripts or
13
+ // the ledger; `lane:stats` prints the tables they were read from.
14
+ //
15
+ // **Per lane is per lane, not per session** — every lane runs at least a child and a ship-reviewer
16
+ // session, so the divisor is the lanes whose sessions made a tool call in the period. A metric with no
17
+ // sample answers `undefined`, never a zero that would pass.
18
+
19
+ const LINT_RELATED = 'lint:related'
20
+ const SHIP = 'ship'
21
+ const EDIT_TOOL = 'Edit'
22
+ // Both pre-detach forms: a `--since` window from before joshuafolkken/kit#4716 holds the bare form's.
23
+ const METRICS_STOPS: ReadonlyArray<string> = [
24
+ metrics_totals.METRICS_RERECORD_COMMAND,
25
+ metrics_totals.METRICS_TOTALS_COMMAND,
26
+ ].map((list) => run_ship_pre_detach.failure_prefix(list))
27
+
28
+ interface LaneRecords {
29
+ entries: ReadonlyArray<LedgerEntry>
30
+ calls: ReadonlyArray<ToolCall>
31
+ }
32
+
33
+ type Metric = (records: LaneRecords) => number | undefined
34
+
35
+ function seconds_of(ms: number | undefined): number | undefined {
36
+ return ms === undefined ? undefined : ms / MS_PER_SECOND
37
+ }
38
+
39
+ // `count` spread over the period's lanes, or `undefined` when no lane ran.
40
+ function per_lane(count: number, calls: ReadonlyArray<ToolCall>): number | undefined {
41
+ const lanes = new Set(calls.map((call) => call.lane)).size
42
+
43
+ return lanes === 0 ? undefined : count / lanes
44
+ }
45
+
46
+ // #4599: the median time of a `lint:related` that found nothing moved since its last green run.
47
+ function lint_unmoved({ calls }: LaneRecords): number | undefined {
48
+ const unmoved = lane_transcript_stats
49
+ .solo_calls(calls, LINT_RELATED)
50
+ .filter((call) => call.output.includes(since_green.UNMOVED_REASON))
51
+
52
+ return seconds_of(lane_stats.median(lane_transcript_stats.durations(unmoved)))
53
+ }
54
+
55
+ // #4600: the `ship` calls whose pre-detach check the metrics totals stopped, per lane.
56
+ function metrics_stops({ calls }: LaneRecords): number | undefined {
57
+ const stops = calls.filter(
58
+ (call) =>
59
+ lane_transcript_stats.verification_commands(call.command).includes(SHIP) &&
60
+ METRICS_STOPS.some((stop) => call.output.includes(stop)),
61
+ )
62
+
63
+ return per_lane(stops.length, calls)
64
+ }
65
+
66
+ // A lane's `lint:related` wait — one outside a lane (the main checkout's) has no `issue` and no lane
67
+ // session to be divided among.
68
+ function is_lint_wait(entry: LedgerEntry): entry is CoreWaitEntry {
69
+ return (
70
+ entry.kind === lane_ledger.KIND.CORE_WAIT &&
71
+ entry.command === LINT_RELATED &&
72
+ entry.issue !== undefined
73
+ )
74
+ }
75
+
76
+ // #4601: the minutes `lint:related` waited for the core budget, per lane.
77
+ function lint_core_wait({ entries, calls }: LaneRecords): number | undefined {
78
+ const waited = entries.filter((entry) => is_lint_wait(entry))
79
+ const total_ms = waited.reduce((sum, wait) => sum + wait.waited_ms, 0)
80
+
81
+ return per_lane(total_ms / MS_PER_MINUTE, calls)
82
+ }
83
+
84
+ // #4602: the median time of one `Edit` call, hooks included.
85
+ function edit({ calls }: LaneRecords): number | undefined {
86
+ const edits = calls.filter((call) => call.tool === EDIT_TOOL)
87
+
88
+ return seconds_of(lane_stats.median(lane_transcript_stats.durations(edits)))
89
+ }
90
+
91
+ const METRICS: Readonly<Record<string, Metric>> = {
92
+ 'lint-unmoved': lint_unmoved,
93
+ 'metrics-stops': metrics_stops,
94
+ 'lint-core-wait': lint_core_wait,
95
+ edit,
96
+ }
97
+
98
+ const NAMES: ReadonlyArray<string> = Object.keys(METRICS)
99
+
100
+ function is_metric(name: string): boolean {
101
+ return Object.hasOwn(METRICS, name)
102
+ }
103
+
104
+ // The value of `name` over `window`; `records` are the whole ledger and every lane call, cut here.
105
+ function value(name: string, records: LaneRecords, window: StatsWindow): number | undefined {
106
+ const metric = is_metric(name) ? METRICS[name] : undefined
107
+ const period = {
108
+ entries: lane_stats.in_window(records.entries, window),
109
+ calls: lane_transcript_stats.in_period(records.calls, window),
110
+ }
111
+
112
+ return metric === undefined ? undefined : metric(period)
113
+ }
114
+
115
+ const lane_value = { NAMES, is_metric, value }
116
+
117
+ export type { LaneRecords }
118
+ export { lane_value }