tumwater 0.2.0 → 0.4.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 (464) 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 +55 -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 +6 -4
  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 +404 -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 +281 -55
  41. package/dist/src/backlog/backlog-cli-view.js +255 -0
  42. package/dist/src/backlog/backlog-cli.js +91 -335
  43. package/dist/src/backlog/backlog-commit.js +20 -13
  44. package/dist/src/backlog/backlog-eligibility.js +193 -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 +264 -0
  48. package/dist/src/backlog/backlog-release.js +134 -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-guardrails.js +136 -0
  56. package/dist/src/baseline/main-red.js +153 -36
  57. package/dist/src/baseline/main-watch.js +232 -0
  58. package/dist/src/brief/brief-edit.js +12 -12
  59. package/dist/src/brief/brief.js +31 -51
  60. package/dist/src/budget/budget-fallback-view.js +288 -0
  61. package/dist/src/budget/budget-wiring.js +158 -0
  62. package/dist/src/budget/budget.js +31 -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 +65 -3
  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 +181 -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 +183 -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 +291 -598
  82. package/dist/src/cli/cli-repo-gate.js +11 -1
  83. package/dist/src/cli/cli-review-commands.js +113 -0
  84. package/dist/src/cli/cli-role-command.js +525 -0
  85. package/dist/src/cli/cli-start-summary.js +167 -0
  86. package/dist/src/cli/{cli-run.js → cli-start.js} +193 -237
  87. package/dist/src/cli/completion.js +169 -22
  88. package/dist/src/cli/config-commands.js +58 -26
  89. package/dist/src/cli/follow-view.js +75 -0
  90. package/dist/src/cli/help.js +258 -82
  91. package/dist/src/cli/log-commands.js +164 -85
  92. package/dist/src/cli/question-commands.js +211 -10
  93. package/dist/src/cli.js +173 -45
  94. package/dist/src/collections.js +56 -13
  95. package/dist/src/concurrency/check-permit.js +20 -3
  96. package/dist/src/concurrency/lock.js +17 -13
  97. package/dist/src/concurrency/provider-lease.js +44 -8
  98. package/dist/src/concurrency/semaphore.js +102 -26
  99. package/dist/src/config/config-aliases.js +0 -1
  100. package/dist/src/config/config-editable-keys.js +16 -1
  101. package/dist/src/config/config-fallbacks.js +94 -56
  102. package/dist/src/config/config-field-checks.js +40 -10
  103. package/dist/src/config/config-keys.js +30 -8
  104. package/dist/src/config/config-live.js +63 -24
  105. package/dist/src/config/config-request.js +137 -0
  106. package/dist/src/config/config-schema.js +31 -24
  107. package/dist/src/config/config-selector-checks.js +123 -0
  108. package/dist/src/config/config-validation-agents.js +80 -0
  109. package/dist/src/config/config-validation-model.js +116 -0
  110. package/dist/src/config/config-validation.js +169 -222
  111. package/dist/src/config/config-views.js +136 -54
  112. package/dist/src/config/config-write-core.js +135 -0
  113. package/dist/src/config/config-write.js +164 -265
  114. package/dist/src/config/config.js +53 -38
  115. package/dist/src/config/model-selector.js +34 -34
  116. package/dist/src/doctor/doctor-backlog-bugs.js +330 -0
  117. package/dist/src/doctor/doctor-backlog-plans.js +398 -0
  118. package/dist/src/doctor/doctor-backlog.js +249 -512
  119. package/dist/src/doctor/doctor-check-names.js +85 -0
  120. package/dist/src/doctor/doctor-checks.js +61 -593
  121. package/dist/src/doctor/doctor-claude-checks.js +142 -0
  122. package/dist/src/doctor/doctor-codex-checks.js +276 -0
  123. package/dist/src/doctor/doctor-contention-checks.js +506 -0
  124. package/dist/src/doctor/doctor-fallback-checks.js +130 -0
  125. package/dist/src/doctor/doctor-holds.js +80 -66
  126. package/dist/src/doctor/doctor-install.js +2 -1
  127. package/dist/src/doctor/doctor-model-checks.js +216 -537
  128. package/dist/src/doctor/doctor-notify.js +9 -5
  129. package/dist/src/doctor/doctor-opencode-checks.js +164 -0
  130. package/dist/src/doctor/doctor-orphans.js +182 -63
  131. package/dist/src/doctor/doctor-pi-checks.js +62 -0
  132. package/dist/src/doctor/doctor-project-checks.js +397 -0
  133. package/dist/src/doctor/doctor.js +126 -77
  134. package/dist/src/doctor/image-probe.js +21 -8
  135. package/dist/src/doctor/outcome.js +3 -2
  136. package/dist/src/doctor/probe.js +21 -12
  137. package/dist/src/events/event-format.js +185 -89
  138. package/dist/src/events/event-labels.js +11 -5
  139. package/dist/src/events/event-legacy.js +39 -15
  140. package/dist/src/events/event-read.js +52 -39
  141. package/dist/src/events/event-tally.js +16 -0
  142. package/dist/src/events/event-window.js +80 -26
  143. package/dist/src/events/events.js +30 -13
  144. package/dist/src/events/notify.js +7 -7
  145. package/dist/src/failure/failure-cluster.js +12 -12
  146. package/dist/src/failure/failure-data.js +65 -60
  147. package/dist/src/failure/failure-render.js +29 -21
  148. package/dist/src/failure/failure-state-change.js +17 -12
  149. package/dist/src/failure/time-spend.js +35 -35
  150. package/dist/src/files/file-queue.js +17 -0
  151. package/dist/src/files/files.js +111 -35
  152. package/dist/src/files/json-files.js +3 -3
  153. package/dist/src/files/json-object.js +28 -7
  154. package/dist/src/files/stat-cache.js +2 -2
  155. package/dist/src/files/tail.js +1 -1
  156. package/dist/src/gates/bootstrap-gates.js +4 -5
  157. package/dist/src/gates/budget-gates.js +88 -225
  158. package/dist/src/gates/disk-gate.js +1 -1
  159. package/dist/src/gates/gate-polls.js +101 -143
  160. package/dist/src/gates/maintenance-quota.js +37 -30
  161. package/dist/src/gates/pause-gates.js +28 -10
  162. package/dist/src/gates/readiness.js +7 -3
  163. package/dist/src/gates/role-cap-gates.js +8 -8
  164. package/dist/src/gates/startup-gate.js +94 -32
  165. package/dist/src/gates/streak-gate.js +54 -30
  166. package/dist/src/git/commit-message.js +89 -46
  167. package/dist/src/git/git-diff.js +16 -10
  168. package/dist/src/git/git-run.js +79 -38
  169. package/dist/src/git/git.js +54 -34
  170. package/dist/src/git/slot-salvage.js +253 -0
  171. package/dist/src/git/slots-state.js +6 -6
  172. package/dist/src/git/worktree-pool.js +132 -38
  173. package/dist/src/git/worktree-use.js +4 -11
  174. package/dist/src/git/worktree.js +94 -37
  175. package/dist/src/gui/gui-admin-endpoints.js +96 -0
  176. package/dist/src/gui/gui-args.js +79 -8
  177. package/dist/src/gui/gui-backlog-commands.js +43 -19
  178. package/dist/src/gui/gui-backlog-endpoints.js +156 -0
  179. package/dist/src/gui/gui-command.js +26 -13
  180. package/dist/src/gui/gui-config-commands.js +168 -80
  181. package/dist/src/gui/gui-endpoint-commands.js +5 -4
  182. package/dist/src/gui/gui-endpoint-common.js +4 -4
  183. package/dist/src/gui/gui-endpoints.js +11 -380
  184. package/dist/src/gui/gui-history-endpoints.js +301 -0
  185. package/dist/src/gui/gui-operator-commands.js +235 -13
  186. package/dist/src/gui/gui-prompt-commands.js +26 -20
  187. package/dist/src/gui/gui-report-endpoints.js +97 -0
  188. package/dist/src/gui/gui-server.js +26 -5
  189. package/dist/src/gui/gui-team-commands.js +7 -7
  190. package/dist/src/gui/http-body.js +1 -1
  191. package/dist/src/history/history-data.js +122 -234
  192. package/dist/src/history/history-queued.js +155 -0
  193. package/dist/src/history/history-render.js +42 -22
  194. package/dist/src/history/history.js +190 -49
  195. package/dist/src/inbox/inbox-attachments.js +29 -30
  196. package/dist/src/inbox/inbox-cancel.js +10 -11
  197. package/dist/src/inbox/inbox-edit.js +9 -9
  198. package/dist/src/inbox/inbox-submit.js +18 -18
  199. package/dist/src/inbox/inbox-top.js +28 -17
  200. package/dist/src/inbox/inbox.js +42 -43
  201. package/dist/src/inbox/pending-prompt.js +26 -26
  202. package/dist/src/inbox/prompt-commands.js +62 -49
  203. package/dist/src/inbox/prompt-not-before.js +2 -2
  204. package/dist/src/init/init-templates.js +1 -2
  205. package/dist/src/init/init.js +28 -23
  206. package/dist/src/merge/backlog-conflicts.js +215 -35
  207. package/dist/src/merge/merge-batch.js +14 -8
  208. package/dist/src/merge/merge-cancel.js +53 -0
  209. package/dist/src/merge/merge-check-failures.js +44 -22
  210. package/dist/src/merge/merge-core.js +58 -32
  211. package/dist/src/merge/merge-diff.js +5 -4
  212. package/dist/src/merge/merge-drain.js +45 -19
  213. package/dist/src/merge/merge-git.js +177 -38
  214. package/dist/src/merge/merge-pipeline.js +15 -3
  215. package/dist/src/merge/merge-post-review.js +214 -0
  216. package/dist/src/merge/merge-queue-render.js +48 -11
  217. package/dist/src/merge/merge-queue.js +52 -15
  218. package/dist/src/merge/merge-requeue.js +103 -0
  219. package/dist/src/merge/merge-slot.js +61 -16
  220. package/dist/src/merge/merge-stack.js +27 -11
  221. package/dist/src/merge/merge-to-main.js +77 -26
  222. package/dist/src/merge/merge-top.js +83 -0
  223. package/dist/src/merge/merge-verify.js +75 -18
  224. package/dist/src/notes-cli.js +35 -15
  225. package/dist/src/operator/operator-commands.js +192 -81
  226. package/dist/src/operator/operator-intent.js +69 -42
  227. package/dist/src/operator/operator-requests.js +35 -40
  228. package/dist/src/operator/retire.js +5 -6
  229. package/dist/src/operator/revert.js +142 -34
  230. package/dist/src/orchestrator/orchestrator-launch.js +60 -52
  231. package/dist/src/orchestrator/orchestrator-poll.js +352 -0
  232. package/dist/src/orchestrator/orchestrator-scheduling.js +71 -370
  233. package/dist/src/orchestrator/orchestrator.js +129 -369
  234. package/dist/src/orchestrator/retention.js +42 -8
  235. package/dist/src/orchestrator/scheduling-admit.js +116 -0
  236. package/dist/src/orchestrator/scheduling-scope.js +245 -0
  237. package/dist/src/orchestrator/scheduling-skip.js +195 -0
  238. package/dist/src/orchestrator/startup-migration-steps.js +8 -7
  239. package/dist/src/orchestrator/startup-migrations.js +11 -7
  240. package/dist/src/paths.js +126 -49
  241. package/dist/src/pi/command-shape.js +12 -1
  242. package/dist/src/pi/pi-args.js +6 -4
  243. package/dist/src/pi/pi-backend.js +6 -6
  244. package/dist/src/pi/pi-bin.js +54 -63
  245. package/dist/src/pi/pi-listed-models.js +53 -0
  246. package/dist/src/pi/pi-models.js +63 -30
  247. package/dist/src/pi/pi-run-result.js +1 -1
  248. package/dist/src/pi/pi-settings.js +1 -1
  249. package/dist/src/pi/pi-stream.js +6 -6
  250. package/dist/src/pi/pi-watchdogs.js +18 -19
  251. package/dist/src/pi-extension/bounded-output.js +5 -3
  252. package/dist/src/pi-extension/context-budget.js +2 -2
  253. package/dist/src/pi-extension/context-shake.js +1 -1
  254. package/dist/src/pi-extension/context-window.js +0 -1
  255. package/dist/src/pi-extension/full-output.js +3 -10
  256. package/dist/src/pi-extension/strip-thinking.js +0 -1
  257. package/dist/src/pi-extension/trim-system-prompt.js +3 -4
  258. package/dist/src/pi-extension/write-guard.js +37 -0
  259. package/dist/src/process/launch-services.js +1 -2
  260. package/dist/src/process/node-bin.js +16 -0
  261. package/dist/src/process/process-group.js +8 -18
  262. package/dist/src/process/process.js +41 -2
  263. package/dist/src/process/run-marker.js +26 -6
  264. package/dist/src/process/supervisor.js +22 -21
  265. package/dist/src/prompt/dialect.js +17 -2
  266. package/dist/src/prompt/gate-notes.js +203 -0
  267. package/dist/src/prompt/gate-prompts.js +150 -217
  268. package/dist/src/prompt/notes-backlog.js +184 -0
  269. package/dist/src/prompt/principles.js +1 -2
  270. package/dist/src/prompt/prompt-followup.js +19 -14
  271. package/dist/src/prompt/prompt.js +131 -204
  272. package/dist/src/redeploy/redeploy-policy.js +24 -19
  273. package/dist/src/redeploy/redeploy-probes.js +1 -1
  274. package/dist/src/redeploy/redeploy.js +2 -3
  275. package/dist/src/redeploy/redeployer.js +20 -20
  276. package/dist/src/redeploy/self-reload.js +2 -1
  277. package/dist/src/report/report-data.js +188 -124
  278. package/dist/src/report/report-fold.js +130 -54
  279. package/dist/src/report/report-main-red.js +55 -0
  280. package/dist/src/report/report-render.js +136 -65
  281. package/dist/src/report/report-types.js +1 -0
  282. package/dist/src/report/report.js +23 -9
  283. package/dist/src/request-timeouts.js +2 -2
  284. package/dist/src/review/diff-rules.js +53 -0
  285. package/dist/src/review/known-flakes.js +112 -1
  286. package/dist/src/review/post-merge-review-accept.js +51 -0
  287. package/dist/src/review/post-merge-review-render.js +112 -0
  288. package/dist/src/review/post-merge-review.js +239 -0
  289. package/dist/src/review/protected-edits.js +24 -10
  290. package/dist/src/review/quarantine-render.js +65 -0
  291. package/dist/src/review/review-followup.js +8 -8
  292. package/dist/src/review/review-precheck.js +66 -23
  293. package/dist/src/review/review-run.js +395 -0
  294. package/dist/src/review/review-verdict.js +2 -2
  295. package/dist/src/review/review.js +180 -401
  296. package/dist/src/review/secret-leaks.js +36 -16
  297. package/dist/src/review/suite-rerun.js +0 -1
  298. package/dist/src/roles/legacy-role-migration.js +80 -7
  299. package/dist/src/roles/role-catalog.js +127 -12
  300. package/dist/src/roles/role-guidance.js +65 -20
  301. package/dist/src/roles/role-labels.js +7 -5
  302. package/dist/src/roles/role-render.js +108 -56
  303. package/dist/src/roles/role-view.js +95 -56
  304. package/dist/src/roles/roles.js +33 -33
  305. package/dist/src/roles/runner-ids.js +97 -0
  306. package/dist/src/run/acceptance-greps.js +119 -0
  307. package/dist/src/run/ci-parity.js +121 -0
  308. package/dist/src/run/feedback-filings.js +51 -0
  309. package/dist/src/run/qa-coverage.js +125 -0
  310. package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
  311. package/dist/src/run/run-detail-data.js +175 -0
  312. package/dist/src/run/run-detail-render.js +91 -0
  313. package/dist/src/run/run-detail.js +168 -0
  314. package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +73 -60
  315. package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
  316. package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +122 -73
  317. package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
  318. package/dist/src/{tick/tick-stage.js → run/run-stage.js} +170 -83
  319. package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
  320. package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
  321. package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +105 -57
  322. package/dist/src/run/selfcheck-digest.js +40 -0
  323. package/dist/src/run/stage-backlog-checks.js +249 -0
  324. package/dist/src/run/stage-check.js +178 -0
  325. package/dist/src/run/stage-checks-shared.js +65 -0
  326. package/dist/src/run/stage-name-checks.js +204 -0
  327. package/dist/src/run/stage-path-checks.js +231 -0
  328. package/dist/src/run/stage-prose-checks.js +261 -0
  329. package/dist/src/runner/leftover.js +216 -0
  330. package/dist/src/{loop → runner}/model-fallback.js +12 -12
  331. package/dist/src/runner/revision.js +191 -0
  332. package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
  333. package/dist/src/runner/runner-outcome.js +109 -0
  334. package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
  335. package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +39 -18
  336. package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +236 -245
  337. package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
  338. package/dist/src/runner/runner-state.js +103 -0
  339. package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
  340. package/dist/src/scheduling/backoff.js +38 -38
  341. package/dist/src/scheduling/claim-entries.js +49 -0
  342. package/dist/src/scheduling/claims.js +35 -22
  343. package/dist/src/scheduling/once-round.js +22 -22
  344. package/dist/src/scheduling/quiet-hours.js +5 -6
  345. package/dist/src/scheduling/scheduling.js +74 -64
  346. package/dist/src/scheduling/work-merged-cache.js +2 -2
  347. package/dist/src/status/status-budget.js +55 -47
  348. package/dist/src/status/status-data.js +49 -17
  349. package/dist/src/status/status-inbox.js +2 -2
  350. package/dist/src/status/status-install.js +0 -1
  351. package/dist/src/status/status-polls.js +43 -28
  352. package/dist/src/status/status-roles.js +50 -13
  353. package/dist/src/team/error-storm.js +8 -8
  354. package/dist/src/team/failure-spread.js +2 -2
  355. package/dist/src/team/orchestrator-info.js +15 -9
  356. package/dist/src/team/reclaim.js +7 -8
  357. package/dist/src/team/team-hold.js +7 -7
  358. package/dist/src/team/team-polls.js +91 -50
  359. package/dist/src/team/team-state.js +8 -2
  360. package/dist/src/text/datetime.js +65 -18
  361. package/dist/src/text/format.js +48 -11
  362. package/dist/src/text/markdown.js +8 -5
  363. package/dist/src/text/phrases.js +60 -23
  364. package/dist/src/text/suggest.js +1 -1
  365. package/dist/src/text/text.js +78 -13
  366. package/dist/src/ui/badges.js +99 -38
  367. package/dist/src/ui/gui/gui-client-backlog.js +314 -0
  368. package/dist/src/ui/gui/gui-client-boot.js +89 -61
  369. package/dist/src/ui/gui/gui-client-composer.js +8 -9
  370. package/dist/src/ui/gui/gui-client-drawer-config.js +254 -0
  371. package/dist/src/ui/gui/gui-client-drawer.js +155 -289
  372. package/dist/src/ui/gui/gui-client-feed.js +271 -0
  373. package/dist/src/ui/gui/gui-client-history.js +437 -68
  374. package/dist/src/ui/gui/gui-client-labels.js +6 -3
  375. package/dist/src/ui/gui/gui-client-markdown.js +1 -2
  376. package/dist/src/ui/gui/gui-client-model.js +27 -36
  377. package/dist/src/ui/gui/gui-client-operator.js +60 -52
  378. package/dist/src/ui/gui/gui-client-pending.js +405 -24
  379. package/dist/src/ui/gui/gui-client-report-chart.js +492 -0
  380. package/dist/src/ui/gui/gui-client-report.js +105 -430
  381. package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
  382. package/dist/src/ui/gui/gui-client-settings.js +163 -5
  383. package/dist/src/ui/gui/gui-client-team.js +36 -465
  384. package/dist/src/ui/gui/gui-client.js +38 -26
  385. package/dist/src/ui/gui/gui-icons.js +0 -2
  386. package/dist/src/ui/gui/gui-page.js +10 -7
  387. package/dist/src/ui/gui/gui-styles.js +38 -34
  388. package/dist/src/ui/progress-data.js +9 -11
  389. package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +33 -32
  390. package/dist/src/ui/status-model.js +133 -125
  391. package/dist/src/ui/status-payload.js +364 -49
  392. package/dist/src/ui/status-render.js +109 -82
  393. package/dist/src/ui/team-alerts.js +728 -62
  394. package/dist/src/ui/tone.js +12 -6
  395. package/dist/src/ui/transcript-run.js +58 -0
  396. package/dist/src/ui/transcript-tail.js +28 -20
  397. package/dist/src/ui/transcript.js +63 -23
  398. package/dist/src/ui/tui/tui-app.js +0 -1
  399. package/dist/src/ui/tui/tui-backlog.js +53 -14
  400. package/dist/src/ui/tui/tui-editor-answer.js +27 -31
  401. package/dist/src/ui/tui/tui-editor-attach.js +4 -12
  402. package/dist/src/ui/tui/tui-editor-backends.js +127 -0
  403. package/dist/src/ui/tui/tui-editor-brief.js +14 -36
  404. package/dist/src/ui/tui/tui-editor-budget.js +36 -39
  405. package/dist/src/ui/tui/tui-editor-check.js +29 -0
  406. package/dist/src/ui/tui/tui-editor-checks.js +28 -0
  407. package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
  408. package/dist/src/ui/tui/tui-editor-concurrency.js +25 -0
  409. package/dist/src/ui/tui/tui-editor-config.js +99 -0
  410. package/dist/src/ui/tui/tui-editor-entry.js +39 -64
  411. package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
  412. package/dist/src/ui/tui/tui-editor-instances.js +46 -0
  413. package/dist/src/ui/tui/tui-editor-instructions.js +117 -0
  414. package/dist/src/ui/tui/tui-editor-interval.js +29 -16
  415. package/dist/src/ui/tui/tui-editor-model.js +29 -0
  416. package/dist/src/ui/tui/tui-editor-notes.js +9 -29
  417. package/dist/src/ui/tui/tui-editor-notify.js +25 -0
  418. package/dist/src/ui/tui/tui-editor-pause.js +48 -59
  419. package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
  420. package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
  421. package/dist/src/ui/tui/tui-editor-queued.js +5 -9
  422. package/dist/src/ui/tui/tui-editor-quiet.js +95 -41
  423. package/dist/src/ui/tui/tui-editor-retire.js +5 -13
  424. package/dist/src/ui/tui/tui-editor-role-enabled.js +126 -0
  425. package/dist/src/ui/tui/tui-editor-role-key.js +71 -0
  426. package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
  427. package/dist/src/ui/tui/tui-editor-search.js +45 -33
  428. package/dist/src/ui/tui/tui-editor-session.js +101 -34
  429. package/dist/src/ui/tui/tui-editor-slots.js +24 -0
  430. package/dist/src/ui/tui/tui-editors.js +44 -2
  431. package/dist/src/ui/tui/tui-frame.js +68 -11
  432. package/dist/src/ui/tui/tui-history.js +8 -9
  433. package/dist/src/ui/tui/tui-input.js +13 -11
  434. package/dist/src/ui/tui/tui-keymap.js +0 -1
  435. package/dist/src/ui/tui/tui-keys.js +33 -9
  436. package/dist/src/ui/tui/tui-pane-activity.js +35 -19
  437. package/dist/src/ui/tui/tui-pane-backlog.js +198 -126
  438. package/dist/src/ui/tui/tui-pane-brief.js +0 -1
  439. package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
  440. package/dist/src/ui/tui/tui-pane-health.js +168 -8
  441. package/dist/src/ui/tui/tui-pane-history.js +446 -82
  442. package/dist/src/ui/tui/tui-pane-pending.js +539 -15
  443. package/dist/src/ui/tui/tui-pane-queued.js +78 -57
  444. package/dist/src/ui/tui/tui-pane-report.js +54 -16
  445. package/dist/src/ui/tui/tui-pane-session.js +34 -6
  446. package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
  447. package/dist/src/ui/tui/tui-pane.js +91 -28
  448. package/dist/src/ui/tui/tui-panes.js +153 -34
  449. package/dist/src/ui/tui/tui.js +18 -15
  450. package/dist/src/verdict/fix-claim.js +129 -33
  451. package/dist/src/verdict/no-change.js +4 -4
  452. package/dist/src/verdict/refusal.js +40 -16
  453. package/dist/src/verdict/reply-contract.js +77 -31
  454. package/dist/src/version.js +0 -1
  455. package/package.json +2 -1
  456. package/dist/src/loop/leftover.js +0 -130
  457. package/dist/src/loop/revision.js +0 -70
  458. package/dist/src/roles/loop-ids.js +0 -77
  459. package/dist/src/tick/qa-coverage.js +0 -108
  460. package/dist/src/tick/stage-check.js +0 -419
  461. package/dist/src/tick/telemetry-digest.js +0 -25
  462. package/dist/src/tick/tick-detail-data.js +0 -108
  463. package/dist/src/tick/tick-detail.js +0 -163
  464. package/dist/src/ui/gui/gui-client-sound.js +0 -53
@@ -1,30 +1,32 @@
1
- // vocabulary: plain
2
1
  /** The browser-side dashboard app inlined as the GUI page's only <script>. It polls /api/status
3
2
  * every second and renders, in the order an operator needs them: the masthead's team status
4
3
  * (running, build, main check) and team controls (budget, pause); alerts for whatever needs a
5
- * human (a failing loop, a red main, a spent budget, an old build, open questions, a pause);
6
- * the composer that steers the director or one loop; and the team view — today's progress,
7
- * the loops grouped by what they are doing, the backlog, and the notable activity. The
4
+ * human (a failing role, a red main, a spent budget, an old build, open questions, a pause);
5
+ * the composer that steers the director or one role; and the team view — today's progress,
6
+ * the roles grouped by what they are doing, the backlog, and the notable activity. The
8
7
  * view-model, the drawer, the History/Usage/Failures/Pending views, the operator controls, the
9
8
  * composer, the Markdown renderer, and the routing/poll boot live in their own modules and
10
9
  * are spliced in below; everything shares one scope. The page
11
10
  * cannot import the harness modules, so it keeps its own copies of the few display rules it
12
- * needs (formatters, loop order), each pinned against its TypeScript twin by test. Written as
11
+ * needs (formatters, role order), each pinned against its TypeScript twin by test. Written as
13
12
  * String.raw templates so the served script is exactly the text below — no double escaping. */
13
+ import { GUI_CLIENT_BACKLOG_JS } from "./gui-client-backlog.js";
14
14
  import { GUI_CLIENT_BOOT_JS } from "./gui-client-boot.js";
15
15
  import { GUI_CLIENT_COMPOSER_JS } from "./gui-client-composer.js";
16
16
  import { GUI_CLIENT_DRAWER_JS } from "./gui-client-drawer.js";
17
+ import { GUI_CLIENT_DRAWER_CONFIG_JS } from "./gui-client-drawer-config.js";
18
+ import { GUI_CLIENT_FEED_JS } from "./gui-client-feed.js";
17
19
  import { GUI_CLIENT_TEAM_JS } from "./gui-client-team.js";
18
20
  import { GUI_CLIENT_HISTORY_JS } from "./gui-client-history.js";
19
21
  import { GUI_CLIENT_LABELS_JS } from "./gui-client-labels.js";
20
- import { GUI_CLIENT_LOOPS_JS } from "./gui-client-loops.js";
22
+ import { GUI_CLIENT_ROLES_JS } from "./gui-client-roles.js";
21
23
  import { GUI_CLIENT_MARKDOWN_JS } from "./gui-client-markdown.js";
22
24
  import { GUI_CLIENT_MODEL_JS } from "./gui-client-model.js";
23
25
  import { GUI_CLIENT_OPERATOR_JS } from "./gui-client-operator.js";
24
26
  import { GUI_CLIENT_PENDING_JS } from "./gui-client-pending.js";
27
+ import { GUI_CLIENT_REPORT_CHART_JS } from "./gui-client-report-chart.js";
25
28
  import { GUI_CLIENT_REPORT_JS } from "./gui-client-report.js";
26
29
  import { GUI_CLIENT_SETTINGS_JS } from "./gui-client-settings.js";
27
- import { GUI_CLIENT_SOUND_JS } from "./gui-client-sound.js";
28
30
  import { ICON_PATHS } from "./gui-icons.js";
29
31
  import { ACTIVE_PHASE_PREFIXES } from "../status-model.js";
30
32
  /** The client's opening: escaping and the guarded API helpers every call goes through. Kept
@@ -86,7 +88,7 @@ const CORE_JS = String.raw ` const esc = (s) => String(s).replace(/[&<>"']/g, (
86
88
  // accepts the connection but never answers
87
89
  // would otherwise hold the poll open forever, leaving the page frozen on stale data with no
88
90
  // offline alert — the catch in refresh() only runs when fetch rejects. The timeout aborts the
89
- // request, so the poll fails, the red "Lost contact" alert shows, and the loop keeps polling.
91
+ // request, so the poll fails, the red "Lost contact" alert shows, and polling continues.
90
92
  // AbortSignal.timeout is standard since 2022; where a browser lacks it the fetch stays
91
93
  // unbounded, exactly the old behavior.
92
94
  const POLL_TIMEOUT_MS = 15000;
@@ -141,11 +143,11 @@ const FORMAT_JS = String.raw ` // format:start
141
143
  const agree = (n, one, many) => (n === 1 ? one : many);
142
144
  const plural = (n, one, many) => n + " " + agree(n, one, many || one + "s");
143
145
  // The merged-commit work/maintenance split the sidebar's "Merged today" tile and the report's
144
- // "Commits merged" tile both print. report-data.ts leaves the counts optional, so a missing
146
+ // "Commits merged" tile both print. report-types.ts leaves the counts optional, so a missing
145
147
  // count reads 0 here rather than "undefined".
146
148
  const workSplit = (t) => (t.workCommits ?? 0) + " work / " + (t.maintenanceCommits ?? 0) + " maintenance";
147
149
  // The unmerged-commit list both change views paint — each commit's sha and subject on its
148
- // own clamped line. The Pending roster's Ahead cell and the loop drawer's Pending change
150
+ // own clamped line. The Pending roster's Ahead cell and the role drawer's Pending change
149
151
  // section had the byte-identical map; a missing or non-array list paints nothing.
150
152
  const commitRowsHtml = (commits) =>
151
153
  (Array.isArray(commits) ? commits : []).map((c) => "<div class='mono clamp1'>" + esc(c.sha) + " " + esc(c.subject || "—") + "</div>").join("");
@@ -168,7 +170,7 @@ const FORMAT_JS = String.raw ` // format:start
168
170
  };
169
171
  // human-seconds-fmt:end
170
172
  // active-phase-fmt:start
171
- // A PERMIT-HOLDING loop in flight — status-model.ts's isActivePhase. The three prefixes are
173
+ // A PERMIT-HOLDING role in flight — status-model.ts's isActivePhase. The three prefixes are
172
174
  // interpolated from status-model.ts's ACTIVE_PHASE_PREFIXES (String.raw still substitutes),
173
175
  // so this copy cannot drift from the server's; the running director's own director-working
174
176
  // label is checked beside it rather than folded in, because it holds no permit (BUGS.md
@@ -176,14 +178,14 @@ const FORMAT_JS = String.raw ` // format:start
176
178
  const ACTIVE_PHASE_PREFIXES = ${JSON.stringify(ACTIVE_PHASE_PREFIXES)};
177
179
  const isActivePhase = (phase) => ACTIVE_PHASE_PREFIXES.some((p) => phase.startsWith(p));
178
180
  // active-phase-fmt:end
179
- // last-tick-fmt:start
180
- // The absolute local time of a tick end with its age ("14:32:05 · 3m ago"), prefixed MM-DD
181
- // once older than a day; "-" when never ticked — status-render.ts's lastTickCell.
181
+ // last-run-fmt:start
182
+ // The absolute local time of a run end with its age ("14:32:05 · 3m ago"), prefixed MM-DD
183
+ // once older than a day; "-" when never ran — status-render.ts's lastRunCell.
182
184
  // Whole elapsed seconds since an instant, clamped at zero and rounded to the nearest
183
- // second — datetime.ts's elapsedSeconds (the browser runtime cannot import it), shared by
184
- // fmtLastTick and fmtAgo below so the clamp cannot drift between the page's ages.
185
+ // second — datetime.ts's secondsSince (the browser runtime cannot import it), shared by
186
+ // fmtLastRun and fmtAgo below so the clamp cannot drift between the page's ages.
185
187
  const ageSec = (ts) => Math.max(0, Math.round((Date.now() - ts) / 1000));
186
- const fmtLastTick = (ts) => {
188
+ const fmtLastRun = (ts) => {
187
189
  if (!ts) return "-";
188
190
  const d = new Date(ts);
189
191
  const p = (n) => String(n).padStart(2, "0");
@@ -193,16 +195,16 @@ const FORMAT_JS = String.raw ` // format:start
193
195
  const age = humanSeconds(sec) + " ago";
194
196
  return s + " · " + age;
195
197
  };
196
- // last-tick-fmt:end
198
+ // last-run-fmt:end
197
199
  // next-run-fmt:start
198
- // When an idle loop may tick again — status-render.ts's nextRunCell: "-" for a loop in flight
200
+ // When an idle role may run again — status-render.ts's nextRunCell: "-" for a role in flight
199
201
  // or parked awaiting a slot (it has no next run to speak of) and for a stopped team; "now"
200
202
  // when due; otherwise the remaining time, prefixed "backoff " while backing off. A yield-scaled
201
203
  // clock rides as a " ×N" suffix: a quiet role's effective gap is longer than the countdown.
202
204
  const fmtNextRun = (l, teamRunning) => {
203
- // A torn loop object — a payload missing its phase field — reads as the empty (inactive)
205
+ // A torn role object — a payload missing its phase field — reads as the empty (inactive)
204
206
  // phase rather than throwing: the browser copy is untyped, and its sibling phaseInfo/
205
- // loopRank already coerce the same way, so one bad row must not blank the whole table.
207
+ // roleRank already coerce the same way, so one bad row must not blank the whole table.
206
208
  const phase = String(l.phase || "");
207
209
  const active = isActivePhase(phase) || phase.startsWith("waiting to start") || phase.startsWith("waiting for ") || phase.startsWith("director working");
208
210
  if (!teamRunning || active) return "-";
@@ -220,7 +222,7 @@ const FORMAT_JS = String.raw ` // format:start
220
222
  return sec < 60 ? "just now" : humanSeconds(sec) + " ago";
221
223
  };
222
224
  const fmtClock = (ts) => new Date(ts).toLocaleString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" });
223
- // A tick's duration: seconds under two minutes, then minutes, then hours. Each bucket is
225
+ // A run's duration: seconds under two minutes, then minutes, then hours. Each bucket is
224
226
  // chosen from the rounded count so a span just under a cutoff never prints "120s" or "120m".
225
227
  const fmtSpan = (ms) => {
226
228
  const sec = Math.round(ms / 1000);
@@ -230,7 +232,7 @@ const FORMAT_JS = String.raw ` // format:start
230
232
  return (ms / 3600000).toFixed(1) + "h";
231
233
  };
232
234
  // A run's wall-clock span cell: fmtSpan when a row carries a duration, a dash when it does not
233
- // (a tick whose start sits outside the window). Shared by the History table and summary rows
235
+ // (a run whose start sits outside the window). Shared by the History table and summary rows
234
236
  // (and any future duration cell) so the null rule and its em-dash cannot drift between them.
235
237
  const durCell = (ms) => (ms === null || ms === undefined ? "—" : esc(fmtSpan(Number(ms))));
236
238
  // Server text sometimes carries ISO instants (a restart cooldown's deadline); show them in
@@ -290,13 +292,23 @@ const DOM_JS = String.raw ` const ICONS = ${JSON.stringify(ICON_PATHS)};
290
292
  return true;
291
293
  }
292
294
  // panel-build:end
295
+ // err-message:start
296
+ // The one error-message read every fetch/parse catch shares: a thrown Error's message when
297
+ // it has one (the server's JSON body, or the client's own throws, put it there), else the
298
+ // caller's fallback. Single-homed here in the core before every splice so the catch blocks
299
+ // cannot drift the ternary's shape; settings' clientErrorMessage feeds it String(err) as the
300
+ // fallback.
301
+ function errMessage(err, fallback) {
302
+ return err && err.message ? err.message : fallback;
303
+ }
304
+ // err-message:end
293
305
  // The standard "this view could not load" panel: a title plus the server's error message,
294
306
  // if any. cls is "empty" for the bare panels (history, drawer) and "card empty" where the
295
307
  // view frames panels in cards (the report views). Drawer paints it through paintPanel so a
296
308
  // retry loop does not repaint; callers that own the element assign it directly.
297
309
  function errorPanel(title, err, cls) {
298
310
  return "<div class='" + (cls || "empty") + "'><strong>" + esc(title) + "</strong>" +
299
- esc(err && err.message ? err.message : "") + "</div>";
311
+ esc(errMessage(err, "")) + "</div>";
300
312
  }
301
313
  // Mark the one button of a segmented filter whose dataset[attr] matches current as
302
314
  // "active" and clear the rest: one home for the toggle loop each of the page's segmented
@@ -349,5 +361,5 @@ const DOM_JS = String.raw ` const ICONS = ${JSON.stringify(ICON_PATHS)};
349
361
  let offline = false;
350
362
  let activeView = "team";
351
363
  `;
352
- export const GUI_CLIENT_JS = [CORE_JS, FORMAT_JS, GUI_CLIENT_LABELS_JS, GUI_CLIENT_MODEL_JS, DOM_JS, GUI_CLIENT_MARKDOWN_JS, GUI_CLIENT_SOUND_JS, GUI_CLIENT_OPERATOR_JS, GUI_CLIENT_LOOPS_JS, GUI_CLIENT_TEAM_JS, GUI_CLIENT_COMPOSER_JS,
353
- GUI_CLIENT_DRAWER_JS, GUI_CLIENT_HISTORY_JS, GUI_CLIENT_PENDING_JS, GUI_CLIENT_REPORT_JS, GUI_CLIENT_SETTINGS_JS, GUI_CLIENT_BOOT_JS].join("\n");
364
+ export const GUI_CLIENT_JS = [CORE_JS, FORMAT_JS, GUI_CLIENT_LABELS_JS, GUI_CLIENT_MODEL_JS, DOM_JS, GUI_CLIENT_MARKDOWN_JS, GUI_CLIENT_OPERATOR_JS, GUI_CLIENT_ROLES_JS, GUI_CLIENT_TEAM_JS, GUI_CLIENT_BACKLOG_JS, GUI_CLIENT_FEED_JS, GUI_CLIENT_COMPOSER_JS,
365
+ GUI_CLIENT_DRAWER_JS, GUI_CLIENT_DRAWER_CONFIG_JS, GUI_CLIENT_HISTORY_JS, GUI_CLIENT_PENDING_JS, GUI_CLIENT_REPORT_CHART_JS, GUI_CLIENT_REPORT_JS, GUI_CLIENT_SETTINGS_JS, GUI_CLIENT_BOOT_JS].join("\n");
@@ -32,8 +32,6 @@ export const ICON_PATHS = {
32
32
  dot: `<circle cx="12" cy="12" r="3" ${FILLED}/>`,
33
33
  fail: '<circle cx="12" cy="12" r="9"/><path d="M9 9l6 6M15 9l-6 6"/>',
34
34
  offline: '<path d="M3 3l18 18M8.5 16.5a5 5 0 0 1 7 0M5 12.6a10 10 0 0 1 4.2-2.3M14.8 10.3A10 10 0 0 1 19 12.6M12 20h.01"/>',
35
- sound: '<path d="M4 9.5v5h3l4.5 4v-13L7 9.5z"/><path d="M15.5 8.7a5 5 0 0 1 0 6.6M18 6.2a9 9 0 0 1 0 11.6"/>',
36
- mute: '<path d="M4 9.5v5h3l4.5 4v-13L7 9.5z"/><path d="M15.5 9.5l5 5M20.5 9.5l-5 5"/>',
37
35
  settings: '<circle cx="12" cy="12" r="3.2"/><path d="M12 3v2.6M12 18.4V21M3 12h2.6M18.4 12H21M5.6 5.6l1.9 1.9M16.5 16.5l1.9 1.9M5.6 18.4l1.9-1.9M16.5 7.5l1.9-1.9"/>',
38
36
  };
39
37
  /** One icon's markup — the same shape the browser script's icon() builds. */
@@ -5,9 +5,9 @@
5
5
  * views — Team's carrying a badge while something needs a human — and the team-wide
6
6
  * controls: today's spend against the cap, and pause. The main column shows one view at a
7
7
  * time: Team (alerts for whatever needs a human, the composer that steers the director or
8
- * one loop, today's progress, every loop grouped by what it is doing, the backlog, and the
8
+ * one role, today's progress, every role grouped by what it is doing, the backlog, and the
9
9
  * notable activity), History, Usage, Failures, Pending, or Settings. A drawer opens any
10
- * loop's detail and live transcript, or any backlog entry in full. Kept apart from
10
+ * role's detail and live transcript, or any backlog entry in full. Kept apart from
11
11
  * gui/gui-server.ts so the server module stays about serving. */
12
12
  import { GUI_CLIENT_JS } from "./gui-client.js";
13
13
  import { GUI_STYLES } from "./gui-styles.js";
@@ -30,7 +30,6 @@ export const GUI_PAGE = `<!doctype html>
30
30
  <div class="side-top">
31
31
  <a class="brand" href="#team" aria-label="tumwater team">${LOGO_SVG}<span>tumwater</span></a>
32
32
  <div class="side-top-actions">
33
- <span id="soundwrap"></span>
34
33
  <button type="button" class="icon-btn" id="themetoggle" title="Switch between light and dark" aria-label="Switch between light and dark">${iconSvg("sun", "theme-sun")}${iconSvg("moon", "theme-moon")}</button>
35
34
  </div>
36
35
  </div>
@@ -73,17 +72,17 @@ export const GUI_PAGE = `<!doctype html>
73
72
  </div>
74
73
  </form>
75
74
  <div id="stats" class="stats"></div>
76
- <section class="card" aria-labelledby="loops-title">
75
+ <section class="card" aria-labelledby="roles-title">
77
76
  <header class="card-head">
78
- <h2 id="loops-title">Roles</h2>
77
+ <h2 id="roles-title">Roles</h2>
79
78
  <div class="spacer"></div>
80
79
  <button type="button" class="btn btn-sm" id="resetall" title="Zero every role's runs, commits, tokens, and cost — scheduling is untouched">${iconSvg("refresh")}<span>Reset counters</span></button>
81
80
  <div class="menu-anchor" id="wakelaterwrap"><button type="button" class="btn btn-sm" id="wakelater" aria-haspopup="menu" title="Wake every role later">${iconSvg("clock")}<span>Wake later</span></button></div>
82
81
  <button type="button" class="btn btn-sm" id="wakeall" title="Wake every role now, skipping any sleep">${iconSvg("bolt")}<span>Wake all</span></button>
83
82
  </header>
84
83
  <div class="table-wrap">
85
- <table class="table loops" id="loopstable">
86
- <thead><tr><th class="c-loop">Role</th><th class="c-status">Status</th><th class="c-activity">Now, or the last result</th><th class="c-today num">Spent today</th><th class="c-actions"><span class="sr-only">Actions</span></th></tr></thead>
84
+ <table class="table roles" id="rolestable">
85
+ <thead><tr><th class="c-role">Role</th><th class="c-status">Status</th><th class="c-activity">Now, or the last result</th><th class="c-today num">Spent today</th><th class="c-actions"><span class="sr-only">Actions</span></th></tr></thead>
87
86
  <tbody id="rolerows"></tbody>
88
87
  </table>
89
88
  </div>
@@ -94,6 +93,8 @@ export const GUI_PAGE = `<!doctype html>
94
93
  <h2 id="backlog-title">Backlog</h2>
95
94
  <div class="spacer"></div>
96
95
  <div class="seg" id="backlogtabs" role="tablist" aria-label="Backlog sections"></div>
96
+ <label id="backlogreadywrap"><input class="field" id="backlogready" type="checkbox"><span>Ready only</span></label>
97
+ <label><span class="sr-only">Search</span><input class="field" id="backloggrep" type="search" placeholder="Search backlog…" autocomplete="off"></label>
97
98
  </header>
98
99
  <div id="backlog" class="list"></div>
99
100
  </section>
@@ -105,6 +106,8 @@ export const GUI_PAGE = `<!doctype html>
105
106
  <button type="button" data-filter="notable" class="active">Notable</button><button type="button" data-filter="all">All events</button>
106
107
  </div>
107
108
  <label><span class="sr-only">Role</span><select class="field" id="feedrole"><option value="">All roles</option></select></label>
109
+ <label><span class="sr-only">Window</span><select class="field" id="feedwindow"></select></label>
110
+ <span id="feedstep" style="display:none"><button type="button" class="btn btn-sm" id="feedearlier">◀ Earlier</button><button type="button" class="btn btn-sm" id="feedlater">Later ▶</button></span>
108
111
  <label><span class="sr-only">Search</span><input class="field" id="feedgrep" type="search" placeholder="Search activity…" autocomplete="off"></label>
109
112
  </header>
110
113
  <ol id="feed" class="feed"></ol>
@@ -250,7 +250,7 @@ export const GUI_STYLES = String.raw `
250
250
  .settings-row .field { flex: 1; min-width: 0; }
251
251
  .settings-row .btn { flex: none; }
252
252
 
253
- /* ---- composer: the director prompt (or one loop's), always in reach ---- */
253
+ /* ---- composer: the director prompt (or one role's), always in reach ---- */
254
254
  .composer { padding: 8px; border: 1px solid var(--line); border-radius: 12px;
255
255
  background: var(--surface); box-shadow: var(--shadow-sm); }
256
256
  .composer:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--accent-bg); }
@@ -296,6 +296,10 @@ export const GUI_STYLES = String.raw `
296
296
  .card-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; padding: 10px 16px; min-height: 50px;
297
297
  border-bottom: 1px solid var(--line); }
298
298
  .card-head h2 { margin: 0; font-size: 14.5px; font-weight: 650; }
299
+ /* The Post-merge reviews card's header line and one queue entry each, beside the Merge
300
+ queue card's mono lines — the dashboard twin of 'tumwater reviews'. */
301
+ .queue-head { padding: 10px 16px 2px; font-size: 12.5px; font-weight: 600; color: var(--text-3); }
302
+ .queue-entry { padding: 3px 16px; }
299
303
  .grid-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 20px; align-items: start; }
300
304
  .grid-2.even { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
301
305
  .seg { display: inline-flex; padding: 2px; gap: 2px; border-radius: 9px; background: var(--surface-3); }
@@ -320,7 +324,7 @@ export const GUI_STYLES = String.raw `
320
324
  font-size: 12px; font-weight: 600; color: var(--text-3); }
321
325
  .table tr.group:not(:first-child) td { border-top: 1px solid var(--line); }
322
326
  .table tr.group .badge { margin-left: 6px; }
323
- /* The history view's tick drill-down: the rows' details toggle and the expanded card that
327
+ /* The history view's run drill-down: the rows' details toggle and the expanded card that
324
328
  rides beneath its row, full width. */
325
329
  .history .c-x { width: 40px; white-space: nowrap; }
326
330
  .hist-x { padding: 0 6px; }
@@ -336,22 +340,22 @@ export const GUI_STYLES = String.raw `
336
340
  .clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
337
341
  .clamp1 { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
338
342
 
339
- table.loops .c-loop { width: 15%; }
340
- table.loops .c-status { width: 24%; }
341
- table.loops .c-today { width: 110px; }
342
- table.loops .c-actions { width: 136px; }
343
- table.loops.free .c-today { display: none; }
344
- .loops tr.loop.selected td { background: var(--accent-bg); }
345
- .loops tr.loop.selected td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
346
- .loops tr.loop.live td:first-child { box-shadow: inset 3px 0 0 var(--blue); }
347
- .loops tr.loop.live.selected td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
348
- .loop-name { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
349
- .loop-name .role { font-family: var(--font-mono); font-weight: 600; font-size: 13.5px; }
343
+ table.roles .c-role { width: 15%; }
344
+ table.roles .c-status { width: 24%; }
345
+ table.roles .c-today { width: 110px; }
346
+ table.roles .c-actions { width: 136px; }
347
+ table.roles.free .c-today { display: none; }
348
+ .roles tr.role-row.selected td { background: var(--accent-bg); }
349
+ .roles tr.role-row.selected td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
350
+ .roles tr.role-row.live td:first-child { box-shadow: inset 3px 0 0 var(--blue); }
351
+ .roles tr.role-row.live.selected td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
352
+ .role-name { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
353
+ .role-name .role { font-family: var(--font-mono); font-weight: 600; font-size: 13.5px; }
350
354
  .now { color: var(--text); }
351
355
  .actions { display: flex; justify-content: flex-end; gap: 2px; }
352
- tr.loop .actions { opacity: 0.55; transition: opacity 0.12s; }
353
- tr.loop:hover .actions, tr.loop:focus-within .actions, tr.loop.selected .actions { opacity: 1; }
354
- @media (hover: none) { tr.loop .actions { opacity: 1; } }
356
+ tr.role-row .actions { opacity: 0.55; transition: opacity 0.12s; }
357
+ tr.role-row:hover .actions, tr.role-row:focus-within .actions, tr.role-row.selected .actions { opacity: 1; }
358
+ @media (hover: none) { tr.role-row .actions { opacity: 1; } }
355
359
 
356
360
  /* ---- backlog list ---- */
357
361
  .list { max-height: 460px; overflow-y: auto; }
@@ -380,14 +384,14 @@ export const GUI_STYLES = String.raw `
380
384
  .feed-icon svg.i { width: 15px; height: 15px; }
381
385
  .feed-msg { color: var(--text-2); overflow-wrap: anywhere; }
382
386
  .feed-item.k-routine .feed-msg { color: var(--text-3); }
383
- .feed-loop { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; color: var(--text); margin-right: 4px; }
387
+ .feed-source { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; color: var(--text); margin-right: 4px; }
384
388
  .feed-time { font-size: 12px; color: var(--text-4); white-space: nowrap; font-variant-numeric: tabular-nums; padding-top: 1px; }
385
389
  .feed-tally { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 16px 6px; border-bottom: 1px solid var(--line-soft); }
386
390
  .feed-tally .chip { height: 22px; padding: 0 9px; font-size: 12px; font-weight: 500; }
387
391
  .feed-tally button.chip { cursor: pointer; }
388
392
  .feed-tally .chip.active { border-color: var(--accent); background: var(--accent-bg); color: var(--text); }
389
393
 
390
- /* ---- drawer: a loop's detail and live transcript, or one backlog entry ---- */
394
+ /* ---- drawer: a role's detail and live transcript, or one backlog entry ---- */
391
395
  .drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 50; width: min(660px, 100vw); display: flex;
392
396
  flex-direction: column; background: var(--bg); border-left: 1px solid var(--line); box-shadow: var(--shadow-lg);
393
397
  animation: slidein 0.16s ease-out; }
@@ -412,11 +416,11 @@ export const GUI_STYLES = String.raw `
412
416
  .kv dt { font-size: 12px; color: var(--text-3); }
413
417
  .kv dd { margin: 1px 0 0; font-weight: 600; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
414
418
  .note { padding: 11px 13px; border-radius: 10px; border: 1px solid var(--line); background: var(--surface); overflow-wrap: anywhere; }
415
- .ticks { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--surface); }
416
- .ticks li { display: grid; grid-template-columns: 116px minmax(0, 1fr) auto; gap: 10px; padding: 8px 12px;
419
+ .run-list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--surface); }
420
+ .run-list li { display: grid; grid-template-columns: 116px minmax(0, 1fr) auto; gap: 10px; padding: 8px 12px;
417
421
  border-bottom: 1px solid var(--line-soft); font-size: 13px; }
418
- .ticks li:last-child { border-bottom: 0; }
419
- .ticks .when { color: var(--text-4); white-space: nowrap; font-variant-numeric: tabular-nums; }
422
+ .run-list li:last-child { border-bottom: 0; }
423
+ .run-list .when { color: var(--text-4); white-space: nowrap; font-variant-numeric: tabular-nums; }
420
424
  .transcript-sec { flex: 1; min-height: 320px; display: flex; flex-direction: column; }
421
425
  .transcript { flex: 1; min-height: 280px; margin: 0; overflow: auto; padding: 12px 14px; border-radius: 10px;
422
426
  border: 1px solid var(--line); background: var(--surface); font: 12.5px/1.6 var(--font-mono);
@@ -500,7 +504,7 @@ export const GUI_STYLES = String.raw `
500
504
  .menu-anchor.up .popover { top: calc(100% + 8px); bottom: auto; }
501
505
  .main { padding-left: 16px; padding-right: 16px; }
502
506
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
503
- table.loops .c-today { display: none; }
507
+ table.roles .c-today { display: none; }
504
508
  .kv { grid-template-columns: repeat(2, minmax(0, 1fr)); }
505
509
  .history .c-usage, .history .c-dur, .history .c-model { display: none; }
506
510
  }
@@ -508,22 +512,22 @@ export const GUI_STYLES = String.raw `
508
512
  .stats.six { grid-template-columns: repeat(2, minmax(0, 1fr)); }
509
513
  .stat-value { font-size: 21px; }
510
514
  .composer-foot #prompthint { display: none; }
511
- table.loops thead { display: none; }
512
- table.loops, table.loops tbody { display: block; }
513
- table.loops tr.loop { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px;
515
+ table.roles thead { display: none; }
516
+ table.roles, table.roles tbody { display: block; }
517
+ table.roles tr.role-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px;
514
518
  padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
515
- table.loops tr.loop td { display: block; width: auto; padding: 0; border: 0; background: none !important; box-shadow: none !important; }
516
- table.loops tr.loop td.c-status, table.loops tr.loop td.c-activity { grid-column: 1 / -1; margin-top: 6px; }
517
- table.loops tr.loop td.c-actions { grid-row: 1; grid-column: 2; }
518
- table.loops tr.loop td.c-today { display: none; }
519
+ table.roles tr.role-row td { display: block; width: auto; padding: 0; border: 0; background: none !important; box-shadow: none !important; }
520
+ table.roles tr.role-row td.c-status, table.roles tr.role-row td.c-activity { grid-column: 1 / -1; margin-top: 6px; }
521
+ table.roles tr.role-row td.c-actions { grid-row: 1; grid-column: 2; }
522
+ table.roles tr.role-row td.c-today { display: none; }
519
523
  .composer-row { flex-wrap: wrap; }
520
524
  .composer textarea { order: -1; flex-basis: 100%; }
521
525
  .composer-target { flex: 1; }
522
526
  .composer-target select { max-width: none; width: 100%; }
523
- table.loops tr.group, table.loops tr.group td { display: block; }
524
- .history .c-tick, .history .c-detail { display: none; }
525
- .ticks li { grid-template-columns: minmax(0, 1fr) auto; }
526
- .ticks .when:first-child { grid-column: 1 / -1; }
527
+ table.roles tr.group, table.roles tr.group td { display: block; }
528
+ .history .c-run, .history .c-detail { display: none; }
529
+ .run-list li { grid-template-columns: minmax(0, 1fr) auto; }
530
+ .run-list .when:first-child { grid-column: 1 / -1; }
527
531
  .md { padding: 4px 16px 16px; }
528
532
  }
529
533
  @media (prefers-reduced-motion: reduce) {
@@ -1,8 +1,6 @@
1
- // vocabulary: plain
2
1
  import path from "node:path";
3
2
  import { applyToolExecutionEvent, parsePiEventLine, toolCallCommand, } from "../pi/pi-event-line.js";
4
- import { bufferedCommandStallMs, commandBuffersOutput } from "../pi/command-shape.js";
5
- import { streamsToolOutput } from "../agent/backend-traits.js";
3
+ import { bufferedCommandStallMs, callBuffersOutput } from "../pi/command-shape.js";
6
4
  import { describeToolCall } from "../text/phrases.js";
7
5
  import { squash } from "../text/text.js";
8
6
  import { defaultConfig, liveConfig } from "../config/config.js";
@@ -17,7 +15,7 @@ const TAIL_BYTES = 4 * 1024 * 1024;
17
15
  /** Byte offset a first observation of the log (size `size`) seeds from: the start of the
18
16
  * smallest suffix window, grown from TAIL_BYTES in doubling steps, that contains a complete
19
17
  * `session` event line — or 0 when the log carries none. A bare `size - TAIL_BYTES` seed
20
- * opens mid-run whenever the log (which accumulates across ticks up to logMaxBytes) is
18
+ * opens mid-run whenever the log (which accumulates across runs up to logMaxBytes) is
21
19
  * larger than the window, and the first observation then counts every assistant turn after
22
20
  * that offset as if it were one run — a number with no defined meaning (BUGS.md 2026-09-22).
23
21
  * Seeding at the window that contains the last session anchors the read at a real run
@@ -103,7 +101,7 @@ const PROGRESS_TYPES = new Set([
103
101
  /** Apply one parsed progress event to a progress object (mutates it). */
104
102
  function feedLine(progress, event) {
105
103
  switch (event.type) {
106
- case "session": // A new run starts: everything before it was a previous tick — restore the at-time-zero state.
104
+ case "session": // A new run starts: everything before it was a previous run — restore the at-time-zero state.
107
105
  Object.assign(progress, freshProgress(progress.quietMs));
108
106
  break;
109
107
  case "tool_execution_start": {
@@ -143,7 +141,7 @@ function feedLine(progress, event) {
143
141
  progress.outputTokens += nonNegativeNumber(usage.output, 0);
144
142
  progress.peakContextTokens = Math.max(progress.peakContextTokens, nonNegativeNumber(usage.totalTokens, 0));
145
143
  }
146
- // The first text the loop speaks in this run is its work item ("I'll implement plan X");
144
+ // The first text the role speaks in this run is its work item ("I'll implement plan X");
147
145
  // later messages never replace it.
148
146
  if (!progress.currentWork)
149
147
  progress.currentWork = workItemFromContent(event.message.content);
@@ -180,7 +178,7 @@ export function toolCallStallMs(root) {
180
178
  * line, so this runs on every read rather than in feedLine; `now` and `stallMs` are injectable
181
179
  * for tests (freshly fed lines stamp Date.now(), so nothing is stalled right after parsing).
182
180
  * A call whose stdout is piped or redirected holds its bytes away from pi until it exits
183
- * (BUGS.md 2026-09-28: the tick prompt itself prescribes `npm run test 2>&1 | tail`), so its
181
+ * (BUGS.md 2026-09-28: the run prompt itself prescribes `npm run test 2>&1 | tail`), so its
184
182
  * silence is the prescribed shape and never evidence of a hang — but the command can still
185
183
  * hang (a leaked grandchild holding the pipe open), so past a wall-clock threshold it is
186
184
  * flagged anyway (BUGS.md 2026-10-06), the same threshold runPi's warning applies so the
@@ -192,7 +190,7 @@ export function stalledToolLabel(open, now = Date.now(), stallMs = defaultConfig
192
190
  // claude never sends content-bearing tool updates, so silence is expected for every call
193
191
  // and only the wall-clock duration can show a hang (plans/agent-backends.md "Watchdogs and
194
192
  // progress"); pi keeps the per-command classification.
195
- if (!streamsToolOutput(agent) || commandBuffersOutput(call.command || call.label)) {
193
+ if (callBuffersOutput(agent, call.command || call.label)) {
196
194
  if (now - call.lastActivityAt >= bufferedCommandStallMs(stallMs))
197
195
  return call.label;
198
196
  continue;
@@ -233,7 +231,7 @@ function feedDemuxed(tail, line, gateCwd) {
233
231
  // accumulator as soon as the marker's stage says `reviewing` — a poll that can land
234
232
  // before the session event. Resetting here means a previous run's turns/context (the
235
233
  // last review, or another run the gate spent) can never show in the new run's cell.
236
- // Safe for the in-tick reviewing cell too: a role's gate is serial, so a marker ends
234
+ // Safe for the in-run reviewing cell too: a role's gate is serial, so a marker ends
237
235
  // the previous run.
238
236
  tail.cur = kind;
239
237
  tail.kindFromMarker = true;
@@ -249,8 +247,8 @@ function feedDemuxed(tail, line, gateCwd) {
249
247
  }
250
248
  feedLine(tail[tail.cur], event);
251
249
  }
252
- /** Live progress for one of a loop's pi run kinds (`author` — the in-flight tick's run in
253
- * the role's own worktree, the default; `gate` — the review gate's runs in a leased pool
250
+ /** Live progress for one of a role's agent run kinds (`author` — the in-flight authoring agent
251
+ * run in the role's own worktree, the default; `gate` — the review gate's runs in a leased pool
254
252
  * slot, what the reviewing cell shows), or null when there is no log yet. The raw log is
255
253
  * append-only while pi runs and every run starts with a `tumwater_run` marker
256
254
  * (src/agent/run-agent.ts), so after seeding from the tail window once we only read and parse
@@ -1,15 +1,16 @@
1
1
  import { readLiveProgress } from "./progress-data.js";
2
2
  import { compactTokens } from "../text/format.js";
3
- /** The in-flight tick's PROGRESS display model (split from status-model.ts, which keeps the
4
- * loop-phase ladder, the merge cells, and the row derivations): how a running tick renders
3
+ import { pad2 } from "../text/datetime.js";
4
+ /** The in-flight run's PROGRESS display model (split from status-model.ts, which keeps the
5
+ * role-phase ladder, the merge cells, and the row derivations): how an in-flight run renders
5
6
  * its live detail — elapsed, turn, context, current tool, stall — derived from the live log
6
7
  * tail (progress-data.ts) and shared by BOTH observer surfaces: the terminal table
7
8
  * (status-render.ts's state cell) and the JSON/GUI payload (status-payload.ts), plus the
8
- * phase ladder's in-flight branches (status-model.ts's loopPhase), so the three cannot
9
+ * phase ladder's in-flight branches (status-model.ts's rolePhase), so the three cannot
9
10
  * drift apart. This is the disk-reading half: every helper here reads (or accepts a caller's
10
11
  * precomputed) live log tail, while status-model.ts derives labels from the snapshot. */
11
12
  /** Seconds/minutes/hours elapsed formatting shared by the progress helpers and the phase
12
- * ladder's elapsed branches (loopPhase's merge and parked branches). The hours bucket
13
+ * ladder's elapsed branches (rolePhase's merge and parked branches). The hours bucket
13
14
  * rounds to whole minutes off the total, so a rounded-up 59m5Xs carries into the next hour
14
15
  * ("2h0m"); rounding the remainder alone would print the impossible "1h60m". */
15
16
  export function duration(ms) {
@@ -17,41 +18,41 @@ export function duration(ms) {
17
18
  if (s < 60)
18
19
  return `${s}s`;
19
20
  if (s < 3600)
20
- return `${Math.floor(s / 60)}m${String(s % 60).padStart(2, "0")}s`;
21
+ return `${Math.floor(s / 60)}m${pad2(s % 60)}s`;
21
22
  const totalMinutes = Math.round(s / 60);
22
23
  return `${Math.floor(totalMinutes / 60)}h${totalMinutes % 60}m`;
23
24
  }
24
- /** The label for a loop's in-flight tick: `<label>` plus how long it has been running
25
+ /** The label for a role's in-flight run: `<label>` plus how long it has been running
25
26
  * ("working 3m", "reviewing 2m") — shared by workingDetail and the review-gate branch of
26
- * loopPhase so their elapsed formatting cannot drift. A tick with no recorded start time
27
- * renders as just the bare label. (A merge's elapsed is its marker's, never the tick's —
28
- * loopPhase's merge branch builds that head itself.) */
27
+ * rolePhase so their elapsed formatting cannot drift. A run with no recorded start time
28
+ * renders as just the bare label. (A merge's elapsed is its marker's, never the run's —
29
+ * rolePhase's merge branch builds that head itself.) */
29
30
  export function inFlightLabel(s, label) {
30
31
  const elapsed = s.lastRunStartedAt ? duration(Date.now() - s.lastRunStartedAt) : "";
31
32
  return `${label} ${elapsed}`.trim();
32
33
  }
33
- /** One rule for whether a log tail's progress describes THIS tick: the log's newest write
34
- * (quietMs is measured from the raw log's mtime) must have happened after the tick started.
35
- * Until this tick's pi run writes its first line, the log's tail is the PREVIOUS run — the
36
- * baseline check (worktree reset plus suite) can take 30–130 s, and a tick waking after 5+
34
+ /** One rule for whether a log tail's progress describes THIS run: the log's newest write
35
+ * (quietMs is measured from the raw log's mtime) must have happened after the run started.
36
+ * Until the current agent run writes its first line, the log's tail is the PREVIOUS run — the
37
+ * baseline check (worktree reset plus suite) can take 30–130 s, and a run waking after 5+
37
38
  * minutes idle would otherwise show the earlier run's turn/context/tool and a "no pi output"
38
- * quiet time measured from before the tick even began, tripping the STALL alert on every
39
- * such tick's baseline (BUGS.md 2026-09-30). Once the run writes, its mtime is inside the
40
- * tick, so quietMs can never exceed the tick's own age and the honest quiet measure survives.
41
- * Applied by every progress reader (workingDetail, the reviewing branch of loopPhase, the
42
- * token metrics, loopRowCells' precomputed tail) and by status-render's state cell via
43
- * tickProgress, so the TUI, the GUI payload, and the alert cannot drift apart. A state with
39
+ * quiet time measured from before the run even began, tripping the STALL alert on every
40
+ * such run's baseline (BUGS.md 2026-09-30). Once the run writes, its mtime is inside the
41
+ * run, so quietMs can never exceed the run's own age and the honest quiet measure survives.
42
+ * Applied by every progress reader (workingDetail, the reviewing branch of rolePhase, the
43
+ * token metrics, roleRowCells' precomputed tail) and by status-render's state cell via
44
+ * runProgress, so the TUI, the GUI payload, and the alert cannot drift apart. A state with
44
45
  * no recorded start (a stale `running` flag after a crash) cannot be judged and keeps the
45
46
  * tail as before. `p` may be undefined (the caller's "no tail fetched" sentinel) or null
46
47
  * ("no log"); both read as no progress. */
47
- function progressOfTick(s, p) {
48
+ function progressOfRun(s, p) {
48
49
  if (!p)
49
50
  return null;
50
51
  if (s.lastRunStartedAt === undefined)
51
52
  return p;
52
53
  const elapsed = Math.max(0, Date.now() - s.lastRunStartedAt);
53
54
  if (p.quietMs > elapsed)
54
- return null; // Newest write predates this tick: a previous run's tail.
55
+ return null; // Newest write predates this run: a previous run's tail.
55
56
  return { ...p, quietMs: Math.min(p.quietMs, elapsed) };
56
57
  }
57
58
  /** The stall phrases a phase label can carry — inFlightDetail appends "tool call stalled:
@@ -65,7 +66,7 @@ export const STALL_SOURCE = String.raw `tool call stalled[^·]*|no agent output
65
66
  /** STALL_SOURCE compiled — the server-side matcher (team-alerts.ts's stuck alert). */
66
67
  export const STALL_RE = new RegExp(STALL_SOURCE);
67
68
  /** The shared parts assembly for an in-flight state cell: `head` (the phase label with its
68
- * own elapsed — inFlightLabel for a tick, the merge branch's own for a merge) plus turn,
69
+ * own elapsed — inFlightLabel for a run, the merge branch's own for a merge) plus turn,
69
70
  * live context, current tool, and the ≥5-min no-output stall flag. `p` is null when there is
70
71
  * no progress (no log yet) — falls back to the bare head. */
71
72
  export function inFlightDetail(head, p) {
@@ -87,31 +88,31 @@ export function inFlightDetail(head, p) {
87
88
  parts.push(`no agent output for ${duration(p.quietMs)}`);
88
89
  return parts.join(" · ");
89
90
  }
90
- /** Which pi run kind (progress-data.ts's ProgressRunKind) a loop's in-flight cells should read:
91
- * the review gate's reviewer run ("gate") while the tick is under review — its `session`
91
+ /** Which pi run kind (progress-data.ts's ProgressRunKind) a role's in-flight cells should read:
92
+ * the review gate's reviewer run ("gate") while the run is under review — its `session`
92
93
  * event is the log's newest and its counts are what "reviewing" describes — the author's
93
94
  * own run ("author") for every other in-flight phase. One home for the rule so the TUI
94
95
  * table and the GUI payload cannot drift (the precomputed `live` tail must name the same
95
- * run the phase label does). status-model's loopRowCells is its only reader. */
96
+ * run the phase label does). status-model's roleRowCells is its only reader. */
96
97
  export function progressKind(s) {
97
98
  return s.phase === "review" ? "gate" : "author";
98
99
  }
99
- /** progressOfTick with the tail read folded in — the one home for the
100
+ /** progressOfRun with the tail read folded in — the one home for the
100
101
  * `live === undefined ? readLiveProgress(...) : live` idiom. Callers holding this frame's
101
- * already-fetched tail pass it as `live` (renderStatus reads once per running loop and
102
+ * already-fetched tail pass it as `live` (renderStatus reads once per running role and
102
103
  * threads it through every helper); standalone callers pass undefined and this reads the
103
104
  * role's log itself — the author accumulator by default, the gate one via `kind`. Every
104
105
  * tail-reading caller outside this module routes through it; status-model.ts's staged branch
105
106
  * is the one exception, keeping its raw readLiveProgress call because it feeds inFlightDetail
106
107
  * a freshly read gate tail for the merge label, not a per-frame threaded one. */
107
- export function tickProgress(root, s, live, kind = "author") {
108
- return progressOfTick(s, live === undefined ? readLiveProgress(root, s.role, kind) : live);
108
+ export function runProgress(root, s, live, kind = "author") {
109
+ return progressOfRun(s, live === undefined ? readLiveProgress(root, s.role, kind) : live);
109
110
  }
110
- /** The state cell for a working loop: elapsed · turns · live context · current tool. Pass
111
- * `live` — this frame's already-fetched tail (renderStatus reads once per running loop and
111
+ /** The state cell for a working role: elapsed · turns · live context · current tool. Pass
112
+ * `live` — this frame's already-fetched tail (renderStatus reads once per running role and
112
113
  * threads it through every helper) — to avoid re-reading the log; without it, this reads on
113
114
  * its own for standalone callers. */
114
115
  export function workingDetail(root, s, live) {
115
- const p = tickProgress(root, s, live);
116
+ const p = runProgress(root, s, live);
116
117
  return inFlightDetail(inFlightLabel(s, "working"), p);
117
118
  }