tumwater 0.4.0 → 0.4.2

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 (291) hide show
  1. package/README.md +4 -6
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-backend.js +5 -2
  4. package/dist/src/agent/backend-traits.js +21 -0
  5. package/dist/src/agent/claude/claude-backend.js +1 -1
  6. package/dist/src/agent/claude/claude-stream.js +181 -182
  7. package/dist/src/agent/claude/{write-guard-hook.js → claude-write-guard-hook.js} +1 -1
  8. package/dist/src/agent/claude/claude-write-guard.js +5 -4
  9. package/dist/src/agent/codex/codex-backend.js +1 -1
  10. package/dist/src/agent/codex/codex-rollout.js +48 -45
  11. package/dist/src/agent/codex/codex-stream.js +269 -215
  12. package/dist/src/agent/codex/{write-guard-hook.js → codex-write-guard-hook.js} +2 -1
  13. package/dist/src/agent/codex/codex-write-guard.js +3 -3
  14. package/dist/src/agent/image-input.js +18 -87
  15. package/dist/src/agent/notes-shim.js +2 -2
  16. package/dist/src/agent/opencode/opencode-backend.js +2 -2
  17. package/dist/src/agent/opencode/opencode-catalog.js +2 -2
  18. package/dist/src/agent/opencode/opencode-config.js +2 -2
  19. package/dist/src/agent/opencode/opencode-stream.js +101 -92
  20. package/dist/src/agent/run-agent.js +76 -382
  21. package/dist/src/agent/run-settle.js +25 -15
  22. package/dist/src/agent/run-spawn.js +226 -0
  23. package/dist/src/agent/run-stream.js +185 -0
  24. package/dist/src/agent/session-ledger.js +16 -0
  25. package/dist/src/agent/write-guard.js +5 -3
  26. package/dist/src/backlog/backlog-archive.js +86 -170
  27. package/dist/src/backlog/backlog-cli-view.js +89 -70
  28. package/dist/src/backlog/backlog-eligibility.js +51 -59
  29. package/dist/src/backlog/backlog-file-bugs.js +133 -0
  30. package/dist/src/backlog/backlog-index-data.js +159 -0
  31. package/dist/src/backlog/backlog-index.js +7 -5
  32. package/dist/src/backlog/backlog-md.js +39 -28
  33. package/dist/src/backlog/backlog-release.js +21 -36
  34. package/dist/src/backlog/backlog-render.js +1 -1
  35. package/dist/src/backlog/backlog-structure.js +14 -145
  36. package/dist/src/backlog/backlog-write.js +31 -18
  37. package/dist/src/backlog/backlog.js +14 -4
  38. package/dist/src/backlog/question-answer.js +33 -15
  39. package/dist/src/baseline/main-baseline.js +71 -26
  40. package/dist/src/baseline/main-guardrails.js +76 -15
  41. package/dist/src/baseline/main-red.js +45 -94
  42. package/dist/src/baseline/main-watch.js +9 -3
  43. package/dist/src/brief/brief.js +40 -0
  44. package/dist/src/budget/budget-fallback-view.js +50 -8
  45. package/dist/src/budget/budget-wiring.js +15 -11
  46. package/dist/src/budget/budget.js +3 -2
  47. package/dist/src/build/build-check-events.js +4 -2
  48. package/dist/src/build/build-check-report.js +2 -1
  49. package/dist/src/build/build-check-scoped.js +70 -41
  50. package/dist/src/change/change-data.js +4 -2
  51. package/dist/src/cli/cli-args.js +9 -0
  52. package/dist/src/cli/cli-command-args.js +199 -98
  53. package/dist/src/cli/cli-flag-specs.js +6 -4
  54. package/dist/src/cli/cli-marker-commands.js +2 -2
  55. package/dist/src/cli/cli-query-commands.js +41 -6
  56. package/dist/src/cli/cli-review-commands.js +2 -1
  57. package/dist/src/cli/cli-role-command.js +46 -92
  58. package/dist/src/cli/cli-start.js +123 -47
  59. package/dist/src/cli/follow-view.js +3 -1
  60. package/dist/src/cli/help.js +19 -12
  61. package/dist/src/cli/log-commands.js +128 -107
  62. package/dist/src/cli/question-commands.js +82 -63
  63. package/dist/src/cli.js +396 -343
  64. package/dist/src/collections.js +46 -9
  65. package/dist/src/config/config-fallbacks.js +0 -4
  66. package/dist/src/config/config-live.js +179 -147
  67. package/dist/src/config/config-request.js +176 -51
  68. package/dist/src/config/config-validation-roles.js +271 -0
  69. package/dist/src/config/config-validation.js +10 -205
  70. package/dist/src/config/config-views.js +2 -7
  71. package/dist/src/config/config-write.js +205 -175
  72. package/dist/src/config/config.js +2 -1
  73. package/dist/src/doctor/doctor-backlog-bugs.js +24 -122
  74. package/dist/src/doctor/doctor-backlog-plans.js +24 -25
  75. package/dist/src/doctor/doctor-backlog.js +34 -42
  76. package/dist/src/doctor/doctor-check-names.js +4 -2
  77. package/dist/src/doctor/doctor-checks.js +16 -4
  78. package/dist/src/doctor/doctor-claude-checks.js +2 -2
  79. package/dist/src/doctor/doctor-codex-checks.js +9 -20
  80. package/dist/src/doctor/doctor-contention-checks.js +52 -19
  81. package/dist/src/doctor/doctor-holds.js +22 -20
  82. package/dist/src/doctor/doctor-model-checks.js +8 -184
  83. package/dist/src/doctor/doctor-notify.js +2 -5
  84. package/dist/src/doctor/doctor-opencode-checks.js +90 -41
  85. package/dist/src/doctor/doctor-principles.js +26 -0
  86. package/dist/src/doctor/doctor-project-checks.js +69 -19
  87. package/dist/src/doctor/doctor-role-notes.js +33 -0
  88. package/dist/src/doctor/doctor.js +13 -6
  89. package/dist/src/doctor/probe.js +1 -1
  90. package/dist/src/events/event-format.js +307 -162
  91. package/dist/src/events/event-labels.js +0 -1
  92. package/dist/src/events/event-legacy.js +1 -1
  93. package/dist/src/events/event-window.js +1 -1
  94. package/dist/src/events/events.js +15 -2
  95. package/dist/src/events/notify.js +1 -1
  96. package/dist/src/failure/failure-cluster.js +4 -3
  97. package/dist/src/failure/failure-data.js +126 -94
  98. package/dist/src/failure/failure-render.js +58 -17
  99. package/dist/src/failure/failure-state-change.js +308 -27
  100. package/dist/src/failure/time-spend.js +139 -106
  101. package/dist/src/files/files.js +7 -0
  102. package/dist/src/files/json-object.js +15 -2
  103. package/dist/src/gates/budget-gates.js +90 -68
  104. package/dist/src/gates/gate-polls.js +92 -61
  105. package/dist/src/gates/role-cap-gates.js +1 -1
  106. package/dist/src/gates/startup-gate.js +28 -28
  107. package/dist/src/gates/streak-gate.js +1 -1
  108. package/dist/src/git/git.js +1 -1
  109. package/dist/src/git/slot-salvage.js +9 -9
  110. package/dist/src/git/slots-state.js +12 -3
  111. package/dist/src/git/worktree-pool-startup.js +127 -0
  112. package/dist/src/git/worktree-pool.js +1 -120
  113. package/dist/src/gui/gui-admin-endpoints.js +3 -4
  114. package/dist/src/gui/gui-args.js +1 -1
  115. package/dist/src/gui/gui-backlog-commands.js +20 -11
  116. package/dist/src/gui/gui-backlog-endpoints.js +2 -8
  117. package/dist/src/gui/gui-config-commands.js +51 -76
  118. package/dist/src/gui/gui-endpoint-commands.js +4 -2
  119. package/dist/src/gui/gui-endpoint-common.js +4 -3
  120. package/dist/src/gui/gui-history-endpoints.js +36 -27
  121. package/dist/src/gui/gui-operator-commands.js +43 -17
  122. package/dist/src/gui/gui-prompt-commands.js +27 -21
  123. package/dist/src/gui/gui-quarantine-commands.js +30 -0
  124. package/dist/src/gui/gui-report-endpoints.js +13 -9
  125. package/dist/src/gui/gui-server.js +5 -3
  126. package/dist/src/gui/gui-team-commands.js +1 -1
  127. package/dist/src/history/history-data.js +7 -1
  128. package/dist/src/history/history-render.js +15 -10
  129. package/dist/src/history/history.js +103 -68
  130. package/dist/src/inbox/inbox-submit.js +1 -5
  131. package/dist/src/inbox/pending-prompt.js +3 -2
  132. package/dist/src/inbox/prompt-commands.js +194 -161
  133. package/dist/src/init/init.js +104 -25
  134. package/dist/src/merge/merge-core.js +1 -1
  135. package/dist/src/merge/merge-drain.js +1 -1
  136. package/dist/src/merge/merge-git.js +5 -5
  137. package/dist/src/merge/{merge-post-review.js → merge-post-review-drain.js} +1 -1
  138. package/dist/src/merge/merge-queue.js +2 -1
  139. package/dist/src/merge/merge-to-main.js +1 -1
  140. package/dist/src/merge/merge-verify.js +3 -2
  141. package/dist/src/operator/operator-commands.js +84 -52
  142. package/dist/src/operator/operator-intent.js +26 -3
  143. package/dist/src/operator/retire.js +2 -1
  144. package/dist/src/operator/revert.js +18 -12
  145. package/dist/src/orchestrator/orchestrator-poll.js +2 -2
  146. package/dist/src/orchestrator/orchestrator.js +1 -1
  147. package/dist/src/orchestrator/retention.js +6 -5
  148. package/dist/src/paths.js +10 -10
  149. package/dist/src/pi/pi-bin.js +2 -2
  150. package/dist/src/pi/pi-models.js +9 -0
  151. package/dist/src/pi/pi-stream.js +63 -41
  152. package/dist/src/pi/pi-watchdogs.js +84 -49
  153. package/dist/src/pi-extension/context-shake.js +55 -31
  154. package/dist/src/pi-extension/full-output.js +2 -3
  155. package/dist/src/process/process.js +24 -1
  156. package/dist/src/process/run-marker.js +2 -2
  157. package/dist/src/process/supervisor.js +24 -5
  158. package/dist/src/prompt/gate-notes.js +41 -16
  159. package/dist/src/prompt/notes-backlog.js +22 -29
  160. package/dist/src/prompt/principles.js +11 -0
  161. package/dist/src/prompt/prompt-followup.js +4 -12
  162. package/dist/src/prompt/prompt.js +15 -12
  163. package/dist/src/redeploy/redeploy-probes.js +11 -1
  164. package/dist/src/redeploy/redeployer.js +54 -7
  165. package/dist/src/redeploy/self-reload.js +7 -2
  166. package/dist/src/report/report-data.js +224 -320
  167. package/dist/src/report/report-fold.js +23 -231
  168. package/dist/src/report/report-main-red.js +70 -3
  169. package/dist/src/report/report-render.js +181 -48
  170. package/dist/src/report/report-since-data.js +218 -0
  171. package/dist/src/report/report-usage-fold.js +288 -0
  172. package/dist/src/report/report.js +2 -1
  173. package/dist/src/review/known-flakes.js +22 -3
  174. package/dist/src/review/post-merge-review-accept.js +3 -2
  175. package/dist/src/review/post-merge-review.js +16 -14
  176. package/dist/src/review/quarantine-render.js +29 -11
  177. package/dist/src/review/review-precheck.js +2 -1
  178. package/dist/src/roles/role-catalog.js +30 -29
  179. package/dist/src/roles/role-guidance.js +26 -43
  180. package/dist/src/roles/role-render.js +19 -0
  181. package/dist/src/roles/role-view.js +19 -5
  182. package/dist/src/roles/roles.js +2 -22
  183. package/dist/src/run/ci-parity.js +5 -6
  184. package/dist/src/run/feedback-filings.js +10 -1
  185. package/dist/src/run/qa-coverage.js +4 -4
  186. package/dist/src/run/run-apply.js +14 -2
  187. package/dist/src/run/run-detail-data.js +50 -10
  188. package/dist/src/run/run-detail-render.js +32 -1
  189. package/dist/src/run/run-detail.js +44 -22
  190. package/dist/src/run/run-dismiss.js +43 -0
  191. package/dist/src/run/run-finalize.js +75 -46
  192. package/dist/src/run/run-prompt.js +271 -187
  193. package/dist/src/run/run-resume.js +32 -31
  194. package/dist/src/run/run-stage.js +116 -64
  195. package/dist/src/run/run-timing.js +18 -4
  196. package/dist/src/run/run-verdict.js +119 -83
  197. package/dist/src/run/stage-backlog-checks.js +4 -31
  198. package/dist/src/run/stage-check.js +4 -12
  199. package/dist/src/run/stage-checks-shared.js +15 -0
  200. package/dist/src/run/stage-name-checks.js +3 -13
  201. package/dist/src/run/stage-path-checks.js +26 -29
  202. package/dist/src/runner/leftover.js +13 -4
  203. package/dist/src/runner/revision.js +2 -2
  204. package/dist/src/runner/runner-fallback.js +1 -1
  205. package/dist/src/runner/runner-outcome.js +0 -5
  206. package/dist/src/runner/runner-pi.js +72 -49
  207. package/dist/src/runner/runner-run.js +19 -102
  208. package/dist/src/runner/runner-state-load.js +0 -10
  209. package/dist/src/runner/runner.js +1 -32
  210. package/dist/src/scheduling/claims.js +4 -11
  211. package/dist/src/status/status-data.js +140 -91
  212. package/dist/src/status/status-polls.js +41 -5
  213. package/dist/src/status/status-role-notes.js +53 -0
  214. package/dist/src/status/status-roles.js +10 -37
  215. package/dist/src/text/datetime.js +8 -1
  216. package/dist/src/text/format.js +13 -1
  217. package/dist/src/text/phrases.js +16 -2
  218. package/dist/src/text/text.js +12 -0
  219. package/dist/src/ui/alert-feeds.js +115 -0
  220. package/dist/src/ui/badges.js +3 -4
  221. package/dist/src/ui/gui/gui-client-boot.js +78 -57
  222. package/dist/src/ui/gui/gui-client-drawer-config.js +8 -3
  223. package/dist/src/ui/gui/gui-client-drawer.js +18 -2
  224. package/dist/src/ui/gui/gui-client-feed.js +3 -8
  225. package/dist/src/ui/gui/gui-client-history.js +16 -16
  226. package/dist/src/ui/gui/gui-client-model.js +9 -9
  227. package/dist/src/ui/gui/gui-client-operator.js +6 -10
  228. package/dist/src/ui/gui/gui-client-pending.js +4 -4
  229. package/dist/src/ui/gui/gui-client-report-chart.js +111 -10
  230. package/dist/src/ui/gui/gui-client-report.js +70 -11
  231. package/dist/src/ui/gui/gui-client-roles.js +52 -12
  232. package/dist/src/ui/gui/gui-client-settings.js +31 -26
  233. package/dist/src/ui/gui/gui-client-team.js +28 -2
  234. package/dist/src/ui/gui/gui-client.js +33 -4
  235. package/dist/src/ui/gui/gui-icons.js +7 -1
  236. package/dist/src/ui/progress-data.js +2 -2
  237. package/dist/src/ui/run-progress-model.js +2 -2
  238. package/dist/src/ui/status-model.js +26 -16
  239. package/dist/src/ui/status-payload.js +149 -326
  240. package/dist/src/ui/status-render.js +131 -80
  241. package/dist/src/ui/team-alert-backlog.js +236 -0
  242. package/dist/src/ui/team-alert-shared.js +92 -0
  243. package/dist/src/ui/team-alert-status.js +326 -0
  244. package/dist/src/ui/team-alert-tail.js +238 -0
  245. package/dist/src/ui/team-alerts.js +16 -984
  246. package/dist/src/ui/transcript-tail.js +137 -94
  247. package/dist/src/ui/tui/tui-backlog.js +1 -1
  248. package/dist/src/ui/tui/tui-editor-answer.js +54 -44
  249. package/dist/src/ui/tui/tui-editor-attach.js +74 -63
  250. package/dist/src/ui/tui/tui-editor-backends.js +62 -56
  251. package/dist/src/ui/tui/tui-editor-brief.js +64 -54
  252. package/dist/src/ui/tui/tui-editor-budget.js +106 -89
  253. package/dist/src/ui/tui/tui-editor-config.js +2 -4
  254. package/dist/src/ui/tui/tui-editor-ctx.js +12 -1
  255. package/dist/src/ui/tui/tui-editor-entry.js +171 -153
  256. package/dist/src/ui/tui/tui-editor-instances.js +2 -1
  257. package/dist/src/ui/tui/tui-editor-instructions.js +50 -107
  258. package/dist/src/ui/tui/tui-editor-interval.js +39 -112
  259. package/dist/src/ui/tui/tui-editor-notes.js +28 -84
  260. package/dist/src/ui/tui/tui-editor-pause.js +210 -183
  261. package/dist/src/ui/tui/tui-editor-prompt.js +46 -35
  262. package/dist/src/ui/tui/tui-editor-queued.js +74 -64
  263. package/dist/src/ui/tui/tui-editor-quiet.js +122 -108
  264. package/dist/src/ui/tui/tui-editor-release.js +85 -0
  265. package/dist/src/ui/tui/tui-editor-retire.js +57 -46
  266. package/dist/src/ui/tui/tui-editor-role-enabled.js +90 -80
  267. package/dist/src/ui/tui/tui-editor-role-file.js +87 -0
  268. package/dist/src/ui/tui/tui-editor-role-key.js +50 -42
  269. package/dist/src/ui/tui/tui-editor-search.js +70 -58
  270. package/dist/src/ui/tui/tui-editor-session.js +3 -0
  271. package/dist/src/ui/tui/tui-editors.js +4 -1
  272. package/dist/src/ui/tui/tui-frame.js +7 -3
  273. package/dist/src/ui/tui/tui-history.js +12 -10
  274. package/dist/src/ui/tui/tui-keys.js +6 -1
  275. package/dist/src/ui/tui/tui-pane-activity.js +62 -50
  276. package/dist/src/ui/tui/tui-pane-backlog.js +4 -4
  277. package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
  278. package/dist/src/ui/tui/tui-pane-health.js +45 -26
  279. package/dist/src/ui/tui/tui-pane-history.js +296 -264
  280. package/dist/src/ui/tui/tui-pane-report.js +74 -57
  281. package/dist/src/ui/tui/tui-pane-session.js +6 -2
  282. package/dist/src/ui/tui/tui-pane-transcript.js +158 -131
  283. package/dist/src/ui/tui/tui-pane.js +201 -174
  284. package/dist/src/ui/tui/tui-panes.js +211 -148
  285. package/dist/src/ui/tui/tui.js +4 -4
  286. package/dist/src/verdict/fix-claim.js +18 -6
  287. package/dist/src/verdict/refusal.js +32 -7
  288. package/package.json +1 -1
  289. package/dist/src/doctor/image-probe.js +0 -257
  290. /package/dist/src/gui/{http-body.js → gui-http-body.js} +0 -0
  291. /package/dist/src/merge/{backlog-conflicts.js → merge-backlog-conflicts.js} +0 -0
@@ -151,6 +151,23 @@ const FORMAT_JS = String.raw ` // format:start
151
151
  // section had the byte-identical map; a missing or non-array list paints nothing.
152
152
  const commitRowsHtml = (commits) =>
153
153
  (Array.isArray(commits) ? commits : []).map((c) => "<div class='mono clamp1'>" + esc(c.sha) + " " + esc(c.subject || "—") + "</div>").join("");
154
+ // Title-case a phrase's first character and keep the rest — backend-traits.ts's
155
+ // backendDisplayName rule, the one home of the upcasing gui-client-model.ts's result labels
156
+ // and gui-client-history.ts's window options each inlined, so a label cannot drift between
157
+ // them. A leading em dash or other non-letter upcases to itself, and an empty string stays
158
+ // empty.
159
+ const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
160
+ // A whole-hour window length as "1 hour"/"N hours", or "1 day"/"N days" for a whole
161
+ // number of days — the shared span the activity feed's and History view's stepped-window
162
+ // bounds both print (each previously spelled out the modulo/pluralization inline). A minute
163
+ // count that isn't a whole number of days prints hours (a stepped window is always whole
164
+ // hours), and the singular drops the trailing s.
165
+ const timeSpan = (ms) => { const d = ms % 86400000 === 0; const n = d ? ms / 86400000 : ms / 3600000; return n + (d ? " day" : " hour") + (n === 1 ? "" : "s"); };
166
+ // The "between X and Y ago" window-length phrase a stepped window's two bounds both print
167
+ // (newer first) — the Activity feed's feedWindowPhrase and the History view's histWindowPhrase
168
+ // previously spelled out the span-and-framing inline, so the two could drift on the words
169
+ // around timeSpan. One home keeps the stepped-window phrasing identical everywhere.
170
+ const betweenAgo = (newerMs, olderMs) => "between " + timeSpan(newerMs) + " and " + timeSpan(olderMs) + " ago";
154
171
  // human-seconds-fmt:start
155
172
  // Whole-second s/m/h/d label: <60 → Ns, <3600 → rounded Nm, <86400 → rounded Nh, else
156
173
  // rounded Nd — datetime.ts's humanSeconds, shared by every relative time on the page. A
@@ -179,12 +196,15 @@ const FORMAT_JS = String.raw ` // format:start
179
196
  const isActivePhase = (phase) => ACTIVE_PHASE_PREFIXES.some((p) => phase.startsWith(p));
180
197
  // active-phase-fmt:end
181
198
  // last-run-fmt:start
199
+ // Whole seconds between two epoch-ms instants, clamped at zero and rounded to the nearest
200
+ // second — datetime.ts's secondsSince/secondsUntil (the browser runtime cannot import
201
+ // them), shared by every relative time on the page so the clamp cannot drift between
202
+ // fmtLastRun, fmtAgo, and the spliced views' pause/wake/queue ages.
203
+ const secsSince = (sinceMs, now) => Math.max(0, Math.round((now - sinceMs) / 1000));
204
+ const secsUntil = (atMs, now) => Math.max(0, Math.round((atMs - now) / 1000));
182
205
  // The absolute local time of a run end with its age ("14:32:05 · 3m ago"), prefixed MM-DD
183
206
  // once older than a day; "-" when never ran — status-render.ts's lastRunCell.
184
- // Whole elapsed seconds since an instant, clamped at zero and rounded to the nearest
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.
187
- const ageSec = (ts) => Math.max(0, Math.round((Date.now() - ts) / 1000));
207
+ const ageSec = (ts) => secsSince(ts, Date.now());
188
208
  const fmtLastRun = (ts) => {
189
209
  if (!ts) return "-";
190
210
  const d = new Date(ts);
@@ -222,6 +242,10 @@ const FORMAT_JS = String.raw ` // format:start
222
242
  return sec < 60 ? "just now" : humanSeconds(sec) + " ago";
223
243
  };
224
244
  const fmtClock = (ts) => new Date(ts).toLocaleString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" });
245
+ // The local wall-clock HH:MM of an instant — datetime.ts's clockHm, shared by the operator
246
+ // view's window clock (returnsAt/heldUntil/~cap-at stamps) and the role drawer's ~cap-at
247
+ // stamp, which each spelled the two-digit getHours/getMinutes join inline.
248
+ const clockHm = (ms) => { const d = new Date(ms); return String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0"); };
225
249
  // A run's duration: seconds under two minutes, then minutes, then hours. Each bucket is
226
250
  // chosen from the rounded count so a span just under a cutoff never prints "120s" or "120m".
227
251
  const fmtSpan = (ms) => {
@@ -238,6 +262,11 @@ const FORMAT_JS = String.raw ` // format:start
238
262
  // Server text sometimes carries ISO instants (a restart cooldown's deadline); show them in
239
263
  // the viewer's local time.
240
264
  const localizeInstants = (s) => String(s).replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z/g, (iso) => fmtClock(Date.parse(iso)));
265
+ // Collapse runs of whitespace to a single space and trim — text.ts's squash, kept as this
266
+ // browser twin because the page cannot import the server module. The one shared home of the
267
+ // squash the drawer-config verbatim value's single-line clamp and the pending view's status
268
+ // suffix+clamp each spelled inline, so the two cannot drift on the collapse rule.
269
+ const squash = (s) => String(s).replace(/\s+/g, " ").trim();
241
270
  // format:end
242
271
  `;
243
272
  /** Shared DOM helpers: icons, pills, panels that repaint only on change, local preferences,
@@ -4,6 +4,12 @@
4
4
  * the browser script receives ICON_PATHS as data and builds the same markup for everything it
5
5
  * renders. */
6
6
  const FILLED = 'fill="currentColor" stroke="none"';
7
+ /** The fixed icon set, keyed by the name every renderer passes: a `Record<string, string>`
8
+ * here would let a typo'd icon name (a misspelled key in `iconSvg`'s argument) compile and
9
+ * silently render an empty SVG in the dashboard — the literal key union below turns that
10
+ * into a compile error at every call site. The browser script still embeds the whole map
11
+ * (gui-client.ts JSON.stringify) and looks names up dynamically, so a dynamic name there is
12
+ * unchecked the same way its other data-driven lookups are. */
7
13
  export const ICON_PATHS = {
8
14
  grid: '<rect x="4" y="4" width="7" height="7" rx="1.5"/><rect x="13" y="4" width="7" height="7" rx="1.5"/><rect x="4" y="13" width="7" height="7" rx="1.5"/><rect x="13" y="13" width="7" height="7" rx="1.5"/>',
9
15
  list: `<path d="M9 6h11M9 12h11M9 18h11"/><circle cx="4.5" cy="6" r="1.2" ${FILLED}/><circle cx="4.5" cy="12" r="1.2" ${FILLED}/><circle cx="4.5" cy="18" r="1.2" ${FILLED}/>`,
@@ -36,7 +42,7 @@ export const ICON_PATHS = {
36
42
  };
37
43
  /** One icon's markup — the same shape the browser script's icon() builds. */
38
44
  export function iconSvg(name, extraClass = "") {
39
- return `<svg class="i${extraClass ? ` ${extraClass}` : ""}" viewBox="0 0 24 24" aria-hidden="true">${ICON_PATHS[name] ?? ""}</svg>`;
45
+ return `<svg class="i${extraClass ? ` ${extraClass}` : ""}" viewBox="0 0 24 24" aria-hidden="true">${ICON_PATHS[name]}</svg>`;
40
46
  }
41
47
  /** The tumwater mark: two waves on a teal tile (the name is Chinook Jargon for "thumping water").
42
48
  * Also the page's favicon (as a data URI). */
@@ -86,7 +86,7 @@ function freshProgress(quietMs, agent) {
86
86
  /** The event types feedLine acts on — everything else (streaming deltas, turn/agent
87
87
  * bookkeeping) is ignored. Also passed as parsePiEventLine's pre-filter to skip JSON.parse for
88
88
  * pi lines whose type is verifiably not one of these; a new case in the switch must be added
89
- * here too. `tumwater_run` is the harness's own marker (src/agent/run-agent.ts), read for its
89
+ * here too. `tumwater_run` is the harness's own marker (src/agent/run-spawn.ts), read for its
90
90
  * run kind: a kind-bearing marker flips the demux and starts that accumulator fresh before
91
91
  * its `session` event lands, and a legacy label-only marker ("review") still flips the demux
92
92
  * to gate. */
@@ -251,7 +251,7 @@ function feedDemuxed(tail, line, gateCwd) {
251
251
  * run in the role's own worktree, the default; `gate` — the review gate's runs in a leased pool
252
252
  * slot, what the reviewing cell shows), or null when there is no log yet. The raw log is
253
253
  * append-only while pi runs and every run starts with a `tumwater_run` marker
254
- * (src/agent/run-agent.ts), so after seeding from the tail window once we only read and parse
254
+ * (src/agent/run-spawn.ts), so after seeding from the tail window once we only read and parse
255
255
  * bytes appended since the last poll — observers that call this every second (TUI, GUI) stop
256
256
  * rescanning up to TAIL_BYTES
257
257
  * of JSON per role per poll. */
@@ -59,11 +59,11 @@ function progressOfRun(s, p) {
59
59
  * <tool>" and "no agent output for <duration>" to the head — as a regex SOURCE string plus the
60
60
  * compiled matcher, single-homed beside the code that renders those phrases. Every consumer
61
61
  * that must recognize a stall inside a rendered phase label goes through these two, so the
62
- * server-side alert (team-alerts.ts) and the dashboard's browser twin (gui-client-model.ts,
62
+ * server-side alert (team-alert-status.ts) and the dashboard's browser twin (gui-client-model.ts,
63
63
  * interpolating the source into its String.raw script) cannot drift from the wording the cell
64
64
  * actually renders. */
65
65
  export const STALL_SOURCE = String.raw `tool call stalled[^·]*|no agent output for [^·]*`;
66
- /** STALL_SOURCE compiled — the server-side matcher (team-alerts.ts's stuck alert). */
66
+ /** STALL_SOURCE compiled — the server-side matcher (team-alert-status.ts's stuck alert). */
67
67
  export const STALL_RE = new RegExp(STALL_SOURCE);
68
68
  /** The shared parts assembly for an in-flight state cell: `head` (the phase label with its
69
69
  * own elapsed — inFlightLabel for a run, the merge branch's own for a merge) plus turn,
@@ -1,7 +1,7 @@
1
1
  import { DIRECTOR_ROLE } from "../roles/roles.js";
2
2
  import { baseRoleOf } from "../roles/runner-ids.js";
3
3
  import { heldByMainRed } from "../status/status-polls.js";
4
- import { ERROR_STREAK_WARN, QUIET_KILL_RESUME_LIMIT } from "../run/run-apply.js";
4
+ import { errorStreakAtWarn, QUIET_KILL_RESUME_LIMIT } from "../run/run-apply.js";
5
5
  import { readLiveProgress } from "./progress-data.js";
6
6
  import { quietWindowEnd } from "../scheduling/quiet-hours.js";
7
7
  import { yieldMultiplierFor } from "../scheduling/backoff.js";
@@ -76,12 +76,12 @@ export function idleClaimText(s, _phase) {
76
76
  export function onErrorStreak(s) {
77
77
  // An error streak at or past the warning threshold (run-apply.ts's ERROR_STREAK_WARN), or
78
78
  // a quiet-kill streak at the give-up threshold (BUGS.md 2026-09-18): the exact condition
79
- // that makes rolePhase read "failing", split out so the team alert (team-alerts.ts) can
79
+ // that makes rolePhase read "failing", split out so the team alert (team-alert-status.ts) can
80
80
  // key its red "failing run after run" banner on the streak rather than on the rendered
81
81
  // phase — an in-flight run hides the streak behind a `working`/`merge` label (rolePhase
82
82
  // ranks running work above failing), but the alert must stay up while the role's next
83
83
  // run is in flight and clear only when the streak resets (BUGS.md 2026-10-14).
84
- return ((s.consecutiveErrors ?? 0) >= ERROR_STREAK_WARN ||
84
+ return (errorStreakAtWarn(s) ||
85
85
  (s.lastResult === "quiet_killed" && (s.quietKillStreak ?? 0) >= QUIET_KILL_RESUME_LIMIT));
86
86
  }
87
87
  export function rolePhase(s, orchestratorRunning, root, budgetPaused = false, live, userPaused = false, merge, mainCheck, capPaused = false, quietHold, diskHeld = false, bootstrapHeld = false, maintenanceQuotaHold, permits, pausedUntilMs) {
@@ -190,14 +190,14 @@ export function rolePhase(s, orchestratorRunning, root, budgetPaused = false, li
190
190
  // snapshot's holder, like roleQuietPaused's window.
191
191
  if (maintenanceQuotaHold)
192
192
  return maintenanceQuotaHold;
193
- // Main's own suite is known red at this role's last run: code-producing roles are blocked
194
- // from authoring until main is green (the red-main baseline check). Shown before sleep/queue
193
+ // Main's own suite is known red at this role's last run: main_red is the last completed
194
+ // outcome. Shown before sleep/queue
195
195
  // because it explains why the role keeps waking and merge nothing; self-correcting, since
196
- // each blocked run re-records main_red while red and a green wake overwrites lastResult.
196
+ // each run re-records main_red while red and a green wake overwrites lastResult.
197
197
  // Except a green wake that queues a change: `queued` is in flight, not a completed result,
198
198
  // so it leaves the main_red pair in place until its merge resolves (run-apply.ts's
199
- // applyRunOutcome) — its stashed summary is the tell that the role's latest run got past
200
- // the red-main gate, so main was green then and "main red" would be stale.
199
+ // applyRunOutcome) — its stashed summary is the tell that the role's latest run saw
200
+ // main green, so "main red" would be stale.
201
201
  // Except too when main's newest merge-scope check PASSED after this role's red run ended:
202
202
  // the team-level verdict is fresher than the role's own, so the label is stale until the
203
203
  // role's next run — which can be hours away (BUGS.md 2026-09-30). A check older than the
@@ -263,11 +263,21 @@ export function rolePhase(s, orchestratorRunning, root, budgetPaused = false, li
263
263
  cell = "due";
264
264
  }
265
265
  }
266
- // Two or more consecutive no-change runs earn the suffix (N=1 is a single quiet run —
267
- // ordinary, not yet a pattern).
268
- const streak = s.noChangeStreak ?? 0;
269
- if (streak >= 2)
270
- cell += ` · no change ×${streak}`;
266
+ // Two or more consecutive runs earn the suffix (N=1 is a single run — ordinary, not yet
267
+ // a pattern). A cut-off run is itself a `no_change`, so noChangeStreak counts a
268
+ // context-ceiling episode too — but the plain suffix would mislabel a window-burning
269
+ // episode as a benign idle one, so when the cut-off streak reads 2+ it wins: it names the
270
+ // costly cause (operator should split the task or raise the model's context window before
271
+ // the give-up). Observational only, like the no-change suffix — it never warns from here
272
+ // (the once-per-episode crossing warning comes from run-finalize.ts).
273
+ const cutOff = s.cutOffStreak ?? 0;
274
+ if (cutOff >= 2)
275
+ cell += ` · cut off ×${cutOff}`;
276
+ else {
277
+ const streak = s.noChangeStreak ?? 0;
278
+ if (streak >= 2)
279
+ cell += ` · no change ×${streak}`;
280
+ }
271
281
  return cell;
272
282
  }
273
283
  /** One role row's display derivation — the block both observer surfaces re-ran per role
@@ -337,7 +347,7 @@ function isBindingLimit(permits, key) {
337
347
  }
338
348
  /** Is this phase one of the PERMIT-HOLDER in-flight states — the rows an operator counts
339
349
  * against maxConcurrent? The three labels come from rolePhase: `working …`, `reviewing …`,
340
- * and the marker-driven `merge …` (merge queue 4/5) — a approved change waiting for the merge
350
+ * and the marker-driven `merge …` (merge queue 4/5) — an approved change waiting for the merge
341
351
  * slot (`approved, awaiting merge`) runs nothing, so it is not in flight. The running director's
342
352
  * `director working …` label is deliberately outside the set: the director never calls
343
353
  * `semaphore.acquire`, so the cap-mirroring rows are exactly the permit holders (BUGS.md
@@ -352,9 +362,9 @@ export function isActivePhase(phase) {
352
362
  /** A role's rank in the rendered tables, from its phase label (rolePhase): -1 a RUNNING
353
363
  * DIRECTOR — live work of its own, outside the maxConcurrent cap it bypasses, so it groups
354
364
  * apart and never inflates the permit-holder count (BUGS.md 2026-10-06); 0 live work —
355
- * working, reviewing, merge (isActivePhase); 1 work waiting in the pipeline — a approved change
365
+ * working, reviewing, merge (isActivePhase); 1 work waiting in the pipeline — an approved change
356
366
  * awaiting the merge slot, or a run parked awaiting a permit; 2 needs attention — failing, or
357
- * blocked by a red main; 3 paused — by the operator, by the budget, or by the role's own
367
+ * its last run read main red; 3 paused — by the operator, by the budget, or by the role's own
358
368
  * per-role cap; 4 everything else (idle: sleeping, queued, the director waiting for prompts, a
359
369
  * stopped team). The dashboard groups its role table by these ranks; the TUI/status table
360
370
  * orders by them.