tumwater 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. package/README.md +27 -13
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +8 -9
  4. package/dist/src/agent/agent-backend.js +8 -6
  5. package/dist/src/agent/backend-traits.js +42 -25
  6. package/dist/src/agent/claude/claude-account.js +71 -204
  7. package/dist/src/agent/claude/claude-args.js +9 -6
  8. package/dist/src/agent/claude/claude-backend.js +10 -10
  9. package/dist/src/agent/claude/claude-env.js +1 -1
  10. package/dist/src/agent/claude/claude-stream.js +4 -3
  11. package/dist/src/agent/claude/claude-write-guard.js +49 -0
  12. package/dist/src/agent/claude/write-guard-hook.js +13 -0
  13. package/dist/src/agent/codex/codex-account.js +119 -0
  14. package/dist/src/agent/codex/codex-args.js +181 -0
  15. package/dist/src/agent/codex/codex-backend.js +279 -0
  16. package/dist/src/agent/codex/codex-catalog.js +97 -0
  17. package/dist/src/agent/codex/codex-env.js +18 -0
  18. package/dist/src/agent/codex/codex-hook-trust.js +246 -0
  19. package/dist/src/agent/codex/codex-patch-paths.js +36 -0
  20. package/dist/src/agent/codex/codex-rollout.js +179 -0
  21. package/dist/src/agent/codex/codex-stream.js +402 -0
  22. package/dist/src/agent/codex/codex-write-guard.js +86 -0
  23. package/dist/src/agent/codex/write-guard-hook.js +12 -0
  24. package/dist/src/agent/image-input.js +29 -24
  25. package/dist/src/agent/notes-shim.js +11 -12
  26. package/dist/src/agent/opencode/opencode-args.js +0 -1
  27. package/dist/src/agent/opencode/opencode-backend.js +12 -9
  28. package/dist/src/agent/opencode/opencode-catalog.js +55 -29
  29. package/dist/src/agent/opencode/opencode-config.js +19 -14
  30. package/dist/src/agent/opencode/opencode-env.js +0 -1
  31. package/dist/src/agent/opencode/opencode-session-id.js +1 -2
  32. package/dist/src/agent/opencode/opencode-stream.js +1 -1
  33. package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
  34. package/dist/src/agent/role-notes-write.js +5 -6
  35. package/dist/src/agent/run-agent.js +465 -448
  36. package/dist/src/agent/run-settle.js +214 -0
  37. package/dist/src/agent/session-ledger.js +86 -16
  38. package/dist/src/agent/subscription-account.js +220 -0
  39. package/dist/src/agent/write-guard.js +148 -0
  40. package/dist/src/backlog/backlog-archive.js +246 -55
  41. package/dist/src/backlog/backlog-cli-view.js +280 -0
  42. package/dist/src/backlog/backlog-cli.js +85 -274
  43. package/dist/src/backlog/backlog-commit.js +4 -5
  44. package/dist/src/backlog/backlog-eligibility.js +175 -242
  45. package/dist/src/backlog/backlog-index.js +149 -0
  46. package/dist/src/backlog/backlog-md.js +54 -6
  47. package/dist/src/backlog/backlog-parts.js +254 -0
  48. package/dist/src/backlog/backlog-release.js +117 -0
  49. package/dist/src/backlog/backlog-render.js +27 -0
  50. package/dist/src/backlog/backlog-structure.js +99 -156
  51. package/dist/src/backlog/backlog-write.js +40 -127
  52. package/dist/src/backlog/backlog.js +21 -6
  53. package/dist/src/backlog/question-answer.js +21 -15
  54. package/dist/src/baseline/main-baseline.js +89 -20
  55. package/dist/src/baseline/main-culprit.js +94 -0
  56. package/dist/src/baseline/main-guardrails.js +209 -0
  57. package/dist/src/baseline/main-red.js +139 -32
  58. package/dist/src/baseline/main-revert.js +133 -0
  59. package/dist/src/baseline/main-watch.js +184 -0
  60. package/dist/src/brief/brief-edit.js +9 -11
  61. package/dist/src/brief/brief.js +31 -51
  62. package/dist/src/budget/budget.js +30 -20
  63. package/dist/src/budget/fallback-breaker.js +26 -27
  64. package/dist/src/build/build-check-detect.js +23 -15
  65. package/dist/src/build/build-check-events.js +25 -6
  66. package/dist/src/build/build-check-report.js +49 -9
  67. package/dist/src/build/build-check-scoped.js +18 -11
  68. package/dist/src/build/build-check.js +2 -1
  69. package/dist/src/build/build-info.js +2 -3
  70. package/dist/src/build/build-stage.js +9 -14
  71. package/dist/src/build/dep-install.js +1 -2
  72. package/dist/src/change/change-data.js +123 -31
  73. package/dist/src/change/change-render.js +46 -5
  74. package/dist/src/cli/brief-command.js +3 -4
  75. package/dist/src/cli/cli-args.js +118 -33
  76. package/dist/src/cli/cli-command-args.js +46 -18
  77. package/dist/src/cli/cli-editor.js +2 -1
  78. package/dist/src/cli/cli-flag-specs.js +150 -45
  79. package/dist/src/cli/cli-init.js +51 -0
  80. package/dist/src/cli/cli-marker-commands.js +4 -4
  81. package/dist/src/cli/cli-query-commands.js +308 -590
  82. package/dist/src/cli/cli-repo-gate.js +11 -1
  83. package/dist/src/cli/cli-role-command.js +420 -0
  84. package/dist/src/cli/cli-start-summary.js +160 -0
  85. package/dist/src/cli/{cli-run.js → cli-start.js} +165 -237
  86. package/dist/src/cli/completion.js +149 -22
  87. package/dist/src/cli/config-commands.js +41 -18
  88. package/dist/src/cli/follow-view.js +75 -0
  89. package/dist/src/cli/help.js +189 -73
  90. package/dist/src/cli/log-commands.js +148 -82
  91. package/dist/src/cli/question-commands.js +205 -10
  92. package/dist/src/cli.js +106 -43
  93. package/dist/src/collections.js +26 -10
  94. package/dist/src/concurrency/check-permit.js +20 -3
  95. package/dist/src/concurrency/lock.js +17 -13
  96. package/dist/src/concurrency/provider-lease.js +44 -8
  97. package/dist/src/concurrency/semaphore.js +102 -26
  98. package/dist/src/config/config-aliases.js +0 -1
  99. package/dist/src/config/config-editable-keys.js +13 -1
  100. package/dist/src/config/config-fallbacks.js +93 -56
  101. package/dist/src/config/config-field-checks.js +40 -10
  102. package/dist/src/config/config-keys.js +30 -8
  103. package/dist/src/config/config-live.js +63 -24
  104. package/dist/src/config/config-request.js +137 -0
  105. package/dist/src/config/config-schema.js +31 -24
  106. package/dist/src/config/config-selector-checks.js +123 -0
  107. package/dist/src/config/config-validation-agents.js +80 -0
  108. package/dist/src/config/config-validation-model.js +116 -0
  109. package/dist/src/config/config-validation.js +141 -199
  110. package/dist/src/config/config-views.js +127 -53
  111. package/dist/src/config/config-write-core.js +135 -0
  112. package/dist/src/config/config-write.js +131 -264
  113. package/dist/src/config/config.js +48 -38
  114. package/dist/src/config/model-selector.js +34 -34
  115. package/dist/src/doctor/doctor-backlog-bugs.js +287 -0
  116. package/dist/src/doctor/doctor-backlog-plans.js +263 -0
  117. package/dist/src/doctor/doctor-backlog.js +100 -496
  118. package/dist/src/doctor/doctor-check-names.js +81 -0
  119. package/dist/src/doctor/doctor-checks.js +52 -579
  120. package/dist/src/doctor/doctor-claude-checks.js +145 -0
  121. package/dist/src/doctor/doctor-codex-checks.js +279 -0
  122. package/dist/src/doctor/doctor-contention-checks.js +380 -0
  123. package/dist/src/doctor/doctor-holds.js +82 -66
  124. package/dist/src/doctor/doctor-install.js +2 -1
  125. package/dist/src/doctor/doctor-model-checks.js +236 -402
  126. package/dist/src/doctor/doctor-notify.js +3 -4
  127. package/dist/src/doctor/doctor-opencode-checks.js +162 -0
  128. package/dist/src/doctor/doctor-orphans.js +182 -63
  129. package/dist/src/doctor/doctor-project-checks.js +303 -0
  130. package/dist/src/doctor/doctor.js +121 -77
  131. package/dist/src/doctor/image-probe.js +21 -8
  132. package/dist/src/doctor/outcome.js +3 -2
  133. package/dist/src/doctor/probe.js +7 -11
  134. package/dist/src/events/event-format.js +181 -86
  135. package/dist/src/events/event-labels.js +12 -5
  136. package/dist/src/events/event-legacy.js +39 -15
  137. package/dist/src/events/event-read.js +39 -39
  138. package/dist/src/events/event-tally.js +16 -0
  139. package/dist/src/events/event-window.js +60 -24
  140. package/dist/src/events/events.js +30 -13
  141. package/dist/src/events/notify.js +7 -7
  142. package/dist/src/failure/failure-cluster.js +12 -12
  143. package/dist/src/failure/failure-data.js +56 -56
  144. package/dist/src/failure/failure-render.js +31 -23
  145. package/dist/src/failure/failure-state-change.js +17 -12
  146. package/dist/src/failure/time-spend.js +99 -35
  147. package/dist/src/files/files.js +111 -35
  148. package/dist/src/files/json-files.js +3 -3
  149. package/dist/src/files/json-object.js +28 -7
  150. package/dist/src/files/stat-cache.js +2 -2
  151. package/dist/src/files/tail.js +1 -1
  152. package/dist/src/gates/bootstrap-gates.js +4 -5
  153. package/dist/src/gates/budget-gates.js +177 -84
  154. package/dist/src/gates/disk-gate.js +1 -1
  155. package/dist/src/gates/gate-polls.js +108 -58
  156. package/dist/src/gates/maintenance-quota.js +10 -13
  157. package/dist/src/gates/pause-gates.js +28 -10
  158. package/dist/src/gates/readiness.js +7 -3
  159. package/dist/src/gates/role-cap-gates.js +8 -8
  160. package/dist/src/gates/startup-gate.js +94 -32
  161. package/dist/src/gates/streak-gate.js +54 -30
  162. package/dist/src/git/commit-message.js +77 -46
  163. package/dist/src/git/git-diff.js +16 -10
  164. package/dist/src/git/git-run.js +79 -38
  165. package/dist/src/git/git.js +36 -34
  166. package/dist/src/git/slot-salvage.js +253 -0
  167. package/dist/src/git/slots-state.js +6 -6
  168. package/dist/src/git/worktree-pool.js +132 -38
  169. package/dist/src/git/worktree-use.js +4 -11
  170. package/dist/src/git/worktree.js +94 -37
  171. package/dist/src/gui/gui-admin-endpoints.js +96 -0
  172. package/dist/src/gui/gui-args.js +79 -8
  173. package/dist/src/gui/gui-backlog-commands.js +42 -18
  174. package/dist/src/gui/gui-backlog-endpoints.js +155 -0
  175. package/dist/src/gui/gui-command.js +8 -13
  176. package/dist/src/gui/gui-config-commands.js +94 -72
  177. package/dist/src/gui/gui-endpoint-commands.js +4 -3
  178. package/dist/src/gui/gui-endpoint-common.js +4 -4
  179. package/dist/src/gui/gui-endpoints.js +11 -380
  180. package/dist/src/gui/gui-history-endpoints.js +301 -0
  181. package/dist/src/gui/gui-operator-commands.js +108 -13
  182. package/dist/src/gui/gui-prompt-commands.js +11 -14
  183. package/dist/src/gui/gui-report-endpoints.js +90 -0
  184. package/dist/src/gui/gui-server.js +11 -4
  185. package/dist/src/gui/gui-team-commands.js +7 -7
  186. package/dist/src/gui/http-body.js +1 -1
  187. package/dist/src/history/history-data.js +122 -234
  188. package/dist/src/history/history-queued.js +147 -0
  189. package/dist/src/history/history-render.js +42 -22
  190. package/dist/src/history/history.js +193 -49
  191. package/dist/src/inbox/inbox-attachments.js +29 -30
  192. package/dist/src/inbox/inbox-cancel.js +10 -11
  193. package/dist/src/inbox/inbox-edit.js +9 -9
  194. package/dist/src/inbox/inbox-submit.js +18 -18
  195. package/dist/src/inbox/inbox-top.js +25 -9
  196. package/dist/src/inbox/inbox.js +42 -43
  197. package/dist/src/inbox/pending-prompt.js +26 -26
  198. package/dist/src/inbox/prompt-commands.js +62 -49
  199. package/dist/src/inbox/prompt-not-before.js +2 -2
  200. package/dist/src/init/init-templates.js +1 -2
  201. package/dist/src/init/init.js +28 -23
  202. package/dist/src/merge/backlog-conflicts.js +215 -35
  203. package/dist/src/merge/merge-batch.js +14 -8
  204. package/dist/src/merge/merge-cancel.js +53 -0
  205. package/dist/src/merge/merge-check-failures.js +44 -22
  206. package/dist/src/merge/merge-core.js +81 -32
  207. package/dist/src/merge/merge-diff.js +5 -4
  208. package/dist/src/merge/merge-drain.js +150 -11
  209. package/dist/src/merge/merge-git.js +177 -38
  210. package/dist/src/merge/merge-pipeline.js +15 -3
  211. package/dist/src/merge/merge-post-review.js +90 -0
  212. package/dist/src/merge/merge-queue-render.js +49 -11
  213. package/dist/src/merge/merge-queue.js +31 -15
  214. package/dist/src/merge/merge-requeue.js +102 -0
  215. package/dist/src/merge/merge-slot.js +61 -16
  216. package/dist/src/merge/merge-stack.js +33 -11
  217. package/dist/src/merge/merge-to-main.js +86 -27
  218. package/dist/src/merge/merge-top.js +69 -0
  219. package/dist/src/merge/merge-verify.js +12 -10
  220. package/dist/src/notes-cli.js +30 -15
  221. package/dist/src/operator/operator-commands.js +192 -81
  222. package/dist/src/operator/operator-intent.js +69 -42
  223. package/dist/src/operator/operator-requests.js +35 -40
  224. package/dist/src/operator/retire.js +5 -6
  225. package/dist/src/operator/revert.js +142 -34
  226. package/dist/src/orchestrator/orchestrator-launch.js +60 -52
  227. package/dist/src/orchestrator/orchestrator-poll.js +320 -0
  228. package/dist/src/orchestrator/orchestrator-scheduling.js +68 -369
  229. package/dist/src/orchestrator/orchestrator.js +128 -369
  230. package/dist/src/orchestrator/retention.js +42 -8
  231. package/dist/src/orchestrator/scheduling-admit.js +116 -0
  232. package/dist/src/orchestrator/scheduling-scope.js +201 -0
  233. package/dist/src/orchestrator/scheduling-skip.js +185 -0
  234. package/dist/src/orchestrator/startup-migration-steps.js +8 -7
  235. package/dist/src/orchestrator/startup-migrations.js +11 -7
  236. package/dist/src/paths.js +116 -49
  237. package/dist/src/pi/command-shape.js +12 -1
  238. package/dist/src/pi/pi-args.js +6 -4
  239. package/dist/src/pi/pi-backend.js +6 -6
  240. package/dist/src/pi/pi-bin.js +54 -63
  241. package/dist/src/pi/pi-listed-models.js +53 -0
  242. package/dist/src/pi/pi-models.js +63 -30
  243. package/dist/src/pi/pi-run-result.js +1 -1
  244. package/dist/src/pi/pi-settings.js +1 -1
  245. package/dist/src/pi/pi-stream.js +6 -6
  246. package/dist/src/pi/pi-watchdogs.js +18 -19
  247. package/dist/src/pi-extension/bounded-output.js +5 -3
  248. package/dist/src/pi-extension/context-budget.js +2 -2
  249. package/dist/src/pi-extension/context-shake.js +1 -1
  250. package/dist/src/pi-extension/context-window.js +0 -1
  251. package/dist/src/pi-extension/full-output.js +3 -10
  252. package/dist/src/pi-extension/strip-thinking.js +0 -1
  253. package/dist/src/pi-extension/trim-system-prompt.js +3 -4
  254. package/dist/src/pi-extension/write-guard.js +37 -0
  255. package/dist/src/process/launch-services.js +1 -2
  256. package/dist/src/process/node-bin.js +16 -0
  257. package/dist/src/process/process-group.js +8 -18
  258. package/dist/src/process/process.js +41 -2
  259. package/dist/src/process/run-marker.js +18 -6
  260. package/dist/src/process/supervisor.js +22 -21
  261. package/dist/src/prompt/dialect.js +17 -2
  262. package/dist/src/prompt/gate-notes.js +203 -0
  263. package/dist/src/prompt/gate-prompts.js +121 -211
  264. package/dist/src/prompt/notes-backlog.js +181 -0
  265. package/dist/src/prompt/principles.js +1 -2
  266. package/dist/src/prompt/prompt-followup.js +19 -14
  267. package/dist/src/prompt/prompt.js +129 -204
  268. package/dist/src/redeploy/redeploy-policy.js +24 -19
  269. package/dist/src/redeploy/redeploy-probes.js +1 -1
  270. package/dist/src/redeploy/redeploy.js +2 -3
  271. package/dist/src/redeploy/redeployer.js +20 -20
  272. package/dist/src/redeploy/self-reload.js +2 -1
  273. package/dist/src/report/report-data.js +161 -77
  274. package/dist/src/report/report-fold.js +129 -52
  275. package/dist/src/report/report-main-red.js +55 -0
  276. package/dist/src/report/report-render.js +133 -64
  277. package/dist/src/report/report-types.js +1 -0
  278. package/dist/src/report/report.js +26 -9
  279. package/dist/src/request-timeouts.js +2 -2
  280. package/dist/src/review/known-flakes.js +100 -1
  281. package/dist/src/review/post-merge-review.js +106 -0
  282. package/dist/src/review/protected-edits.js +4 -5
  283. package/dist/src/review/review-followup.js +8 -8
  284. package/dist/src/review/review-precheck.js +66 -23
  285. package/dist/src/review/review-run.js +395 -0
  286. package/dist/src/review/review-verdict.js +2 -2
  287. package/dist/src/review/review.js +155 -360
  288. package/dist/src/review/secret-leaks.js +36 -16
  289. package/dist/src/review/suite-rerun.js +0 -1
  290. package/dist/src/roles/legacy-role-migration.js +80 -7
  291. package/dist/src/roles/role-catalog.js +69 -12
  292. package/dist/src/roles/role-guidance.js +55 -20
  293. package/dist/src/roles/role-labels.js +1 -2
  294. package/dist/src/roles/role-render.js +67 -50
  295. package/dist/src/roles/role-view.js +59 -47
  296. package/dist/src/roles/roles.js +31 -31
  297. package/dist/src/roles/runner-ids.js +97 -0
  298. package/dist/src/run/acceptance-greps.js +119 -0
  299. package/dist/src/run/feedback-filings.js +51 -0
  300. package/dist/src/{tick → run}/qa-coverage.js +9 -27
  301. package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
  302. package/dist/src/run/run-detail-data.js +179 -0
  303. package/dist/src/run/run-detail-render.js +98 -0
  304. package/dist/src/run/run-detail.js +168 -0
  305. package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +72 -60
  306. package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
  307. package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +108 -73
  308. package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
  309. package/dist/src/{tick/tick-stage.js → run/run-stage.js} +108 -50
  310. package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
  311. package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
  312. package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +91 -56
  313. package/dist/src/run/selfcheck-digest.js +40 -0
  314. package/dist/src/run/stage-backlog-checks.js +249 -0
  315. package/dist/src/run/stage-check.js +172 -0
  316. package/dist/src/run/stage-path-checks.js +730 -0
  317. package/dist/src/{loop → runner}/leftover.js +80 -25
  318. package/dist/src/{loop → runner}/model-fallback.js +12 -12
  319. package/dist/src/runner/revision.js +202 -0
  320. package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
  321. package/dist/src/runner/runner-outcome.js +107 -0
  322. package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
  323. package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +21 -15
  324. package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +218 -240
  325. package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
  326. package/dist/src/runner/runner-state.js +102 -0
  327. package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
  328. package/dist/src/scheduling/backoff.js +38 -38
  329. package/dist/src/scheduling/claims.js +35 -22
  330. package/dist/src/scheduling/once-round.js +22 -22
  331. package/dist/src/scheduling/quiet-hours.js +5 -6
  332. package/dist/src/scheduling/scheduling.js +74 -64
  333. package/dist/src/scheduling/work-merged-cache.js +2 -2
  334. package/dist/src/status/status-budget.js +55 -47
  335. package/dist/src/status/status-data.js +43 -16
  336. package/dist/src/status/status-inbox.js +2 -2
  337. package/dist/src/status/status-install.js +0 -1
  338. package/dist/src/status/status-polls.js +28 -28
  339. package/dist/src/status/status-roles.js +39 -13
  340. package/dist/src/team/error-storm.js +8 -8
  341. package/dist/src/team/failure-spread.js +2 -2
  342. package/dist/src/team/orchestrator-info.js +15 -9
  343. package/dist/src/team/reclaim.js +5 -6
  344. package/dist/src/team/team-hold.js +7 -7
  345. package/dist/src/team/team-polls.js +91 -50
  346. package/dist/src/team/team-state.js +8 -2
  347. package/dist/src/text/datetime.js +63 -17
  348. package/dist/src/text/format.js +48 -11
  349. package/dist/src/text/markdown.js +8 -5
  350. package/dist/src/text/phrases.js +57 -23
  351. package/dist/src/text/suggest.js +1 -1
  352. package/dist/src/text/text.js +67 -13
  353. package/dist/src/ui/badges.js +65 -37
  354. package/dist/src/ui/gui/gui-client-backlog.js +292 -0
  355. package/dist/src/ui/gui/gui-client-boot.js +32 -26
  356. package/dist/src/ui/gui/gui-client-composer.js +8 -9
  357. package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
  358. package/dist/src/ui/gui/gui-client-drawer.js +126 -285
  359. package/dist/src/ui/gui/gui-client-feed.js +271 -0
  360. package/dist/src/ui/gui/gui-client-history.js +434 -67
  361. package/dist/src/ui/gui/gui-client-labels.js +1 -2
  362. package/dist/src/ui/gui/gui-client-markdown.js +1 -2
  363. package/dist/src/ui/gui/gui-client-model.js +27 -36
  364. package/dist/src/ui/gui/gui-client-operator.js +55 -51
  365. package/dist/src/ui/gui/gui-client-pending.js +227 -23
  366. package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
  367. package/dist/src/ui/gui/gui-client-report.js +87 -427
  368. package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
  369. package/dist/src/ui/gui/gui-client-settings.js +56 -4
  370. package/dist/src/ui/gui/gui-client-team.js +25 -465
  371. package/dist/src/ui/gui/gui-client.js +27 -25
  372. package/dist/src/ui/gui/gui-icons.js +0 -2
  373. package/dist/src/ui/gui/gui-page.js +9 -7
  374. package/dist/src/ui/gui/gui-styles.js +34 -34
  375. package/dist/src/ui/progress-data.js +9 -11
  376. package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +31 -31
  377. package/dist/src/ui/status-model.js +133 -125
  378. package/dist/src/ui/status-payload.js +136 -49
  379. package/dist/src/ui/status-render.js +101 -82
  380. package/dist/src/ui/team-alerts.js +248 -57
  381. package/dist/src/ui/tone.js +5 -6
  382. package/dist/src/ui/transcript-run.js +58 -0
  383. package/dist/src/ui/transcript-tail.js +28 -20
  384. package/dist/src/ui/transcript.js +63 -23
  385. package/dist/src/ui/tui/tui-app.js +0 -1
  386. package/dist/src/ui/tui/tui-backlog.js +51 -14
  387. package/dist/src/ui/tui/tui-editor-answer.js +27 -31
  388. package/dist/src/ui/tui/tui-editor-attach.js +3 -5
  389. package/dist/src/ui/tui/tui-editor-backends.js +132 -0
  390. package/dist/src/ui/tui/tui-editor-brief.js +10 -12
  391. package/dist/src/ui/tui/tui-editor-budget.js +35 -31
  392. package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
  393. package/dist/src/ui/tui/tui-editor-concurrency.js +24 -0
  394. package/dist/src/ui/tui/tui-editor-config.js +86 -0
  395. package/dist/src/ui/tui/tui-editor-entry.js +36 -47
  396. package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
  397. package/dist/src/ui/tui/tui-editor-instances.js +37 -0
  398. package/dist/src/ui/tui/tui-editor-instructions.js +136 -0
  399. package/dist/src/ui/tui/tui-editor-interval.js +11 -9
  400. package/dist/src/ui/tui/tui-editor-model.js +29 -0
  401. package/dist/src/ui/tui/tui-editor-notes.js +3 -5
  402. package/dist/src/ui/tui/tui-editor-notify.js +25 -0
  403. package/dist/src/ui/tui/tui-editor-pause.js +48 -59
  404. package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
  405. package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
  406. package/dist/src/ui/tui/tui-editor-queued.js +5 -9
  407. package/dist/src/ui/tui/tui-editor-quiet.js +99 -35
  408. package/dist/src/ui/tui/tui-editor-retire.js +4 -6
  409. package/dist/src/ui/tui/tui-editor-role-key.js +76 -0
  410. package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
  411. package/dist/src/ui/tui/tui-editor-search.js +45 -33
  412. package/dist/src/ui/tui/tui-editor-session.js +60 -34
  413. package/dist/src/ui/tui/tui-editor-slots.js +23 -0
  414. package/dist/src/ui/tui/tui-editors.js +35 -2
  415. package/dist/src/ui/tui/tui-frame.js +56 -11
  416. package/dist/src/ui/tui/tui-history.js +8 -9
  417. package/dist/src/ui/tui/tui-input.js +13 -11
  418. package/dist/src/ui/tui/tui-keymap.js +0 -1
  419. package/dist/src/ui/tui/tui-keys.js +24 -9
  420. package/dist/src/ui/tui/tui-pane-activity.js +14 -11
  421. package/dist/src/ui/tui/tui-pane-backlog.js +171 -126
  422. package/dist/src/ui/tui/tui-pane-brief.js +0 -1
  423. package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
  424. package/dist/src/ui/tui/tui-pane-health.js +58 -8
  425. package/dist/src/ui/tui/tui-pane-history.js +402 -82
  426. package/dist/src/ui/tui/tui-pane-pending.js +308 -13
  427. package/dist/src/ui/tui/tui-pane-queued.js +71 -55
  428. package/dist/src/ui/tui/tui-pane-report.js +46 -14
  429. package/dist/src/ui/tui/tui-pane-session.js +25 -6
  430. package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
  431. package/dist/src/ui/tui/tui-pane.js +80 -23
  432. package/dist/src/ui/tui/tui-panes.js +100 -30
  433. package/dist/src/ui/tui/tui.js +14 -14
  434. package/dist/src/verdict/fix-claim.js +82 -32
  435. package/dist/src/verdict/no-change.js +4 -4
  436. package/dist/src/verdict/refusal.js +40 -16
  437. package/dist/src/verdict/reply-contract.js +42 -31
  438. package/dist/src/version.js +0 -1
  439. package/package.json +2 -1
  440. package/dist/src/loop/revision.js +0 -70
  441. package/dist/src/roles/loop-ids.js +0 -77
  442. package/dist/src/tick/stage-check.js +0 -419
  443. package/dist/src/tick/telemetry-digest.js +0 -25
  444. package/dist/src/tick/tick-detail-data.js +0 -108
  445. package/dist/src/tick/tick-detail.js +0 -163
  446. package/dist/src/ui/gui/gui-client-sound.js +0 -53
@@ -1,19 +1,28 @@
1
- // vocabulary: plain
2
- import { eventRole, eventTick, eventUsage } from "./event-read.js";
1
+ import { claudeUtilizationPct } from "../agent/claude/claude-account.js";
2
+ import { eventRole, eventRun, eventUsage } from "./event-read.js";
3
3
  import { eventTypeLabel } from "./event-labels.js";
4
4
  import { roleLabel } from "../roles/role-labels.js";
5
5
  import { TUMWATER_SOURCE } from "./events.js";
6
- import { ORCHESTRATOR_STOPPED, backendKindPhrase, budgetPhrase, firstReason, teamPausePhrase, teamResumePhrase, holdPhrase, plural, rolePausePhrase, roleResumePhrase, rolesPhrase, shortSpanPhrase } from "../text/phrases.js";
6
+ import { orchestratorStopPhrase, backendKindPhrase, budgetPhrase, firstReason, teamPausePhrase, teamResumePhrase, holdPhrase, plural, rolePausePhrase, roleResumePhrase, rolesPhrase, shortSpanPhrase, PAUSE_EXPIRED_SUFFIX } from "../text/phrases.js";
7
7
  import { compactTokens, formatGB, shortSha, usd } from "../text/format.js";
8
8
  import { padToWidth } from "../text/text-width.js";
9
- import { squash, textOr, truncate } from "../text/text.js";
10
- import { formatTimestamp, formatTime } from "../text/datetime.js";
9
+ import { isNonBlankString, joinWithMore, squash, textOr, truncate } from "../text/text.js";
10
+ import { formatClock, formatTimestamp } from "../text/datetime.js";
11
11
  import { finiteNumber, isJsonObject, stringList } from "../files/json-object.js";
12
12
  import { fallbackTierEntries } from "../budget/budget.js";
13
+ /** The " (most by feature $30.00, plan $10.01)" phrase for `topRoles`; "" if none is usable. */
14
+ function mostByPhrase(value) {
15
+ const top = Array.isArray(value)
16
+ ? value.flatMap((r) => isJsonObject(r) && typeof r.role === "string" && Number.isFinite(r.spentUsd)
17
+ ? [`${r.role} ${usd(r.spentUsd)}`]
18
+ : [])
19
+ : [];
20
+ return top.length > 0 ? ` (most by ${top.join(", ")})` : "";
21
+ }
13
22
  /** The `<N> tok · $<spent>` usage fragment every event that records a run's cost shares (run_end,
14
23
  * merge_done): the usage numbers arrive via eventUsage (the loose-typing coercion every usage
15
24
  * consumer shares), rendered through the shared token/money formats (compactTokens, usd) in one
16
- * place. Either part is omitted when zero or absent, so skipped ticks and zero-usage merges render
25
+ * place. Either part is omitted when zero or absent, so skipped runs and zero-usage merges render
17
26
  * byte-identical to a pre-feature line — no leading or trailing separator. "·" is the separator
18
27
  * the budget badge already uses. Sentence contexts prepend their own " · " via usagePhrase;
19
28
  * table-cell contexts (history's usage column) use the bare text. */
@@ -54,19 +63,34 @@ function gigabytes(v) {
54
63
  function minutes(ms) {
55
64
  return Math.round(Number(finiteNumber(ms, 0)) / 60_000);
56
65
  }
66
+ /** ` until HH:MM:SS` for a timestamped pause event (team_paused/role_paused) whose until time is
67
+ * present, or "" when it is missing or non-numeric — the same corrupt-field rule windowResetPhrase
68
+ * applies (a torn or hand-edited line must not coerce Number("…") into a fabricated clock). Shared
69
+ * by the event feed's pauseSuffix and the failure digest's pause lines so the three cannot drift on
70
+ * the spacing or the clock format. */
71
+ export function untilClockSuffix(until) {
72
+ const ms = finiteNumber(until, null);
73
+ return ms === null ? "" : ` until ${formatClock(ms)}`;
74
+ }
75
+ /** ` until HH:MM:SS` and `: reason` for a paused event, each "" when its field is absent or
76
+ * the wrong type (the corrupt-field rule windowResetPhrase applies). The until half is
77
+ * untilClockSuffix, so the feed and digest aggregators share it. */
78
+ function pauseSuffix(until, reason) {
79
+ return `${untilClockSuffix(until)}${isNonBlankString(reason) ? `: ${reason}` : ""}`;
80
+ }
57
81
  /** `, resets HH:MM` for a subscription-window event carrying resetsAt, or "" when the field is
58
82
  * absent or non-numeric (a torn line, or a window the account did not stamp) — the same
59
83
  * corrupt-field rule the other optional-field renderers apply. */
60
84
  function windowResetPhrase(e) {
61
85
  const at = finiteNumber(e.resetsAt, null);
62
- return at === null ? "" : `, resets ${formatTime(new Date(at))}`;
86
+ return at === null ? "" : `, resets ${formatClock(at)}`;
63
87
  }
64
88
  /** The parenthesized ` (<window>)` a quiet-hours event names when it carries one, or "" when
65
89
  * the field is absent or blank (pollQuietHoursGate may send no window, so a routine hold must
66
90
  * not fabricate a pair). Shared by the started and ended lines so the two cannot drift on the
67
91
  * window's presence or format. */
68
92
  function windowSuffix(e) {
69
- return typeof e.window === "string" && e.window.trim() ? ` (${textOr(e.window)})` : "";
93
+ return isNonBlankString(e.window) ? ` (${textOr(e.window)})` : "";
70
94
  }
71
95
  /** The backend a hold event names, rendered as " at <provider>" when one is configured
72
96
  * (pollTeamHold omits it for pi's default) and "" otherwise, so a multi-provider team can
@@ -80,13 +104,20 @@ function providerAt(e) {
80
104
  function providerScope(e) {
81
105
  return typeof e.provider === "string" ? "that provider" : "every provider";
82
106
  }
107
+ /** A subscription-window event's backend, rendered as the id the event names ("claude", or a
108
+ * later backend's own id), falling back to "claude" for an event written before the window
109
+ * events carried a `backend`. A claude-only team's lines read exactly as the older
110
+ * `claude_window_*` lines did. */
111
+ function backendLabel(e) {
112
+ return typeof e.backend === "string" && e.backend !== "" ? e.backend : "claude";
113
+ }
83
114
  /** The provider/model pair a fallback-ish line names, as "provider/model", defaulting each
84
115
  * half when the event omits it (a bare pi run carries neither). Shared by the budget_fallback
85
116
  * and model_fallback lines so the pair's shape and defaults cannot drift between them. */
86
117
  function providerModelPair(e, providerDefault, modelDefault) {
87
118
  return `${textOr(e.provider, providerDefault)}/${textOr(e.model, modelDefault)}`;
88
119
  }
89
- /** An event's outcome string — a tick's `result`, or a merge's or build check's `status`,
120
+ /** An event's outcome string — a run's `result`, or a merge's or build check's `status`,
90
121
  * whichever the event carries; undefined when neither does. The activity feeds' tone and
91
122
  * filter key (tone.ts's eventKind takes it beside the event type), also shipped as the
92
123
  * status payload's per-event `result` field, so the two surfaces cannot disagree on what a
@@ -96,9 +127,9 @@ export function eventResult(e) {
96
127
  }
97
128
  /** eventResult as a display cell: the outcome text when the event carries one, else "?". A
98
129
  * torn or hand-edited event whose result/status is missing or non-string — the loose payload
99
- * HarnessEvent's index signature hands over unvalidated — reads "?" like eventRole's loop and
100
- * eventTick's tick, never String(undefined)'s literal "undefined". The display surfaces — the
101
- * event line, the history row, the tick drill-down — use this; eventResult stays undefined
130
+ * HarnessEvent's index signature hands over unvalidated — reads "?" like eventRole's role and
131
+ * eventRun's run, never String(undefined)'s literal "undefined". The display surfaces — the
132
+ * event line, the history row, the run drill-down — use this; eventResult stays undefined
102
133
  * for the tone and filter keys, which must tell "no outcome" apart from a real one. */
103
134
  export function outcomeText(e) {
104
135
  return eventResult(e) ?? "?";
@@ -117,31 +148,31 @@ export function deferredReasonText(e) {
117
148
  /** Human one-liner for an event, shared by `logs`, `run` output, and the TUI activity pane:
118
149
  * the local `YYYY-MM-DD HH:MM:SS` stamp (formatTimestamp — the same zero-padded, locale-
119
150
  * independent phrasing the history table and transcript run separators print, not
120
- * toLocaleTimeString's locale-dependent "3:05:12 PM"), the loop padded to a column, then the
151
+ * toLocaleTimeString's locale-dependent "3:05:12 PM"), the role padded to a column, then the
121
152
  * event's message (eventMessage).
122
153
  * Presentation only: depends on the event shape (events.ts's HarnessEvent), not on its log
123
154
  * I/O — so display surfaces never import formatting from the logging module. */
124
155
  export function formatEvent(e) {
125
156
  const time = formatTimestamp(e.ts);
126
157
  // Padded in terminal display columns (padToWidth), not String#padEnd's UTF-16 code units:
127
- // a custom loop name holding a wide character (CJK, emoji) counts one code unit but two
158
+ // a custom role name holding a wide character (CJK, emoji) counts one code unit but two
128
159
  // display columns, so a padEnd cell rendered wider than its column and the message column
129
- // sat right of every ASCII row's (the same unit mistake the aligned tables shed). The loop
130
- // itself goes through eventRole, so a torn or hand-edited event with no loop shows "?" like
160
+ // sat right of every ASCII row's (the same unit mistake the aligned tables shed). The role
161
+ // itself goes through eventRole, so a torn or hand-edited event with no role shows "?" like
131
162
  // the usage report and failure digest, never String(undefined)'s literal "undefined".
132
163
  const source = eventRole(e);
133
- const loop = padToWidth(source === TUMWATER_SOURCE ? "tumwater" : roleLabel(source), 10);
134
- return `${time} ${loop} ${eventMessage(e)}`;
164
+ const roleCell = padToWidth(source === TUMWATER_SOURCE ? "tumwater" : roleLabel(source), 10);
165
+ return `${time} ${roleCell} ${eventMessage(e)}`;
135
166
  }
136
- /** One event as the dashboard's activity feed consumes it: `result` is a tick's outcome, a
167
+ /** One event as the dashboard's activity feed consumes it: `result` is a run's outcome, a
137
168
  * merge's result, or a build check's status — whichever string the event carries. The event
138
169
  * layer owns the event shape's projection, so both the status poll (ui/status-payload.ts) and
139
- * the slice endpoint (gui/gui-endpoints.ts's handleLogs) read the same fields, and a feed row's
140
- * shape cannot drift between them. */
170
+ * the slice endpoint (gui/gui-history-endpoints.ts's handleLogs) read the same fields, and a feed
171
+ * row's shape cannot drift between them. */
141
172
  export function eventFeedItem(e) {
142
173
  const result = eventResult(e);
143
- // eventRole renders a missing/empty loop as "?" like the reports, so a torn or hand-edited
144
- // event never shows a literal "undefined" loop chip in the dashboard's activity feed.
174
+ // eventRole renders a missing/empty role as "?" like the reports, so a torn or hand-edited
175
+ // event never shows a literal "undefined" role chip in the dashboard's activity feed.
145
176
  return { ts: e.ts, source: eventRole(e), type: e.type, ...(result === undefined ? {} : { result }), message: eventMessage(e) };
146
177
  }
147
178
  /** The feed's `--grep`/Search match rule: case-insensitive substring over the raw event type
@@ -151,39 +182,52 @@ export function eventFeedItem(e) {
151
182
  export function eventMatchesGrep(e, patternLower) {
152
183
  return `${e.type} ${formatEvent(e)}`.toLowerCase().includes(patternLower);
153
184
  }
154
- /** An event's message alone — formatEvent's line without the time and loop columns. The GUI's
155
- * activity feed renders the time and loop itself (relative ages, loop chips), so the payload
156
- * ships this beside the event's type and loop instead of making the page re-parse a line. */
185
+ /** An event's message alone — formatEvent's line without the time and role columns. The GUI's
186
+ * activity feed renders the time and role itself (relative ages, role chips), so the payload
187
+ * ships this beside the event's type and role instead of making the page re-parse a line. */
157
188
  export function eventMessage(e) {
158
189
  switch (e.type) {
159
190
  case "run_start":
160
- return `run #${eventTick(e)} started${e.model !== undefined ? ` on ${textOr(e.model)}` : ""}`;
191
+ return `run #${eventRun(e)} started${e.model !== undefined ? ` on ${textOr(e.model)}` : ""}`;
161
192
  case "run_end": {
162
193
  // The payload that explains the outcome: summary for changed/refused/rejected/
163
- // review_error ticks, error (lastError) for error and merge-failed ones. Showing it
194
+ // review_error runs, error (lastError) for error and merge-failed ones. Showing it
164
195
  // for every result that carries one keeps the event feed self-explanatory — a bare
165
- // "tick #N refused" would force operators to open the transcript for the reason.
196
+ // "run #N refused" would force operators to open the transcript for the reason.
166
197
  const extra = e.summary ? ` — ${textOr(e.summary)}` : e.error ? ` — ${textOr(e.error)}` : "";
167
- // Per-tick usage: where the day's spend went.
168
- return `run #${eventTick(e)} ${eventTypeLabel(outcomeText(e))}${extra}${usagePhrase(e)}`;
198
+ // Per-run usage: where the day's spend went.
199
+ return `run #${eventRun(e)} ${eventTypeLabel(outcomeText(e))}${extra}${usagePhrase(e)}`;
169
200
  }
170
201
  case "merged":
171
202
  return `merged ${shortSha(e.commit)} to main — ${textOr(e.summary)}`;
172
203
  case "merge_queued":
173
- // Merge queue 3/5: routine state change (the tick committed and the merge slot is
174
- // ahead) — no warning prefix. The merge's own events follow it.
175
- return `waiting to merge ${shortSha(e.commit)} — ${textOr(e.summary)}`;
204
+ // Merge queue 3/5: routine state change (the run committed and the merge slot is
205
+ // ahead) — no warning prefix. The merge's own events follow it. A retry-bearing event
206
+ // (a re-queued entry) appends its why-it-is-back suffix; a
207
+ // fresh enqueue's line stays byte-identical.
208
+ {
209
+ const base = `waiting to merge ${shortSha(e.commit)} — ${textOr(e.summary)}`;
210
+ return `${base}${typeof e.retry === "string" && e.retry ? ` (re-queued after ${textOr(e.retry)})` : ""}`;
211
+ }
212
+ case "merge_cancelled":
213
+ // Routine operation (the user removed a waiting merge), not a warning — like
214
+ // prompt_cancelled. The merge_cancelled event name carries the payload (commit + summary).
215
+ return `cancelled the merge of ${shortSha(e.commit)} — ${textOr(e.summary)}`;
216
+ case "merge_topped":
217
+ // Routine operation (the user reordered a waiting merge), not a warning — like
218
+ // prompt_topped. The merge_topped event name carries the payload (commit + summary).
219
+ return `moved the merge of ${shortSha(e.commit)} to the front — ${textOr(e.summary)}`;
176
220
  case "merge_done":
177
221
  // The merge slot finished with the change on main; the usage is the merge's own
178
222
  // spend (reviewer + conflict resolution), omitted when zero so review-exempt merges
179
223
  // render bare — the same phrase run_end carries for its run.
180
224
  return `merge of ${shortSha(e.commit)} done${elapsed(e.durationMs)}${usagePhrase(e)}`;
181
225
  case "merge_failed":
182
- // Routine failure detail: the review events themselves (review_rejected/review_failed)
183
- // or the merged/build_check lines carry the reason in the feed.
184
- return `merge of ${shortSha(e.commit)} failed (${eventTypeLabel(outcomeText(e))})${elapsed(e.durationMs)}${typeof e.summary === "string" && e.summary.trim() ? ` — ${truncate(e.summary, 80)}` : ""}`;
226
+ // The event names its reason itself (writeMergeOutcome); the review/check events still
227
+ // carry the full text. An old event without `reason` renders the bare outcome.
228
+ return `merge of ${shortSha(e.commit)} failed (${eventTypeLabel(outcomeText(e))}${isNonBlankString(e.reason) ? `: ${truncate(e.reason.trim(), 120)}` : ""})${elapsed(e.durationMs)}${isNonBlankString(e.summary) ? ` — ${truncate(e.summary, 80)}` : ""}`;
185
229
  case "question_posted":
186
- // Routine operation (a loop asked the user something), not a warning.
230
+ // Routine operation (a role asked the user something), not a warning.
187
231
  // The position is the `<n>` `questions answer` takes, so the notify page is actionable.
188
232
  return `question posted: ${textOr(e.question)}${typeof e.position === "number" && Number.isInteger(e.position) && e.position >= 1 ? ` — question ${e.position} in \`tumwater questions\`; answer it with \`tumwater questions answer ${e.position} "<decision>"\`` : ""}`;
189
233
  case "entry_refused":
@@ -194,16 +238,21 @@ export function eventMessage(e) {
194
238
  return `entry refused: ${textOr(e.reason)} (${files})`;
195
239
  const more = titles.length > 1 ? ` (and ${titles.length - 1} more)` : "";
196
240
  return `entry refused: "${truncate(titles[0], 80)}"${more}: ${textOr(e.reason)} (${files})`;
241
+ case "issue_filed":
242
+ // The feedback observer's filing (plans/feedback-role.md 2/2) is a visible, auditable
243
+ // result — routine operation like question_posted, not a warning. The number is the
244
+ // issue the operator can open; a torn event without it reads "?" never "undefined".
245
+ return `filed issue #${typeof e.issue === "number" ? e.issue : "?"} ${textOr(e.title)} on znation/tumwater`;
197
246
  case "wake":
198
247
  return `woke (${textOr(e.reason)})`;
199
248
  case "run_deferred":
200
249
  // Routine state change (need-based prioritization), like counters_reset — no warning
201
- // prefix. One per deferral episode; the tick's own events cover the episode's end.
250
+ // prefix. One per deferral episode; the run's own events cover the episode's end.
202
251
  return `deferred — ${deferredReasonText(e)}`;
203
252
  case "orchestrator_start":
204
253
  return `tumwater started (pid ${textOr(e.pid)}${e.build ? `, version ${shortSha(e.build)}` : ""})`;
205
254
  case "orchestrator_stop":
206
- return ORCHESTRATOR_STOPPED;
255
+ return orchestratorStopPhrase(e.cause);
207
256
  case "prompt_enqueued":
208
257
  return `message added to the inbox: ${textOr(e.preview)}`;
209
258
  case "prompt_cancelled":
@@ -216,17 +265,17 @@ export function eventMessage(e) {
216
265
  // Routine operation (the user reordered a queued prompt), not a warning.
217
266
  return `inbox message moved to the front: ${textOr(e.preview)}`;
218
267
  case "prompt_taken":
219
- // Routine: a tick dequeued the operator's request to run it.
220
- return `inbox message taken by run #${eventTick(e)}: ${textOr(e.preview)}`;
268
+ // Routine: a run dequeued the operator's request to run it.
269
+ return `inbox message taken by run #${eventRun(e)}: ${textOr(e.preview)}`;
221
270
  case "counters_reset": {
222
- // One role → the event is filed under that loop; several → one harness-level event
271
+ // One role → the event is filed under that role; several → one harness-level event
223
272
  // listing them.
224
273
  const roles = rolesPhrase(e.roles, "");
225
274
  const scope = roles ? ` for ${roles}` : "";
226
275
  return `counters reset${scope} (runs, merges, tokens, cost)`;
227
276
  }
228
277
  case "run_aborted":
229
- // Routine state change (the user stopped one loop's tick), like counters_reset — no
278
+ // Routine state change (the user stopped one role's run), like counters_reset — no
230
279
  // warning prefix. The resulting run_end line carries the user_aborted outcome.
231
280
  return `run stopped by you`;
232
281
  case "review_start": {
@@ -243,7 +292,7 @@ export function eventMessage(e) {
243
292
  const reasons = stringList(e.reasons);
244
293
  // Only the first reason renders; when more exist, say so instead of letting the
245
294
  // one-liner read as the complete verdict — the full list rides in state.lastReview
246
- // into the author's next tick prompt, and this line is the operator's pointer to it.
295
+ // into the author's next run prompt, and this line is the operator's pointer to it.
247
296
  const more = reasons.length > 1
248
297
  ? ` (+${reasons.length - 1} more — the author's next run carries every reason)`
249
298
  : "";
@@ -280,12 +329,12 @@ export function eventMessage(e) {
280
329
  case "budget_warning":
281
330
  // The early page beside budget_paused: the cap is not reached yet, so say so — the
282
331
  // operator still has room to raise it or fix the fallback before the team stops.
283
- return `budget warning — ${budgetPhrase(e.spentUsd, e.capUsd)} of the daily cap spent; runs still start`;
332
+ return `budget warning — ${budgetPhrase(e.spentUsd, e.capUsd)} of the daily cap spent${mostByPhrase(e.topRoles)}; runs still start`;
284
333
  case "budget_paused": {
285
334
  // Routine state change, like counters_reset — no warning prefix. A configured fallback
286
335
  // the gate refused is named here: it is the whole reason the team stopped instead of
287
336
  // switching over, and an operator reading the feed must be able to act on it. So is a
288
- // free fallback the breaker demoted because its ticks kept failing (BUGS.md 2026-09-20):
337
+ // free fallback the breaker demoted because its runs kept failing (BUGS.md 2026-09-20):
289
338
  // the backend, not the price, is what to fix, and the team retries it on its own.
290
339
  // Like role_cap_paused, the line names the lift paths: a raised or removed cap, or the
291
340
  // local day rolling over (the demoted branch's cool-down retry is the automatic third).
@@ -294,7 +343,7 @@ export function eventMessage(e) {
294
343
  : e.fallbackDemoted
295
344
  ? ` (over-cap model ${textOr(e.fallbackDemoted)} is not serving — ${textOr(e.failures)} runs failed in a row on it; one trial run retries it after a cool-down)`
296
345
  : "";
297
- return `budget paused — ${budgetPhrase(e.spentUsd, e.capUsd)} daily cost reached${refused}; roles start no new runs until maxDailyCostUsd is raised or removed in tumwater.json or the local day rolls over`;
346
+ return `budget paused — ${budgetPhrase(e.spentUsd, e.capUsd)} daily cost reached${mostByPhrase(e.topRoles)}${refused}; roles start no new runs until maxDailyCostUsd is raised or removed in tumwater.json or the local day rolls over`;
298
347
  }
299
348
  case "budget_fallback": {
300
349
  // The cap is spent but the team keeps working: name the free model it switched to, the
@@ -304,15 +353,15 @@ export function eventMessage(e) {
304
353
  // strong-tier borrow at a glance; a tier-free event keeps today's single-pair text.
305
354
  const tierEntries = fallbackTierEntries(isJsonObject(e.tiers) ? e.tiers : undefined);
306
355
  if (tierEntries.length >= 2)
307
- return `over-cap model — ${budgetPhrase(e.spentUsd, e.capUsd)} daily cost reached; roles continue on ${tierEntries.join(", ")} (cost n/a)`;
308
- return `over-cap model — ${budgetPhrase(e.spentUsd, e.capUsd)} daily cost reached; roles continue on ${providerModelPair(e, "pi's default provider", "pi's default model")} (cost n/a)`;
356
+ return `over-cap model — ${budgetPhrase(e.spentUsd, e.capUsd)} daily cost reached${mostByPhrase(e.topRoles)}; roles continue on ${tierEntries.join(", ")} (cost n/a)`;
357
+ return `over-cap model — ${budgetPhrase(e.spentUsd, e.capUsd)} daily cost reached${mostByPhrase(e.topRoles)}; roles continue on ${providerModelPair(e, "pi's default provider", "pi's default model")} (cost n/a)`;
309
358
  }
310
359
  case "budget_resumed":
311
360
  return `budget resumed (${budgetPhrase(e.spentUsd, e.capUsd)} today)`;
312
361
  case "budget_handback": {
313
- // Which ticks were handed back: the roles still running on the fallback when the budget
314
- // reopened. Their ticks end `aborted` and resume promptly on the primary, so an operator
315
- // reading the feed can connect the aborted ticks to this one line.
362
+ // Which runs were handed back: the roles still running on the fallback when the budget
363
+ // reopened. Their runs end `aborted` and resume promptly on the primary, so an operator
364
+ // reading the feed can connect the aborted runs to this one line.
316
365
  const roles = rolesPhrase(e.roles, "?");
317
366
  if (e.reason === "window")
318
367
  return `claude subscription window reset: handed ${roles} back to claude`;
@@ -329,33 +378,50 @@ export function eventMessage(e) {
329
378
  }
330
379
  case "team_paused":
331
380
  // Routine state change, like counters_reset — no warning prefix.
332
- return `${teamPausePhrase()} (director keeps running)`;
381
+ return `${teamPausePhrase()}${pauseSuffix(e.until, e.reason)} (director keeps running)`;
333
382
  case "state_migrated":
334
383
  return `state migrated — ${textOr(e.detail)}`;
335
384
  case "team_resumed":
336
- return teamResumePhrase();
385
+ return teamResumePhrase() + (e.expired === true ? PAUSE_EXPIRED_SUFFIX : "");
337
386
  case "role_paused":
338
387
  // Routine state change, like team_paused — no warning prefix.
339
- return `${rolePausePhrase(textOr(e.role))} (the rest of the team keeps running)`;
388
+ return `${rolePausePhrase(textOr(e.role))}${pauseSuffix(e.until, undefined)} (the rest of the team keeps running)`;
340
389
  case "role_resumed":
341
- return roleResumePhrase(textOr(e.role));
390
+ return roleResumePhrase(textOr(e.role)) + (e.expired === true ? PAUSE_EXPIRED_SUFFIX : "");
342
391
  case "role_streak_paused":
343
392
  // Routine-with-explanation, like rate_limit_hold — no warning prefix: the pause IS the
344
393
  // harness handling the failure. Names the streak depth and the lift command, so the
345
- // operator knows why the loop stopped and what to do about it; names the last error
394
+ // operator knows why the role stopped and what to do about it; names the last error
346
395
  // when one is recorded.
347
- return `role ${textOr(e.role)} paused — ${textOr(e.streak)} runs failed in a row${typeof e.lastError === "string" && e.lastError.trim() ? ` — last error: ${squash(e.lastError, 120)}` : ""}; fix the cause and resume it (tumwater resume --role <id>)`;
396
+ return `role ${textOr(e.role)} paused — ${textOr(e.streak)} runs failed in a row${isNonBlankString(e.lastError) ? ` — last error: ${squash(e.lastError, 120)}` : ""}; fix the cause and resume it (tumwater resume --role <id>)`;
348
397
  case "role_cap_warning":
349
398
  // The per-role early page beside role_cap_paused: the cap is not reached yet, so say so —
350
- // the operator still has room to raise it before the loop stops.
399
+ // the operator still has room to raise it before the role stops.
351
400
  return `role ${textOr(e.role)} at ${budgetPhrase(e.spentUsd, e.capUsd)} of its daily cap — it keeps running; raise or remove the cap in tumwater.json before it pauses`;
352
401
  case "role_cap_paused":
353
402
  // Routine state change, like budget_paused — no warning prefix: the pause IS the harness
354
403
  // holding the role's own spend line. Names the role, its spend vs its cap, and the two
355
- // lift paths, so the operator knows why the loop stopped and what to do about it.
404
+ // lift paths, so the operator knows why the role stopped and what to do about it.
356
405
  return `role ${textOr(e.role)} paused — ${budgetPhrase(e.spentUsd, e.capUsd)} of its daily cap spent; it starts no new runs until the cap is raised or removed in tumwater.json or the local day rolls over`;
357
406
  case "role_cap_resumed":
358
407
  return `role ${textOr(e.role)} resumed — it is under its daily cap again and runs again`;
408
+ case "main_check": {
409
+ const failing = Array.isArray(e.failingTests) ? e.failingTests.map(String) : [];
410
+ const covered = Number(e.covered) > 1 ? ` (${textOr(e.covered)} commits)` : "";
411
+ const verdict = e.status === "failed" ? "failed" : "passed";
412
+ const names = failing.length > 0 ? `: ${joinWithMore(failing, 3, "; ")}` : "";
413
+ return `main ${shortSha(String(e.sha ?? ""))}${covered} full check ${verdict}${names}`;
414
+ }
415
+ case "main_culprit": {
416
+ const by = typeof e.runner === "string" && e.runner !== "" ? ` (merged by ${e.runner})` : "";
417
+ return `main turned red at ${shortSha(String(e.commit ?? ""))}${by}; last green ${shortSha(String(e.green ?? ""))}`;
418
+ }
419
+ case "main_revert":
420
+ return `reverted ${shortSha(String(e.commit ?? ""))} (merged by ${textOr(e.runner)}) as ${shortSha(String(e.revert ?? ""))}`;
421
+ case "operator_revert": {
422
+ const undid = typeof e.runner === "string" && e.runner !== "" ? `, undid ${e.runner}` : "";
423
+ return `reverted ${shortSha(String(e.commit ?? ""))} (by operator) as ${shortSha(String(e.revert ?? ""))}${undid}`; // vocabulary-ok: the deliberate-revert writer is the operator role
424
+ }
359
425
  case "main_red_hold":
360
426
  // The message already names the SHA, script, and consequence; the problem tone comes
361
427
  // from PROBLEM_EVENTS.
@@ -363,13 +429,13 @@ export function eventMessage(e) {
363
429
  case "maintenance_quota_hold":
364
430
  // Routine state change, like role_cap_paused — no warning prefix: the hold IS the harness
365
431
  // keeping maintenance merges in proportion to work. Names the window's counts so the
366
- // operator can see why the hygiene loops stopped and what would re-open them.
432
+ // operator can see why the hygiene roles stopped and what would re-open them.
367
433
  return `waiting: maintenance allowance used — ${textOr(e.maint)} maintenance merges against an allowance of ${textOr(e.allowance)} in the last 24h (${textOr(e.work)} work); the maintenance roles wait until the window rolls over`;
368
434
  case "maintenance_quota_resumed":
369
435
  return `maintenance allowance reopened — ${textOr(e.maint)} maintenance merges against an allowance of ${textOr(e.allowance)} in the last 24h; the maintenance roles run again`;
370
436
  case "bootstrap_complete":
371
437
  // Routine state change, like counters_reset — no warning prefix: the team just finished
372
- // its bootstrap and the maintenance loops are admitted.
438
+ // its bootstrap and the maintenance roles are admitted.
373
439
  return `bootstrap complete — ${textOr(e.plansDone)}/${textOr(e.untilPlansDone)} plans done; the maintenance roles now run`;
374
440
  case "disk_low":
375
441
  // Routine state change, like team_paused — no warning prefix: the hold IS the harness
@@ -411,31 +477,34 @@ export function eventMessage(e) {
411
477
  return `backend recovered (${backendKindPhrase(e.kind)})${providerAt(e)} — roles on ${providerScope(e)} run again`;
412
478
  return `provider limit lifted${providerAt(e)} — roles on ${providerScope(e)} run again`;
413
479
  }
414
- case "claude_window_warning":
480
+ case "subscription_window_warning": {
415
481
  // Routine-with-explanation, like rate_limit_hold — no warning prefix: the account is
416
482
  // telling us the window is filling, not failing. It names the window and when it resets.
417
- return `claude subscription window ${textOr(e.windowType)} near its limit${windowResetPhrase(e)} — subscription claude model jobs keep running`;
418
- case "claude_window_reserved": {
483
+ const used = finiteNumber(e.utilization, null);
484
+ const fill = used === null ? "" : ` (at ${claudeUtilizationPct(used)}% used)`;
485
+ return `${backendLabel(e)} subscription window ${textOr(e.windowType)} near its limit${fill}${windowResetPhrase(e)} — subscription ${backendLabel(e)} model jobs keep running`;
486
+ }
487
+ case "subscription_window_reserved": {
419
488
  const used = finiteNumber(e.utilization, null);
420
489
  const line = finiteNumber(e.reserve, null);
421
- const pct = (f) => (f === null ? "?" : `${Math.round(f * 100)}%`);
422
- return `claude subscription window ${textOr(e.windowType)} at ${pct(used)}, past its ${pct(line)} reserve${windowResetPhrase(e)} — the team leaves it to you (director keeps running)`;
490
+ const pct = (f) => (f === null ? "?" : `${claudeUtilizationPct(f)}%`);
491
+ return `${backendLabel(e)} subscription window ${textOr(e.windowType)} at ${pct(used)}, past its ${pct(line)} reserve${windowResetPhrase(e)} — the team leaves it to you (director keeps running)`;
423
492
  }
424
- case "claude_window_limited":
493
+ case "subscription_window_limited":
425
494
  // Routine-with-explanation, like rate_limit_hold — no warning prefix: waiting is the
426
495
  // harness handling the account's own throttle. Names the window, the reset, and which
427
- // seams stop, so the operator knows why claude held and what keeps running.
496
+ // seams stop, so the operator knows why the backend held and what keeps running.
428
497
  // With a fallback the work moves rather than waits: `tiers` names where each moved tier's
429
498
  // work went (the budget poll's window move), the director and any tier with no fallback
430
499
  // still wait.
431
500
  {
432
501
  const moved = fallbackTierEntries(isJsonObject(e.tiers) ? e.tiers : undefined);
433
502
  if (moved.length > 0)
434
- return `claude subscription window ${textOr(e.windowType)} rejected — claude model jobs move to ${moved.join(", ")}${windowResetPhrase(e)} (director keeps waiting)`;
503
+ return `${backendLabel(e)} subscription window ${textOr(e.windowType)} rejected — ${backendLabel(e)} model jobs move to ${moved.join(", ")}${windowResetPhrase(e)} (director keeps waiting)`;
435
504
  }
436
- return `waiting: claude subscription window ${textOr(e.windowType)} rejected — claude model jobs wait${windowResetPhrase(e)} (director keeps running)`;
437
- case "claude_window_reset":
438
- return `claude subscription window reset — claude model jobs run again`;
505
+ return `waiting: ${backendLabel(e)} subscription window ${textOr(e.windowType)} rejected — ${backendLabel(e)} model jobs wait${windowResetPhrase(e)} (director keeps running)`;
506
+ case "subscription_window_reset":
507
+ return `${backendLabel(e)} subscription window reset — ${backendLabel(e)} model jobs run again`;
439
508
  case "max_concurrent_changed": {
440
509
  // Routine state change, like counters_reset — no warning prefix.
441
510
  return `maxConcurrent changed: ${textOr(e.from)} → ${textOr(e.to)}`;
@@ -469,7 +538,7 @@ export function eventMessage(e) {
469
538
  // failure. Names the pair the role now runs on and why the primary was abandoned.
470
539
  return `model fallback — ${providerModelPair(e, "pi's default", "pi's default")} engaged (primary failing: ${textOr(e.reason, "backend failure")})`;
471
540
  case "model_fallback_ended":
472
- // The return counterpart: names the pair ticks resume on and how long the episode ran.
541
+ // The return counterpart: names the pair runs resume on and how long the episode ran.
473
542
  return `model fallback ended — back on ${providerModelPair(e, "pi's default", "pi's default")} after ${minutes(e.durationMs)}m`;
474
543
  case "opencode_free_model_billed":
475
544
  // The promotion ended (plans/opencode-backend.md §7, part 6c/7): a run billed what the
@@ -480,7 +549,7 @@ export function eventMessage(e) {
480
549
  case "image_blind": {
481
550
  // Routine-with-explanation like the model-fallback events — no warning prefix: the
482
551
  // run was stopped (or a gate run noted) because the model cannot see the image. Names
483
- // the file, the model, and whether the tick failed on it or a gate run only logged it.
552
+ // the file, the model, and whether the run failed on it or a gate run only logged it.
484
553
  const action = e.action === "logged"
485
554
  ? " (gate run logged)"
486
555
  : e.action === "fail"
@@ -489,9 +558,9 @@ export function eventMessage(e) {
489
558
  return `blind image read: ${textOr(e.path)} on ${textOr(e.model, "the model")}${action}`;
490
559
  }
491
560
  case "image_reroute":
492
- // The tick's work moves to a model that can read the image (plans/image-input.md part
561
+ // The run's work moves to a model that can read the image (plans/image-input.md part
493
562
  // 4/5). Routine-with-explanation — no warning prefix: names the hop the way the
494
- // handoff reads, and whether the traits-of-attachment preflight avoided a failed tick.
563
+ // handoff reads, and whether the traits-of-attachment preflight avoided a failed run.
495
564
  return e.preflight === true
496
565
  ? `image attachment routed to ${textOr(e.to, "the target")} (${textOr(e.tier, "a peer tier")}) to read its image — no failed run`
497
566
  : `image rerouted to ${textOr(e.to, "the target")} (${textOr(e.tier, "a peer tier")}) — next run goes there after a blind read on ${textOr(e.from, "the prior model")}`;
@@ -499,7 +568,7 @@ export function eventMessage(e) {
499
568
  // Self-hosting teams only (src/redeploy/redeploy.ts): the code main describes is not the
500
569
  // code running. Not a warning prefix — a stale build is a state, and auto-restart resolves
501
570
  // it.
502
- return `update available (${plural(finiteNumber(e.aheadCommits, 0), "commit")} behind) — restarts when idle`;
571
+ return `update available (${plural(finiteNumber(e.aheadCommits, 0), "commit")} behind) — restarts after cooldown; work continues`;
503
572
  case "restart_pending":
504
573
  return `restart pending — main ${shortSha(e.head)} is green; finishing current runs (no new runs start)`;
505
574
  case "restart": {
@@ -523,17 +592,22 @@ export function eventMessage(e) {
523
592
  const how = e.signal ? `was killed by ${textOr(e.signal)}` : `exited ${textOr(e.code)}`;
524
593
  return `tumwater is down — restart ${textOr(e.generation)} ${how}${e.reason ? `: ${textOr(e.reason)}` : ""}; start it again with \`tumwater start\``;
525
594
  }
526
- case "resume":
595
+ case "resume": {
527
596
  // Two causes share the resume machinery; the line names the real one so an operator
528
- // reading the feed can tell a restart from a loop fighting the context ceiling, and a
529
- // cross-agent handoff from either — the handoff keeps the worktree but starts fresh.
530
- return e.cause === "cut-off"
597
+ // reading the feed can tell a restart from a role fighting the context ceiling, and a
598
+ // cross-agent handoff from either — the handoff keeps the worktree but starts fresh. A
599
+ // run that got a salvaged patch back (src/git/slot-salvage.ts) names the patch.
600
+ const restored = e.restored === undefined ? "" : ` — uncommitted edits restored from ${textOr(e.restored)}`;
601
+ return ((e.cause === "cut-off"
531
602
  ? "resuming the run cut off at the context ceiling (compacted pi session, same workspace)"
532
603
  : e.cause === "handoff"
533
604
  ? "handing the interrupted run off to a fresh session on another agent (same workspace, uncommitted edits kept)"
534
605
  : e.cause === "image-reroute"
535
606
  ? "handing the blind-read run off to a fresh session on an image-capable model (same workspace, uncommitted edits kept)"
536
- : "resuming the run a shutdown interrupted (same agent session and workspace)";
607
+ : e.cause === "salvaged"
608
+ ? "handing the work an earlier run left unsaved to a fresh session"
609
+ : "resuming the run a shutdown interrupted (same agent session and workspace)") + restored);
610
+ }
537
611
  case "slot_wait": {
538
612
  // Routine-with-explanation, like rate_limit_hold — no warning prefix: waiting is the pool
539
613
  // working as designed. Names the wait, the pool it waited on, and how many slots are
@@ -545,9 +619,30 @@ export function eventMessage(e) {
545
619
  // Routine-with-explanation, like slot_wait: waiting for a provider/team slot is the cap
546
620
  // working as designed, so no warning prefix. Names the wait and the key (or the run cap
547
621
  // when the seam has none), so the operator can raise maxConcurrentPerProvider with the
548
- // numbers in hand.
549
- return `waited ${shortSpanPhrase(finiteNumber(e.waitedMs, 0))} for a ${typeof e.key === "string" && e.key.length > 0 ? `${e.key} slot` : "run slot"} (${textOr(e.role)})`;
622
+ // numbers in hand. An event carrying `cap` names the cap that held the run and its limit
623
+ // (a team-cap wait names no key); one without `cap` (logged before it existed) renders
624
+ // the key-or-run-slot line alone.
625
+ const span = shortSpanPhrase(finiteNumber(e.waitedMs, 0));
626
+ const limit = finiteNumber(e.limit, Number.NaN);
627
+ const hasKey = typeof e.key === "string" && e.key.length > 0;
628
+ if (e.cap === "provider" && hasKey) {
629
+ return `waited ${span} for a ${e.key} slot (${textOr(e.role)}${Number.isFinite(limit) ? `; maxConcurrentPerProvider.${e.key} is ${limit}` : ""})`;
630
+ }
631
+ if (e.cap === "team") {
632
+ return `waited ${span} for a run slot (${textOr(e.role)}${Number.isFinite(limit) ? `; maxConcurrent is ${limit}` : ""})`;
633
+ }
634
+ return `waited ${span} for a ${hasKey ? `${e.key} slot` : "run slot"} (${textOr(e.role)})`;
550
635
  }
636
+ case "check_wait":
637
+ // Routine-with-explanation, like slot_wait: waiting for a check permit is the cap working
638
+ // as designed, so no warning prefix. Newer events also name the holders and the cap at park
639
+ // time; older or malformed ones render the bare line.
640
+ {
641
+ const heldBy = stringList(e.heldBy);
642
+ const limit = finiteNumber(e.limit, null);
643
+ const behind = heldBy.length > 0 ? `; held by ${heldBy.join(", ")}${limit === null ? "" : ` of maxConcurrentChecks ${limit}`}` : "";
644
+ return `waited ${shortSpanPhrase(finiteNumber(e.waitedMs, 0))} for a check slot (${textOr(e.role)} ${textOr(e.scope)} check${behind})`;
645
+ }
551
646
  case "claim":
552
647
  // Parallel work instances (plans/parallel-work-instances.md "Observability", part 6/7):
553
648
  // the assignment/release is the harness working as designed, so no warning prefix.
@@ -1,4 +1,3 @@
1
- // vocabulary: plain
2
1
  /** Words for the two code families the dashboards show: event types (the activity chips) and run
3
2
  * result codes (the result cells). Pure, no I/O, so core modules and the UI may both import it. */
4
3
  /** The words for each event type. The `Record` type makes the compiler demand a label for every
@@ -9,10 +8,13 @@ export const EVENT_TYPE_LABELS = {
9
8
  run_start: "run started",
10
9
  run_end: "run finished",
11
10
  merge_queued: "waiting to merge",
11
+ merge_cancelled: "merge cancelled",
12
+ merge_topped: "merge moved to front",
12
13
  merge_done: "merge finished",
13
14
  merge_failed: "merge failed",
14
15
  merged: "merged",
15
16
  question_posted: "question posted",
17
+ issue_filed: "issue filed",
16
18
  entry_refused: "plan refused",
17
19
  wake: "woke up",
18
20
  run_deferred: "run deferred",
@@ -49,6 +51,10 @@ export const EVENT_TYPE_LABELS = {
49
51
  role_cap_warning: "role budget warning",
50
52
  role_cap_paused: "role budget paused",
51
53
  role_cap_resumed: "role budget resumed",
54
+ main_check: "main checked",
55
+ main_culprit: "main red culprit",
56
+ main_revert: "main red reverted",
57
+ operator_revert: "operator revert", // vocabulary-ok: "operator" is the operator-role name this label's plan chose
52
58
  main_red_hold: "main's check is failing",
53
59
  maintenance_quota_hold: "waiting: maintenance allowance used",
54
60
  maintenance_quota_resumed: "maintenance allowance reopened",
@@ -62,12 +68,13 @@ export const EVENT_TYPE_LABELS = {
62
68
  permit_reserve: "permits reserved",
63
69
  slot_wait: "waited to start",
64
70
  provider_wait: "waited for a provider slot",
71
+ check_wait: "waited for a check slot",
65
72
  rate_limit_hold: "waiting: provider limit",
66
73
  rate_limit_resumed: "provider limit lifted",
67
- claude_window_reserved: "claude usage window reserved",
68
- claude_window_warning: "claude usage window warning",
69
- claude_window_limited: "waiting: claude usage window",
70
- claude_window_reset: "claude usage window reset",
74
+ subscription_window_reserved: "usage window reserved",
75
+ subscription_window_warning: "usage window warning",
76
+ subscription_window_limited: "waiting: usage window",
77
+ subscription_window_reset: "usage window reset",
71
78
  max_concurrent_changed: "concurrency changed",
72
79
  provider_concurrency_changed: "provider concurrency changed",
73
80
  retention_changed: "retention changed",