tumwater 0.2.0 → 0.3.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 (446) hide show
  1. package/README.md +27 -13
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +8 -9
  4. package/dist/src/agent/agent-backend.js +8 -6
  5. package/dist/src/agent/backend-traits.js +42 -25
  6. package/dist/src/agent/claude/claude-account.js +71 -204
  7. package/dist/src/agent/claude/claude-args.js +9 -6
  8. package/dist/src/agent/claude/claude-backend.js +10 -10
  9. package/dist/src/agent/claude/claude-env.js +1 -1
  10. package/dist/src/agent/claude/claude-stream.js +4 -3
  11. package/dist/src/agent/claude/claude-write-guard.js +49 -0
  12. package/dist/src/agent/claude/write-guard-hook.js +13 -0
  13. package/dist/src/agent/codex/codex-account.js +119 -0
  14. package/dist/src/agent/codex/codex-args.js +181 -0
  15. package/dist/src/agent/codex/codex-backend.js +279 -0
  16. package/dist/src/agent/codex/codex-catalog.js +97 -0
  17. package/dist/src/agent/codex/codex-env.js +18 -0
  18. package/dist/src/agent/codex/codex-hook-trust.js +246 -0
  19. package/dist/src/agent/codex/codex-patch-paths.js +36 -0
  20. package/dist/src/agent/codex/codex-rollout.js +179 -0
  21. package/dist/src/agent/codex/codex-stream.js +402 -0
  22. package/dist/src/agent/codex/codex-write-guard.js +86 -0
  23. package/dist/src/agent/codex/write-guard-hook.js +12 -0
  24. package/dist/src/agent/image-input.js +29 -24
  25. package/dist/src/agent/notes-shim.js +11 -12
  26. package/dist/src/agent/opencode/opencode-args.js +0 -1
  27. package/dist/src/agent/opencode/opencode-backend.js +12 -9
  28. package/dist/src/agent/opencode/opencode-catalog.js +55 -29
  29. package/dist/src/agent/opencode/opencode-config.js +19 -14
  30. package/dist/src/agent/opencode/opencode-env.js +0 -1
  31. package/dist/src/agent/opencode/opencode-session-id.js +1 -2
  32. package/dist/src/agent/opencode/opencode-stream.js +1 -1
  33. package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
  34. package/dist/src/agent/role-notes-write.js +5 -6
  35. package/dist/src/agent/run-agent.js +465 -448
  36. package/dist/src/agent/run-settle.js +214 -0
  37. package/dist/src/agent/session-ledger.js +86 -16
  38. package/dist/src/agent/subscription-account.js +220 -0
  39. package/dist/src/agent/write-guard.js +148 -0
  40. package/dist/src/backlog/backlog-archive.js +246 -55
  41. package/dist/src/backlog/backlog-cli-view.js +280 -0
  42. package/dist/src/backlog/backlog-cli.js +85 -274
  43. package/dist/src/backlog/backlog-commit.js +4 -5
  44. package/dist/src/backlog/backlog-eligibility.js +175 -242
  45. package/dist/src/backlog/backlog-index.js +149 -0
  46. package/dist/src/backlog/backlog-md.js +54 -6
  47. package/dist/src/backlog/backlog-parts.js +254 -0
  48. package/dist/src/backlog/backlog-release.js +117 -0
  49. package/dist/src/backlog/backlog-render.js +27 -0
  50. package/dist/src/backlog/backlog-structure.js +99 -156
  51. package/dist/src/backlog/backlog-write.js +40 -127
  52. package/dist/src/backlog/backlog.js +21 -6
  53. package/dist/src/backlog/question-answer.js +21 -15
  54. package/dist/src/baseline/main-baseline.js +89 -20
  55. package/dist/src/baseline/main-culprit.js +94 -0
  56. package/dist/src/baseline/main-guardrails.js +209 -0
  57. package/dist/src/baseline/main-red.js +139 -32
  58. package/dist/src/baseline/main-revert.js +133 -0
  59. package/dist/src/baseline/main-watch.js +184 -0
  60. package/dist/src/brief/brief-edit.js +9 -11
  61. package/dist/src/brief/brief.js +31 -51
  62. package/dist/src/budget/budget.js +30 -20
  63. package/dist/src/budget/fallback-breaker.js +26 -27
  64. package/dist/src/build/build-check-detect.js +23 -15
  65. package/dist/src/build/build-check-events.js +25 -6
  66. package/dist/src/build/build-check-report.js +49 -9
  67. package/dist/src/build/build-check-scoped.js +18 -11
  68. package/dist/src/build/build-check.js +2 -1
  69. package/dist/src/build/build-info.js +2 -3
  70. package/dist/src/build/build-stage.js +9 -14
  71. package/dist/src/build/dep-install.js +1 -2
  72. package/dist/src/change/change-data.js +123 -31
  73. package/dist/src/change/change-render.js +46 -5
  74. package/dist/src/cli/brief-command.js +3 -4
  75. package/dist/src/cli/cli-args.js +118 -33
  76. package/dist/src/cli/cli-command-args.js +46 -18
  77. package/dist/src/cli/cli-editor.js +2 -1
  78. package/dist/src/cli/cli-flag-specs.js +150 -45
  79. package/dist/src/cli/cli-init.js +51 -0
  80. package/dist/src/cli/cli-marker-commands.js +4 -4
  81. package/dist/src/cli/cli-query-commands.js +308 -590
  82. package/dist/src/cli/cli-repo-gate.js +11 -1
  83. package/dist/src/cli/cli-role-command.js +420 -0
  84. package/dist/src/cli/cli-start-summary.js +160 -0
  85. package/dist/src/cli/{cli-run.js → cli-start.js} +165 -237
  86. package/dist/src/cli/completion.js +149 -22
  87. package/dist/src/cli/config-commands.js +41 -18
  88. package/dist/src/cli/follow-view.js +75 -0
  89. package/dist/src/cli/help.js +189 -73
  90. package/dist/src/cli/log-commands.js +148 -82
  91. package/dist/src/cli/question-commands.js +205 -10
  92. package/dist/src/cli.js +106 -43
  93. package/dist/src/collections.js +26 -10
  94. package/dist/src/concurrency/check-permit.js +20 -3
  95. package/dist/src/concurrency/lock.js +17 -13
  96. package/dist/src/concurrency/provider-lease.js +44 -8
  97. package/dist/src/concurrency/semaphore.js +102 -26
  98. package/dist/src/config/config-aliases.js +0 -1
  99. package/dist/src/config/config-editable-keys.js +13 -1
  100. package/dist/src/config/config-fallbacks.js +93 -56
  101. package/dist/src/config/config-field-checks.js +40 -10
  102. package/dist/src/config/config-keys.js +30 -8
  103. package/dist/src/config/config-live.js +63 -24
  104. package/dist/src/config/config-request.js +137 -0
  105. package/dist/src/config/config-schema.js +31 -24
  106. package/dist/src/config/config-selector-checks.js +123 -0
  107. package/dist/src/config/config-validation-agents.js +80 -0
  108. package/dist/src/config/config-validation-model.js +116 -0
  109. package/dist/src/config/config-validation.js +141 -199
  110. package/dist/src/config/config-views.js +127 -53
  111. package/dist/src/config/config-write-core.js +135 -0
  112. package/dist/src/config/config-write.js +131 -264
  113. package/dist/src/config/config.js +48 -38
  114. package/dist/src/config/model-selector.js +34 -34
  115. package/dist/src/doctor/doctor-backlog-bugs.js +287 -0
  116. package/dist/src/doctor/doctor-backlog-plans.js +263 -0
  117. package/dist/src/doctor/doctor-backlog.js +100 -496
  118. package/dist/src/doctor/doctor-check-names.js +81 -0
  119. package/dist/src/doctor/doctor-checks.js +52 -579
  120. package/dist/src/doctor/doctor-claude-checks.js +145 -0
  121. package/dist/src/doctor/doctor-codex-checks.js +279 -0
  122. package/dist/src/doctor/doctor-contention-checks.js +380 -0
  123. package/dist/src/doctor/doctor-holds.js +82 -66
  124. package/dist/src/doctor/doctor-install.js +2 -1
  125. package/dist/src/doctor/doctor-model-checks.js +236 -402
  126. package/dist/src/doctor/doctor-notify.js +3 -4
  127. package/dist/src/doctor/doctor-opencode-checks.js +162 -0
  128. package/dist/src/doctor/doctor-orphans.js +182 -63
  129. package/dist/src/doctor/doctor-project-checks.js +303 -0
  130. package/dist/src/doctor/doctor.js +121 -77
  131. package/dist/src/doctor/image-probe.js +21 -8
  132. package/dist/src/doctor/outcome.js +3 -2
  133. package/dist/src/doctor/probe.js +7 -11
  134. package/dist/src/events/event-format.js +181 -86
  135. package/dist/src/events/event-labels.js +12 -5
  136. package/dist/src/events/event-legacy.js +39 -15
  137. package/dist/src/events/event-read.js +39 -39
  138. package/dist/src/events/event-tally.js +16 -0
  139. package/dist/src/events/event-window.js +60 -24
  140. package/dist/src/events/events.js +30 -13
  141. package/dist/src/events/notify.js +7 -7
  142. package/dist/src/failure/failure-cluster.js +12 -12
  143. package/dist/src/failure/failure-data.js +56 -56
  144. package/dist/src/failure/failure-render.js +31 -23
  145. package/dist/src/failure/failure-state-change.js +17 -12
  146. package/dist/src/failure/time-spend.js +99 -35
  147. package/dist/src/files/files.js +111 -35
  148. package/dist/src/files/json-files.js +3 -3
  149. package/dist/src/files/json-object.js +28 -7
  150. package/dist/src/files/stat-cache.js +2 -2
  151. package/dist/src/files/tail.js +1 -1
  152. package/dist/src/gates/bootstrap-gates.js +4 -5
  153. package/dist/src/gates/budget-gates.js +177 -84
  154. package/dist/src/gates/disk-gate.js +1 -1
  155. package/dist/src/gates/gate-polls.js +108 -58
  156. package/dist/src/gates/maintenance-quota.js +10 -13
  157. package/dist/src/gates/pause-gates.js +28 -10
  158. package/dist/src/gates/readiness.js +7 -3
  159. package/dist/src/gates/role-cap-gates.js +8 -8
  160. package/dist/src/gates/startup-gate.js +94 -32
  161. package/dist/src/gates/streak-gate.js +54 -30
  162. package/dist/src/git/commit-message.js +77 -46
  163. package/dist/src/git/git-diff.js +16 -10
  164. package/dist/src/git/git-run.js +79 -38
  165. package/dist/src/git/git.js +36 -34
  166. package/dist/src/git/slot-salvage.js +253 -0
  167. package/dist/src/git/slots-state.js +6 -6
  168. package/dist/src/git/worktree-pool.js +132 -38
  169. package/dist/src/git/worktree-use.js +4 -11
  170. package/dist/src/git/worktree.js +94 -37
  171. package/dist/src/gui/gui-admin-endpoints.js +96 -0
  172. package/dist/src/gui/gui-args.js +79 -8
  173. package/dist/src/gui/gui-backlog-commands.js +42 -18
  174. package/dist/src/gui/gui-backlog-endpoints.js +155 -0
  175. package/dist/src/gui/gui-command.js +8 -13
  176. package/dist/src/gui/gui-config-commands.js +94 -72
  177. package/dist/src/gui/gui-endpoint-commands.js +4 -3
  178. package/dist/src/gui/gui-endpoint-common.js +4 -4
  179. package/dist/src/gui/gui-endpoints.js +11 -380
  180. package/dist/src/gui/gui-history-endpoints.js +301 -0
  181. package/dist/src/gui/gui-operator-commands.js +108 -13
  182. package/dist/src/gui/gui-prompt-commands.js +11 -14
  183. package/dist/src/gui/gui-report-endpoints.js +90 -0
  184. package/dist/src/gui/gui-server.js +11 -4
  185. package/dist/src/gui/gui-team-commands.js +7 -7
  186. package/dist/src/gui/http-body.js +1 -1
  187. package/dist/src/history/history-data.js +122 -234
  188. package/dist/src/history/history-queued.js +147 -0
  189. package/dist/src/history/history-render.js +42 -22
  190. package/dist/src/history/history.js +193 -49
  191. package/dist/src/inbox/inbox-attachments.js +29 -30
  192. package/dist/src/inbox/inbox-cancel.js +10 -11
  193. package/dist/src/inbox/inbox-edit.js +9 -9
  194. package/dist/src/inbox/inbox-submit.js +18 -18
  195. package/dist/src/inbox/inbox-top.js +25 -9
  196. package/dist/src/inbox/inbox.js +42 -43
  197. package/dist/src/inbox/pending-prompt.js +26 -26
  198. package/dist/src/inbox/prompt-commands.js +62 -49
  199. package/dist/src/inbox/prompt-not-before.js +2 -2
  200. package/dist/src/init/init-templates.js +1 -2
  201. package/dist/src/init/init.js +28 -23
  202. package/dist/src/merge/backlog-conflicts.js +215 -35
  203. package/dist/src/merge/merge-batch.js +14 -8
  204. package/dist/src/merge/merge-cancel.js +53 -0
  205. package/dist/src/merge/merge-check-failures.js +44 -22
  206. package/dist/src/merge/merge-core.js +81 -32
  207. package/dist/src/merge/merge-diff.js +5 -4
  208. package/dist/src/merge/merge-drain.js +150 -11
  209. package/dist/src/merge/merge-git.js +177 -38
  210. package/dist/src/merge/merge-pipeline.js +15 -3
  211. package/dist/src/merge/merge-post-review.js +90 -0
  212. package/dist/src/merge/merge-queue-render.js +49 -11
  213. package/dist/src/merge/merge-queue.js +31 -15
  214. package/dist/src/merge/merge-requeue.js +102 -0
  215. package/dist/src/merge/merge-slot.js +61 -16
  216. package/dist/src/merge/merge-stack.js +33 -11
  217. package/dist/src/merge/merge-to-main.js +86 -27
  218. package/dist/src/merge/merge-top.js +69 -0
  219. package/dist/src/merge/merge-verify.js +12 -10
  220. package/dist/src/notes-cli.js +30 -15
  221. package/dist/src/operator/operator-commands.js +192 -81
  222. package/dist/src/operator/operator-intent.js +69 -42
  223. package/dist/src/operator/operator-requests.js +35 -40
  224. package/dist/src/operator/retire.js +5 -6
  225. package/dist/src/operator/revert.js +142 -34
  226. package/dist/src/orchestrator/orchestrator-launch.js +60 -52
  227. package/dist/src/orchestrator/orchestrator-poll.js +320 -0
  228. package/dist/src/orchestrator/orchestrator-scheduling.js +68 -369
  229. package/dist/src/orchestrator/orchestrator.js +128 -369
  230. package/dist/src/orchestrator/retention.js +42 -8
  231. package/dist/src/orchestrator/scheduling-admit.js +116 -0
  232. package/dist/src/orchestrator/scheduling-scope.js +201 -0
  233. package/dist/src/orchestrator/scheduling-skip.js +185 -0
  234. package/dist/src/orchestrator/startup-migration-steps.js +8 -7
  235. package/dist/src/orchestrator/startup-migrations.js +11 -7
  236. package/dist/src/paths.js +116 -49
  237. package/dist/src/pi/command-shape.js +12 -1
  238. package/dist/src/pi/pi-args.js +6 -4
  239. package/dist/src/pi/pi-backend.js +6 -6
  240. package/dist/src/pi/pi-bin.js +54 -63
  241. package/dist/src/pi/pi-listed-models.js +53 -0
  242. package/dist/src/pi/pi-models.js +63 -30
  243. package/dist/src/pi/pi-run-result.js +1 -1
  244. package/dist/src/pi/pi-settings.js +1 -1
  245. package/dist/src/pi/pi-stream.js +6 -6
  246. package/dist/src/pi/pi-watchdogs.js +18 -19
  247. package/dist/src/pi-extension/bounded-output.js +5 -3
  248. package/dist/src/pi-extension/context-budget.js +2 -2
  249. package/dist/src/pi-extension/context-shake.js +1 -1
  250. package/dist/src/pi-extension/context-window.js +0 -1
  251. package/dist/src/pi-extension/full-output.js +3 -10
  252. package/dist/src/pi-extension/strip-thinking.js +0 -1
  253. package/dist/src/pi-extension/trim-system-prompt.js +3 -4
  254. package/dist/src/pi-extension/write-guard.js +37 -0
  255. package/dist/src/process/launch-services.js +1 -2
  256. package/dist/src/process/node-bin.js +16 -0
  257. package/dist/src/process/process-group.js +8 -18
  258. package/dist/src/process/process.js +41 -2
  259. package/dist/src/process/run-marker.js +18 -6
  260. package/dist/src/process/supervisor.js +22 -21
  261. package/dist/src/prompt/dialect.js +17 -2
  262. package/dist/src/prompt/gate-notes.js +203 -0
  263. package/dist/src/prompt/gate-prompts.js +121 -211
  264. package/dist/src/prompt/notes-backlog.js +181 -0
  265. package/dist/src/prompt/principles.js +1 -2
  266. package/dist/src/prompt/prompt-followup.js +19 -14
  267. package/dist/src/prompt/prompt.js +129 -204
  268. package/dist/src/redeploy/redeploy-policy.js +24 -19
  269. package/dist/src/redeploy/redeploy-probes.js +1 -1
  270. package/dist/src/redeploy/redeploy.js +2 -3
  271. package/dist/src/redeploy/redeployer.js +20 -20
  272. package/dist/src/redeploy/self-reload.js +2 -1
  273. package/dist/src/report/report-data.js +161 -77
  274. package/dist/src/report/report-fold.js +129 -52
  275. package/dist/src/report/report-main-red.js +55 -0
  276. package/dist/src/report/report-render.js +133 -64
  277. package/dist/src/report/report-types.js +1 -0
  278. package/dist/src/report/report.js +26 -9
  279. package/dist/src/request-timeouts.js +2 -2
  280. package/dist/src/review/known-flakes.js +100 -1
  281. package/dist/src/review/post-merge-review.js +106 -0
  282. package/dist/src/review/protected-edits.js +4 -5
  283. package/dist/src/review/review-followup.js +8 -8
  284. package/dist/src/review/review-precheck.js +66 -23
  285. package/dist/src/review/review-run.js +395 -0
  286. package/dist/src/review/review-verdict.js +2 -2
  287. package/dist/src/review/review.js +155 -360
  288. package/dist/src/review/secret-leaks.js +36 -16
  289. package/dist/src/review/suite-rerun.js +0 -1
  290. package/dist/src/roles/legacy-role-migration.js +80 -7
  291. package/dist/src/roles/role-catalog.js +69 -12
  292. package/dist/src/roles/role-guidance.js +55 -20
  293. package/dist/src/roles/role-labels.js +1 -2
  294. package/dist/src/roles/role-render.js +67 -50
  295. package/dist/src/roles/role-view.js +59 -47
  296. package/dist/src/roles/roles.js +31 -31
  297. package/dist/src/roles/runner-ids.js +97 -0
  298. package/dist/src/run/acceptance-greps.js +119 -0
  299. package/dist/src/run/feedback-filings.js +51 -0
  300. package/dist/src/{tick → run}/qa-coverage.js +9 -27
  301. package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
  302. package/dist/src/run/run-detail-data.js +179 -0
  303. package/dist/src/run/run-detail-render.js +98 -0
  304. package/dist/src/run/run-detail.js +168 -0
  305. package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +72 -60
  306. package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
  307. package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +108 -73
  308. package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
  309. package/dist/src/{tick/tick-stage.js → run/run-stage.js} +108 -50
  310. package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
  311. package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
  312. package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +91 -56
  313. package/dist/src/run/selfcheck-digest.js +40 -0
  314. package/dist/src/run/stage-backlog-checks.js +249 -0
  315. package/dist/src/run/stage-check.js +172 -0
  316. package/dist/src/run/stage-path-checks.js +730 -0
  317. package/dist/src/{loop → runner}/leftover.js +80 -25
  318. package/dist/src/{loop → runner}/model-fallback.js +12 -12
  319. package/dist/src/runner/revision.js +202 -0
  320. package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
  321. package/dist/src/runner/runner-outcome.js +107 -0
  322. package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
  323. package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +21 -15
  324. package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +218 -240
  325. package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
  326. package/dist/src/runner/runner-state.js +102 -0
  327. package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
  328. package/dist/src/scheduling/backoff.js +38 -38
  329. package/dist/src/scheduling/claims.js +35 -22
  330. package/dist/src/scheduling/once-round.js +22 -22
  331. package/dist/src/scheduling/quiet-hours.js +5 -6
  332. package/dist/src/scheduling/scheduling.js +74 -64
  333. package/dist/src/scheduling/work-merged-cache.js +2 -2
  334. package/dist/src/status/status-budget.js +55 -47
  335. package/dist/src/status/status-data.js +43 -16
  336. package/dist/src/status/status-inbox.js +2 -2
  337. package/dist/src/status/status-install.js +0 -1
  338. package/dist/src/status/status-polls.js +28 -28
  339. package/dist/src/status/status-roles.js +39 -13
  340. package/dist/src/team/error-storm.js +8 -8
  341. package/dist/src/team/failure-spread.js +2 -2
  342. package/dist/src/team/orchestrator-info.js +15 -9
  343. package/dist/src/team/reclaim.js +5 -6
  344. package/dist/src/team/team-hold.js +7 -7
  345. package/dist/src/team/team-polls.js +91 -50
  346. package/dist/src/team/team-state.js +8 -2
  347. package/dist/src/text/datetime.js +63 -17
  348. package/dist/src/text/format.js +48 -11
  349. package/dist/src/text/markdown.js +8 -5
  350. package/dist/src/text/phrases.js +57 -23
  351. package/dist/src/text/suggest.js +1 -1
  352. package/dist/src/text/text.js +67 -13
  353. package/dist/src/ui/badges.js +65 -37
  354. package/dist/src/ui/gui/gui-client-backlog.js +292 -0
  355. package/dist/src/ui/gui/gui-client-boot.js +32 -26
  356. package/dist/src/ui/gui/gui-client-composer.js +8 -9
  357. package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
  358. package/dist/src/ui/gui/gui-client-drawer.js +126 -285
  359. package/dist/src/ui/gui/gui-client-feed.js +271 -0
  360. package/dist/src/ui/gui/gui-client-history.js +434 -67
  361. package/dist/src/ui/gui/gui-client-labels.js +1 -2
  362. package/dist/src/ui/gui/gui-client-markdown.js +1 -2
  363. package/dist/src/ui/gui/gui-client-model.js +27 -36
  364. package/dist/src/ui/gui/gui-client-operator.js +55 -51
  365. package/dist/src/ui/gui/gui-client-pending.js +227 -23
  366. package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
  367. package/dist/src/ui/gui/gui-client-report.js +87 -427
  368. package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
  369. package/dist/src/ui/gui/gui-client-settings.js +56 -4
  370. package/dist/src/ui/gui/gui-client-team.js +25 -465
  371. package/dist/src/ui/gui/gui-client.js +27 -25
  372. package/dist/src/ui/gui/gui-icons.js +0 -2
  373. package/dist/src/ui/gui/gui-page.js +9 -7
  374. package/dist/src/ui/gui/gui-styles.js +34 -34
  375. package/dist/src/ui/progress-data.js +9 -11
  376. package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +31 -31
  377. package/dist/src/ui/status-model.js +133 -125
  378. package/dist/src/ui/status-payload.js +136 -49
  379. package/dist/src/ui/status-render.js +101 -82
  380. package/dist/src/ui/team-alerts.js +248 -57
  381. package/dist/src/ui/tone.js +5 -6
  382. package/dist/src/ui/transcript-run.js +58 -0
  383. package/dist/src/ui/transcript-tail.js +28 -20
  384. package/dist/src/ui/transcript.js +63 -23
  385. package/dist/src/ui/tui/tui-app.js +0 -1
  386. package/dist/src/ui/tui/tui-backlog.js +51 -14
  387. package/dist/src/ui/tui/tui-editor-answer.js +27 -31
  388. package/dist/src/ui/tui/tui-editor-attach.js +3 -5
  389. package/dist/src/ui/tui/tui-editor-backends.js +132 -0
  390. package/dist/src/ui/tui/tui-editor-brief.js +10 -12
  391. package/dist/src/ui/tui/tui-editor-budget.js +35 -31
  392. package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
  393. package/dist/src/ui/tui/tui-editor-concurrency.js +24 -0
  394. package/dist/src/ui/tui/tui-editor-config.js +86 -0
  395. package/dist/src/ui/tui/tui-editor-entry.js +36 -47
  396. package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
  397. package/dist/src/ui/tui/tui-editor-instances.js +37 -0
  398. package/dist/src/ui/tui/tui-editor-instructions.js +136 -0
  399. package/dist/src/ui/tui/tui-editor-interval.js +11 -9
  400. package/dist/src/ui/tui/tui-editor-model.js +29 -0
  401. package/dist/src/ui/tui/tui-editor-notes.js +3 -5
  402. package/dist/src/ui/tui/tui-editor-notify.js +25 -0
  403. package/dist/src/ui/tui/tui-editor-pause.js +48 -59
  404. package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
  405. package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
  406. package/dist/src/ui/tui/tui-editor-queued.js +5 -9
  407. package/dist/src/ui/tui/tui-editor-quiet.js +99 -35
  408. package/dist/src/ui/tui/tui-editor-retire.js +4 -6
  409. package/dist/src/ui/tui/tui-editor-role-key.js +76 -0
  410. package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
  411. package/dist/src/ui/tui/tui-editor-search.js +45 -33
  412. package/dist/src/ui/tui/tui-editor-session.js +60 -34
  413. package/dist/src/ui/tui/tui-editor-slots.js +23 -0
  414. package/dist/src/ui/tui/tui-editors.js +35 -2
  415. package/dist/src/ui/tui/tui-frame.js +56 -11
  416. package/dist/src/ui/tui/tui-history.js +8 -9
  417. package/dist/src/ui/tui/tui-input.js +13 -11
  418. package/dist/src/ui/tui/tui-keymap.js +0 -1
  419. package/dist/src/ui/tui/tui-keys.js +24 -9
  420. package/dist/src/ui/tui/tui-pane-activity.js +14 -11
  421. package/dist/src/ui/tui/tui-pane-backlog.js +171 -126
  422. package/dist/src/ui/tui/tui-pane-brief.js +0 -1
  423. package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
  424. package/dist/src/ui/tui/tui-pane-health.js +58 -8
  425. package/dist/src/ui/tui/tui-pane-history.js +402 -82
  426. package/dist/src/ui/tui/tui-pane-pending.js +308 -13
  427. package/dist/src/ui/tui/tui-pane-queued.js +71 -55
  428. package/dist/src/ui/tui/tui-pane-report.js +46 -14
  429. package/dist/src/ui/tui/tui-pane-session.js +25 -6
  430. package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
  431. package/dist/src/ui/tui/tui-pane.js +80 -23
  432. package/dist/src/ui/tui/tui-panes.js +100 -30
  433. package/dist/src/ui/tui/tui.js +14 -14
  434. package/dist/src/verdict/fix-claim.js +82 -32
  435. package/dist/src/verdict/no-change.js +4 -4
  436. package/dist/src/verdict/refusal.js +40 -16
  437. package/dist/src/verdict/reply-contract.js +42 -31
  438. package/dist/src/version.js +0 -1
  439. package/package.json +2 -1
  440. package/dist/src/loop/revision.js +0 -70
  441. package/dist/src/roles/loop-ids.js +0 -77
  442. package/dist/src/tick/stage-check.js +0 -419
  443. package/dist/src/tick/telemetry-digest.js +0 -25
  444. package/dist/src/tick/tick-detail-data.js +0 -108
  445. package/dist/src/tick/tick-detail.js +0 -163
  446. package/dist/src/ui/gui/gui-client-sound.js +0 -53
@@ -1,32 +1,33 @@
1
- // vocabulary: plain
2
1
  /** Collection half of the usage report: read the harness's event log (through the shared
3
2
  * event-folding layer in src/report/report-fold.ts) and the backlog history over a window of
4
3
  * local calendar days and distill it into a `ReportData` / `SinceReport` — per-day
5
- * tick/token/commit/cost counts plus features-done and bugs-fixed tallies from PLANS.md and
4
+ * run/token/commit/cost counts plus features-done and bugs-fixed tallies from PLANS.md and
6
5
  * BUGS.md. The Markdown rendering of this data lives in src/report/report-render.ts, a pure
7
6
  * function of it; the split mirrors the failure report's (src/failure/failure-data.ts /
8
7
  * src/failure/failure-render.ts) and keeps "what happened" (window math, aggregation, bounds)
9
8
  * apart from "how it prints" (bars, cell wording), which change for different reasons — and
10
9
  * keeps core data collection out of the presentation layer, so a core consumer (as
11
10
  * /api/report already is) never forces a core→ui import. This file holds the two window
12
- * collectors and the report shapes they produce; the event-log folding and its memoized
13
- * per-window cache live in src/report/report-fold.ts (its own test, report-fold-cache). */
14
- import { assertReportSinceMs, eventWindowCovers, readWindowEvents } from "../events/event-window.js";
15
- import { DONE_DATE_RE, FIXED_DATE_RE, completionDate, finishedFiles, fixedBugEntries, sectionCompletionDates, sectionFilingDates, } from "../backlog/backlog.js";
11
+ * collectors; the report shapes they produce live in src/report/report-types.ts, and the
12
+ * event-log folding and its memoized per-window cache in src/report/report-fold.ts (its own
13
+ * test, report-fold-cache). */
14
+ import { assertReportSinceMs, eventWindowCovers, readWindowEvents, sinceWindowTiming } from "../events/event-window.js";
15
+ import { DONE_DATE_RE, FIXED_DATE_RE, completionDate, finishedFiles, fixedBugEntries, sectionCompletionDates, sectionCompletionLags, sectionFilingDates, } from "../backlog/backlog.js";
16
16
  import { validationGapTag } from "../backlog/backlog-md.js";
17
17
  import { dayAt, dayKey, dayWindow, formatDate } from "../text/datetime.js";
18
18
  import { addTo, lowerMedian } from "../collections.js";
19
- import { baseRoleOf } from "../roles/loop-ids.js";
20
19
  import { commitTier } from "../roles/roles.js";
21
- import { foldUsageEvent, foldWindowEvents, narrowToRole, roleSum, scopedByRole, zeroCommitCounts, zeroMergeCounts, } from "./report-fold.js";
22
- /** Aggregate team usage over the trailing `sinceMs` window ending now, from the event log
23
- * only (run_end/merged — see SinceReport for why the backlog files are not read). Kept a
20
+ import { foldUsageEvent, foldWindowEvents, narrowToRole, roleSum, scopeBases, scopedByRole, zeroCommitCounts, zeroMergeCounts, } from "./report-fold.js";
21
+ import { mainRedMinutesByDay } from "./report-main-red.js";
22
+ /** Aggregate team usage over the trailing `sinceMs` window ending now (or `untilMs` ago, which
23
+ * drops events at or after that end; `coversFullWindow` still judges the far edge
24
+ * only), from the event log only (run_end/merged — see SinceReport for why the backlog files
25
+ * are not read). Kept a
24
26
  * separate function rather than a `collectReport` variant flag: the day collector's
25
27
  * zero-filled series and backlog-file reads have no place in a window totals view. */
26
- export function collectReportSince(root, sinceMs, role) {
28
+ export function collectReportSince(root, sinceMs, role, untilMs) {
27
29
  assertReportSinceMs(sinceMs);
28
- const now = Date.now();
29
- const cutoff = now - sinceMs;
30
+ const { now, cutoff, end, slice } = sinceWindowTiming(sinceMs, untilMs);
30
31
  // The window key is the cutoff's local calendar day, from the shared dayKey helper
31
32
  // eventDayKey buckets events with, so the read's day keys cannot disagree with the ts
32
33
  // filter below; the day-keyed read may include earlier hours of that day, which the ts
@@ -40,11 +41,16 @@ export function collectReportSince(root, sinceMs, role) {
40
41
  rejectionsByRole: {},
41
42
  revisionsByRole: {},
42
43
  refusalsByRole: {},
44
+ revertsByRole: {},
43
45
  checkDurationsMs: [],
44
46
  checkFailures: 0,
45
47
  mergeWaitsMs: [],
48
+ mergeDurationsMs: [],
49
+ runDurationsMs: [],
46
50
  providerWaitsMs: [],
47
51
  slotWaitsMs: [],
52
+ checkWaitsMs: [],
53
+ reviewDurationsMs: [],
48
54
  failedRunsByRole: {},
49
55
  fallbacksByRole: {},
50
56
  imageReroutesByRole: {},
@@ -61,11 +67,12 @@ export function collectReportSince(root, sinceMs, role) {
61
67
  mergeFailedByReason: {},
62
68
  costByProvider: {},
63
69
  notionalByProvider: {},
70
+ notionalByBackend: {},
64
71
  runsByModel: {},
65
72
  deferredByReason: {},
66
73
  };
67
74
  for (const ev of raw.events) {
68
- if (typeof ev.ts !== "number" || ev.ts < cutoff || ev.ts > now)
75
+ if (typeof ev.ts !== "number" || ev.ts < cutoff || ev.ts > now || (untilMs !== undefined && ev.ts >= end))
69
76
  continue;
70
77
  foldUsageEvent(acc, ev);
71
78
  }
@@ -74,18 +81,19 @@ export function collectReportSince(root, sinceMs, role) {
74
81
  // history that never existed. When neither proof holds, the oldest retained event lies inside
75
82
  // the window and the render notes the possible truncation.
76
83
  const coversFullWindow = eventWindowCovers(raw, cutoff);
77
- // Role scope: every total derives from the fold's per-role maps for the named base role alone
84
+ // Role scope: every total derives from the fold's per-role maps for the named base roles alone
78
85
  // — merge usage (the merge slot's, not the role's) and the team-only fields stay out, so
79
86
  // a scoped --since report sums exactly the same events the scoped day report would. The scope
80
- // is the base role, so instances (`feature-2`) aggregate into their group as the cap does.
81
- const base = role === undefined ? undefined : baseRoleOf(role);
82
- const scopedTotals = base === undefined
87
+ // is the base roles, so instances (`feature-2`) aggregate into their group as the cap does.
88
+ const bases = scopeBases(role);
89
+ const scopedTotals = bases === undefined
83
90
  ? {
84
91
  tokensOut: acc.tokensOut + (acc.mergeTokens ?? 0),
85
92
  runs: acc.runs ?? 0,
86
93
  commits: acc.commits,
87
94
  costUsd: acc.costUsd + (acc.mergeCostUsd ?? 0),
88
95
  ...(acc.notionalCostUsd > 0 ? { notionalCostUsd: acc.notionalCostUsd } : {}),
96
+ ...(Object.keys(acc.notionalByBackend).length > 0 ? { notionalByBackend: acc.notionalByBackend } : {}),
89
97
  mergeRuns: acc.mergeRuns ?? 0,
90
98
  mergeTokens: acc.mergeTokens ?? 0,
91
99
  mergeCostUsd: acc.mergeCostUsd ?? 0,
@@ -94,12 +102,12 @@ export function collectReportSince(root, sinceMs, role) {
94
102
  : (() => {
95
103
  // The role's own notional spend, from its run_end events; the merge slot's
96
104
  // notional is team-wide and stays out like its cost.
97
- const notional = roleSum(acc.notionalByRole, base);
105
+ const notional = roleSum(acc.notionalByRole, bases);
98
106
  return {
99
- tokensOut: roleSum(acc.tokensByRole, base),
100
- runs: roleSum(acc.runsByRole, base),
101
- commits: roleSum(acc.commitsByRole, base),
102
- costUsd: roleSum(acc.costByRole, base),
107
+ tokensOut: roleSum(acc.tokensByRole, bases),
108
+ runs: roleSum(acc.runsByRole, bases),
109
+ commits: roleSum(acc.commitsByRole, bases),
110
+ costUsd: roleSum(acc.costByRole, bases),
103
111
  ...(notional > 0 ? { notionalCostUsd: notional } : {}),
104
112
  ...zeroMergeCounts(),
105
113
  };
@@ -108,49 +116,67 @@ export function collectReportSince(root, sinceMs, role) {
108
116
  // notional spend, scoped to base when a role is given — the same conditional the day
109
117
  // collector applies to ReportDay.notionalByRole (line ~573), so a role-scoped window
110
118
  // whose role spent no notional carries no notionalByRole instead of an empty map.
111
- const notionalByRole = scopedByRole(acc.notionalByRole, base);
119
+ const notionalByRole = scopedByRole(acc.notionalByRole, bases);
112
120
  // Deferred runs are bucketed by a reason code (not a role), so a role-scoped window
113
121
  // carries no entries — the same team-only rule runsByModel follows. Only set when the
114
122
  // window actually deferred (zero means absent), so a deferral-free --since JSON omits it.
115
- const deferred = scopedByRole(acc.deferredByReason, base);
123
+ const deferred = scopedByRole(acc.deferredByReason, bases);
116
124
  return {
117
- sinceMs,
118
- fromIso: new Date(cutoff).toISOString(),
119
- ...(base === undefined ? {} : { role: base }),
125
+ ...slice,
126
+ ...(bases === undefined ? {} : { role: bases.join(",") }),
120
127
  totals: scopedTotals,
121
- runsByRole: scopedByRole(acc.runsByRole, base),
122
- costByRole: scopedByRole(acc.costByRole, base),
123
- rejectionsByRole: scopedByRole(acc.rejectionsByRole, base),
124
- revisionsByRole: scopedByRole(acc.revisionsByRole, base),
125
- refusalsByRole: scopedByRole(acc.refusalsByRole, base),
126
- failedRunsByRole: scopedByRole(acc.failedRunsByRole, base),
127
- fallbacksByRole: scopedByRole(acc.fallbacksByRole, base),
128
- imageReroutesByRole: scopedByRole(acc.imageReroutesByRole, base),
129
- tokensByRole: scopedByRole(acc.tokensByRole, base),
130
- commitsByRole: scopedByRole(acc.commitsByRole, base),
131
- mergedByRole: scopedByRole(acc.mergedByRole, base),
132
- mergeFailedByRole: scopedByRole(acc.mergeFailedByRole, base),
128
+ runsByRole: scopedByRole(acc.runsByRole, bases),
129
+ costByRole: scopedByRole(acc.costByRole, bases),
130
+ rejectionsByRole: scopedByRole(acc.rejectionsByRole, bases),
131
+ revisionsByRole: scopedByRole(acc.revisionsByRole, bases),
132
+ refusalsByRole: scopedByRole(acc.refusalsByRole, bases),
133
+ revertsByRole: scopedByRole(acc.revertsByRole, bases),
134
+ failedRunsByRole: scopedByRole(acc.failedRunsByRole, bases),
135
+ fallbacksByRole: scopedByRole(acc.fallbacksByRole, bases),
136
+ imageReroutesByRole: scopedByRole(acc.imageReroutesByRole, bases),
137
+ tokensByRole: scopedByRole(acc.tokensByRole, bases),
138
+ commitsByRole: scopedByRole(acc.commitsByRole, bases),
139
+ mergedByRole: scopedByRole(acc.mergedByRole, bases),
140
+ mergeFailedByRole: scopedByRole(acc.mergeFailedByRole, bases),
133
141
  // A result code is not a role, so a role-scoped window carries an empty map (a code
134
142
  // never baseRoleOf to the role) — the render gates the reason line to the team report.
135
- mergeFailedByReason: scopedByRole(acc.mergeFailedByReason, base),
143
+ mergeFailedByReason: scopedByRole(acc.mergeFailedByReason, bases),
136
144
  ...(Object.keys(notionalByRole).length > 0 ? { notionalByRole } : {}),
137
145
  // Provider spend is a team attribution (a provider is not a role), so a role-scoped
138
146
  // window carries empty maps — providers never baseRoleOf to the role — and the render
139
147
  // gates the provider lines to the team report anyway. Always present, empty when none.
140
- costByProvider: scopedByRole(acc.costByProvider, base),
141
- notionalByProvider: scopedByRole(acc.notionalByProvider, base),
148
+ costByProvider: scopedByRole(acc.costByProvider, bases),
149
+ notionalByProvider: scopedByRole(acc.notionalByProvider, bases),
150
+ // Backend spend is a team attribution (a backend is not a role), so a role-scoped window
151
+ // omits it — the same absent-when-empty rule the day collector applies.
152
+ ...(Object.keys(scopedByRole(acc.notionalByBackend, bases)).length > 0
153
+ ? { notionalByBackend: scopedByRole(acc.notionalByBackend, bases) }
154
+ : {}),
142
155
  // Model attribution is team-only (a model is not a role, so no model folds to the
143
156
  // scoped role), the same full-map-for-team / empty-for-scoped rule costByProvider
144
157
  // follows. Always present so the render can gate on the window's own events.
145
- runsByModel: scopedByRole(acc.runsByModel, base),
158
+ runsByModel: scopedByRole(acc.runsByModel, bases),
146
159
  ...(Object.keys(deferred).length > 0 ? { deferredByReason: deferred } : {}),
160
+ // Main's red time is main's own (G1's rule), not any role's, so a role-scoped window
161
+ // carries 0 — the type's always-present default for the team-only field.
162
+ mainRedMs: bases === undefined
163
+ ? [...mainRedMinutesByDay(raw.events, cutoff, end).values()].reduce((a, b) => a + b, 0)
164
+ : 0,
147
165
  coversFullWindow,
148
166
  };
149
167
  }
168
+ /** Set one per-day median cell only when the day actually has samples (`ms` non-empty), so a
169
+ * medians-free day keeps its exact pre-feature shape (the field stays absent). The single home
170
+ * of that guard — the day collector's eight `…MedianMs` cells all followed it inline before. */
171
+ function setLowerMedian(day, key, ms) {
172
+ if (ms.length > 0)
173
+ day[key] = lowerMedian(ms);
174
+ }
150
175
  /** Aggregate team usage over exactly `days` local calendar days ending today, from the event
151
176
  * log (run_end/merged) and the backlog history (`## Done` in PLANS.md and PLANS-DONE.md,
152
177
  * `## Fixed` in BUGS.md and BUGS-FIXED.md). */
153
178
  export function collectReport(root, days, role) {
179
+ const bases = scopeBases(role);
154
180
  const now = new Date();
155
181
  // One captured instant for the whole window, so every day key derives from the same day.
156
182
  const { from, to } = dayWindow(days, now);
@@ -175,7 +201,7 @@ export function collectReport(root, days, role) {
175
201
  const day = byDate.get(dayKey);
176
202
  if (!day)
177
203
  continue; // Out-of-window day key (e.g. future-dated) — no series slot to fill.
178
- if (role === undefined) {
204
+ if (bases === undefined) {
179
205
  day.tokensOut = fold.tokensOut;
180
206
  day.runsByRole = fold.runsByRole;
181
207
  day.tokensByRole = fold.tokensByRole;
@@ -187,19 +213,23 @@ export function collectReport(root, days, role) {
187
213
  day.revisionsByRole = fold.revisionsByRole;
188
214
  if (Object.keys(fold.refusalsByRole).length > 0)
189
215
  day.refusalsByRole = fold.refusalsByRole;
216
+ if (Object.keys(fold.revertsByRole).length > 0)
217
+ day.revertsByRole = fold.revertsByRole;
190
218
  // Checks belong to the merge slot, so only the team series carries the median.
191
- if (fold.checkDurationsMs.length > 0) {
192
- day.checkMedianMs = lowerMedian(fold.checkDurationsMs);
193
- }
219
+ setLowerMedian(day, "checkMedianMs", fold.checkDurationsMs);
194
220
  if (fold.checkFailures > 0)
195
221
  day.checkFailures = fold.checkFailures;
196
- if (fold.mergeWaitsMs.length > 0)
197
- day.mergeWaitMedianMs = lowerMedian(fold.mergeWaitsMs);
198
- if (fold.providerWaitsMs.length > 0)
199
- day.providerWaitMedianMs = lowerMedian(fold.providerWaitsMs);
222
+ setLowerMedian(day, "mergeWaitMedianMs", fold.mergeWaitsMs);
223
+ setLowerMedian(day, "mergeSlotMedianMs", fold.mergeDurationsMs);
224
+ setLowerMedian(day, "runMedianMs", fold.runDurationsMs);
225
+ setLowerMedian(day, "providerWaitMedianMs", fold.providerWaitsMs);
200
226
  // A workspace wait is the pool's, not a role's, so only the team series carries it.
201
- if (fold.slotWaitsMs.length > 0)
202
- day.slotWaitMedianMs = lowerMedian(fold.slotWaitsMs);
227
+ setLowerMedian(day, "slotWaitMedianMs", fold.slotWaitsMs);
228
+ // A check permit is the team's, not a role's, so only the team series carries it.
229
+ setLowerMedian(day, "checkWaitMedianMs", fold.checkWaitsMs);
230
+ // A review belongs to the merge slot/reviewer, not the author role, so only the team
231
+ // series carries the median, mirroring checkMedianMs.
232
+ setLowerMedian(day, "reviewMedianMs", fold.reviewDurationsMs);
203
233
  // Only set when nonzero, so a failure-free day keeps its exact pre-feature shape.
204
234
  if (Object.keys(fold.failedRunsByRole).length > 0)
205
235
  day.failedRunsByRole = fold.failedRunsByRole;
@@ -223,6 +253,11 @@ export function collectReport(root, days, role) {
223
253
  day.costByProvider = fold.costByProvider;
224
254
  if (Object.keys(fold.notionalByProvider).length > 0)
225
255
  day.notionalByProvider = fold.notionalByProvider;
256
+ // Backends are a team attribution (a backend is not a role), so only the team series
257
+ // carries the map; only set when a backend actually spent (zero means absent), so a
258
+ // backend-free day keeps its exact pre-feature shape.
259
+ if (Object.keys(fold.notionalByBackend).length > 0)
260
+ day.notionalByBackend = fold.notionalByBackend;
226
261
  // Models are a team attribution (a model is not a role), so only the team series
227
262
  // carries the map; only set when a modelled start happened (zero means absent), so a
228
263
  // model-less day keeps its exact pre-feature shape.
@@ -254,53 +289,55 @@ export function collectReport(root, days, role) {
254
289
  }
255
290
  else {
256
291
  // Role scope: derive this day from the fold's per-role maps, so the series carries only
257
- // the named role's ticks/tokens/cost and (base-role-keyed) commits. The scope is the
292
+ // the named role's runs/tokens/cost and (base-role-keyed) commits. The scope is the
258
293
  // base role, so an instance id's usage and its siblings' commits fold into one group and
259
294
  // the numbers match the per-role cap, which is base-role keyed too. The merge fields
260
295
  // stay unset — merge runs are the merge slot's, not this role's — and the work /
261
296
  // maintenance split follows the base role's own tier rather than the day's whole split.
262
- const base = baseRoleOf(role);
263
- const commits = roleSum(fold.commitsByRole, base);
264
- const tier = commitTier(base);
265
- day.tokensOut = roleSum(fold.tokensByRole, base);
266
- day.runsByRole = narrowToRole(fold.runsByRole, base);
267
- day.tokensByRole = narrowToRole(fold.tokensByRole, base);
268
- day.costByRole = narrowToRole(fold.costByRole, base);
269
- const rejs = narrowToRole(fold.rejectionsByRole, base);
297
+ const commits = roleSum(fold.commitsByRole, bases);
298
+ day.tokensOut = roleSum(fold.tokensByRole, bases);
299
+ day.runsByRole = narrowToRole(fold.runsByRole, bases);
300
+ day.tokensByRole = narrowToRole(fold.tokensByRole, bases);
301
+ day.costByRole = narrowToRole(fold.costByRole, bases);
302
+ const rejs = narrowToRole(fold.rejectionsByRole, bases);
270
303
  if (Object.keys(rejs).length > 0)
271
304
  day.rejectionsByRole = rejs;
272
- const revs = narrowToRole(fold.revisionsByRole, base);
305
+ const revs = narrowToRole(fold.revisionsByRole, bases);
273
306
  if (Object.keys(revs).length > 0)
274
307
  day.revisionsByRole = revs;
275
- const refusals = narrowToRole(fold.refusalsByRole, base);
308
+ const refusals = narrowToRole(fold.refusalsByRole, bases);
276
309
  if (Object.keys(refusals).length > 0)
277
310
  day.refusalsByRole = refusals;
278
- const fails = narrowToRole(fold.failedRunsByRole, base);
311
+ const reverts = narrowToRole(fold.revertsByRole, bases);
312
+ if (Object.keys(reverts).length > 0)
313
+ day.revertsByRole = reverts;
314
+ const fails = narrowToRole(fold.failedRunsByRole, bases);
279
315
  if (Object.keys(fails).length > 0)
280
316
  day.failedRunsByRole = fails;
281
- const falls = narrowToRole(fold.fallbacksByRole, base);
317
+ const falls = narrowToRole(fold.fallbacksByRole, bases);
282
318
  if (Object.keys(falls).length > 0)
283
319
  day.fallbacksByRole = falls;
284
- const reroutes = narrowToRole(fold.imageReroutesByRole, base);
320
+ const reroutes = narrowToRole(fold.imageReroutesByRole, bases);
285
321
  if (Object.keys(reroutes).length > 0)
286
322
  day.imageReroutesByRole = reroutes;
287
323
  day.commits = commits;
288
- day.commitsByRole = narrowToRole(fold.commitsByRole, base);
289
- day.workCommits = tier === "work" ? commits : 0;
290
- day.maintenanceCommits = tier === "maintenance" ? commits : 0;
291
- day.costUsd = roleSum(fold.costByRole, base);
292
- const notional = roleSum(fold.notionalByRole, base);
324
+ day.commitsByRole = narrowToRole(fold.commitsByRole, bases);
325
+ const tierCommits = (tier) => bases.filter((b) => commitTier(b) === tier).reduce((n, b) => n + roleSum(fold.commitsByRole, b), 0);
326
+ day.workCommits = tierCommits("work");
327
+ day.maintenanceCommits = tierCommits("maintenance");
328
+ day.costUsd = roleSum(fold.costByRole, bases);
329
+ const notional = roleSum(fold.notionalByRole, bases);
293
330
  if (notional > 0) {
294
331
  day.notionalCostUsd = notional;
295
- day.notionalByRole = narrowToRole(fold.notionalByRole, base);
332
+ day.notionalByRole = narrowToRole(fold.notionalByRole, bases);
296
333
  }
297
334
  // Merge runs belong to the merge slot, not this role, so the role-scoped series
298
335
  // keeps them unset (matching the merge-share fields) — the render omits the merge
299
336
  // line for a scoped report, so these are carried only for shape consistency.
300
- const mergeDone = narrowToRole(fold.mergedByRole, base);
337
+ const mergeDone = narrowToRole(fold.mergedByRole, bases);
301
338
  if (Object.keys(mergeDone).length > 0)
302
339
  day.mergedByRole = mergeDone;
303
- const mergeDoneFailed = narrowToRole(fold.mergeFailedByRole, base);
340
+ const mergeDoneFailed = narrowToRole(fold.mergeFailedByRole, bases);
304
341
  if (Object.keys(mergeDoneFailed).length > 0)
305
342
  day.mergeFailedByRole = mergeDoneFailed;
306
343
  }
@@ -312,6 +349,19 @@ export function collectReport(root, days, role) {
312
349
  d.tokensOut += d.mergeTokens ?? 0;
313
350
  d.costUsd += d.mergeCostUsd ?? 0;
314
351
  }
352
+ // Main's red wall-clock per day, from G1's rule (main_red_hold / failed ran main_check open a
353
+ // spell, a passing ran main_check closes it, an open spell runs to now). Team-only: main's
354
+ // red time is main's own, not any role's, so a role-scoped series leaves every day unset —
355
+ // exactly as checkFailures is team-only. Only set when a day actually was red (zero-absent,
356
+ // like checkMedianMs).
357
+ if (bases === undefined) {
358
+ const redByDay = mainRedMinutesByDay(foldEntry.redEvents, dayAt(days - 1, now).getTime(), now.getTime());
359
+ for (const d of series) {
360
+ const ms = redByDay.get(d.date);
361
+ if (ms !== undefined)
362
+ d.mainRedMs = ms;
363
+ }
364
+ }
315
365
  const countOn = (date, field) => {
316
366
  const day = byDate.get(date);
317
367
  if (day)
@@ -366,6 +416,36 @@ export function collectReport(root, days, role) {
366
416
  }
367
417
  }
368
418
  }
419
+ // The per-day backlog lag median: how long each finished plan or bug waited from its filing
420
+ // stamp to its done/fixed stamp, folded per completion day — the median backlog age at
421
+ // which the day's items closed. Team-only (a lag has no role, like the validation tally
422
+ // and the filing stamps above) and zero-means-absent: a day with no lag-carrying completion
423
+ // keeps its exact pre-feature shape, and an out-of-window completion date drops out (it
424
+ // must be a byDate key, the same drop countOn applies). The same entry detection and date
425
+ // rules (sectionCompletionLags / entryLags) the completion and filing reads use, so the
426
+ // spans pair the days `featuresDone`/`bugsFixed` count.
427
+ if (role === undefined) {
428
+ const lagsByDay = new Map();
429
+ const foldLag = (date, lagDays) => {
430
+ const list = lagsByDay.get(date);
431
+ if (list)
432
+ list.push(lagDays);
433
+ else
434
+ lagsByDay.set(date, [lagDays]);
435
+ };
436
+ for (const file of finishedFiles("PLANS.md")) {
437
+ for (const { date, lagDays } of sectionCompletionLags(root, file, "Done", DONE_DATE_RE))
438
+ if (byDate.has(date))
439
+ foldLag(date, lagDays);
440
+ }
441
+ for (const file of finishedFiles("BUGS.md")) {
442
+ for (const { date, lagDays } of sectionCompletionLags(root, file, "Fixed", FIXED_DATE_RE))
443
+ if (byDate.has(date))
444
+ foldLag(date, lagDays);
445
+ }
446
+ for (const [date, lags] of lagsByDay)
447
+ byDate.get(date).backlogLagMedianDays = lowerMedian(lags);
448
+ }
369
449
  const totals = {
370
450
  tokensOut: 0,
371
451
  runs: 0,
@@ -383,6 +463,7 @@ export function collectReport(root, days, role) {
383
463
  let mergeFailed = 0;
384
464
  let plansAdded = 0;
385
465
  let bugsFiled = 0;
466
+ const notionalByBackend = {};
386
467
  for (const d of series) {
387
468
  totals.tokensOut += d.tokensOut;
388
469
  for (const n of Object.values(d.runsByRole))
@@ -394,6 +475,8 @@ export function collectReport(root, days, role) {
394
475
  totals.maintenanceCommits += d.maintenanceCommits ?? 0;
395
476
  totals.costUsd += d.costUsd;
396
477
  notionalCostUsd += d.notionalCostUsd ?? 0;
478
+ for (const [backend, c] of Object.entries(d.notionalByBackend ?? {}))
479
+ addTo(notionalByBackend, backend, c);
397
480
  totals.featuresDone += d.featuresDone;
398
481
  totals.bugsFixed += d.bugsFixed;
399
482
  plansAdded += d.plansAdded ?? 0;
@@ -419,11 +502,12 @@ export function collectReport(root, days, role) {
419
502
  days,
420
503
  from,
421
504
  to,
422
- ...(role === undefined ? {} : { role: baseRoleOf(role) }),
505
+ ...(bases === undefined ? {} : { role: bases.join(",") }),
423
506
  series,
424
507
  totals: {
425
508
  ...totals,
426
509
  ...(notionalCostUsd > 0 ? { notionalCostUsd } : {}),
510
+ ...(Object.keys(notionalByBackend).length > 0 ? { notionalByBackend } : {}),
427
511
  ...(mergeFailed > 0 ? { mergeFailed } : {}),
428
512
  ...(plansAdded > 0 ? { plansAdded } : {}),
429
513
  ...(bugsFiled > 0 ? { bugsFiled } : {}),