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
@@ -8,21 +8,23 @@
8
8
  import { runRowCells } from "../../history/history-data.js";
9
9
  /** The pane body for a set of run rows: `time role #run result duration`, then `model`
10
10
  * (between the result and the duration), and `usage` and `detail` when they carry anything,
11
- * joined newest-first by newlines. The cells come from history-data.ts's runRowCells, the one
12
- * home the CLI's table builds on too, so a null `durationMs` (the run's start sits outside
13
- * the scanned window) shows the em dash here the same way; an empty `model` (the start named
14
- * none or sat outside the window), an empty `usage` (neither tokens nor cost), and an empty
15
- * `detail` are omitted rather than leaving a gap. No rows renders `""`, which the pane's
16
- * `emptyNote` replaces. `row.result` is the string history-data.ts resolved (including a
17
- * joined merge verdict), never reclassified here. */
11
+ * joined newest-first by newlines. Both the result and the duration cells come from
12
+ * history-data.ts's runRowCells, the one home the CLI's table builds on too, so a run cut
13
+ * off at its model's context ceiling reads `no change (cut off)` here the way it does in
14
+ * `history`, and a null `durationMs` (the run's start sits outside the scanned window)
15
+ * shows the em dash the same way; an empty `model` (the start named none or sat outside
16
+ * the window), an empty `usage` (neither tokens nor cost), and an empty `detail` are
17
+ * omitted rather than leaving a gap. No rows renders `""`, which the pane's `emptyNote`
18
+ * replaces. The result cell goes through eventTypeLabel exactly as the CLI table's does,
19
+ * so a merged run's resolved verdict and a `no_change` read the same label in both. */
18
20
  export function historyPaneText(rows) {
19
21
  return rows
20
22
  .map((row) => {
21
- const { duration } = runRowCells(row);
22
- const parts = [row.time, `${row.role} #${row.run}`, row.result];
23
+ const cells = runRowCells(row);
24
+ const parts = [row.time, `${row.role} #${row.run}`, cells.result];
23
25
  if (row.model)
24
26
  parts.push(row.model);
25
- parts.push(duration);
27
+ parts.push(cells.duration);
26
28
  if (row.usage)
27
29
  parts.push(row.usage);
28
30
  if (row.detail)
@@ -4,7 +4,9 @@ import { runDoctor } from "../../doctor/doctor.js";
4
4
  import { killOrphans } from "../../doctor/doctor-orphans.js";
5
5
  import { dropBrokenPins, retireLeftovers } from "../../doctor/doctor-contention-checks.js";
6
6
  import { commitUncommittedBacklog } from "../../doctor/doctor-backlog.js";
7
+ import { archiveFinishedOnMain } from "../../backlog/backlog-archive.js";
7
8
  import { loadConfigSafe } from "../../config/config.js";
9
+ import { MERGE_LOCK_WAIT_MS } from "../../paths.js";
8
10
  import { commitOperatorBacklogEdit } from "../../backlog/backlog-commit.js";
9
11
  import { stampedSubject } from "../../git/commit-message.js";
10
12
  import { previewRevert, revertMergedCommit } from "../../operator/revert.js";
@@ -35,11 +37,12 @@ export function createTuiKeys(deps) {
35
37
  const { root } = deps;
36
38
  const collectPending = deps.collectPending ?? collectTeamChanges;
37
39
  const collectOverlaps = deps.collectOverlaps ?? collectChangeOverlaps;
38
- const runDoctorSeam = deps.runDoctor ?? ((r, p) => runDoctor(r, undefined, undefined, p));
40
+ const runDoctorSeam = deps.runDoctor ?? ((r) => runDoctor(r));
39
41
  const killOrphansSeam = deps.killOrphans ?? ((r) => killOrphans(r));
40
42
  const dropBrokenPinsSeam = deps.dropBrokenPins ?? ((r) => dropBrokenPins(r));
41
43
  const retireLeftoversSeam = deps.retireLeftovers ?? ((r) => retireLeftovers(r, loadConfigSafe(r).config));
42
44
  const commitBacklogSeam = deps.commitBacklog ?? ((r) => commitUncommittedBacklog(r, loadConfigSafe(r).config));
45
+ const archiveBacklogSeam = deps.archiveBacklog ?? ((r) => archiveFinishedOnMain(r, { lockTimeoutMs: MERGE_LOCK_WAIT_MS }));
43
46
  const previewRevertSeam = deps.previewRevert ?? previewRevert;
44
47
  const revertCommit = deps.revertCommit ?? revertMergedCommit;
45
48
  const collectChange = deps.collectChange ?? collectRoleChange;
@@ -89,6 +92,7 @@ export function createTuiKeys(deps) {
89
92
  dropBrokenPins: dropBrokenPinsSeam,
90
93
  retireLeftovers: retireLeftoversSeam,
91
94
  commitBacklog: commitBacklogSeam,
95
+ archiveBacklog: archiveBacklogSeam,
92
96
  previewRevert: previewRevertSeam,
93
97
  revertCommit,
94
98
  collectChange,
@@ -108,6 +112,7 @@ export function createTuiKeys(deps) {
108
112
  refreshQueued: panes.refreshQueued,
109
113
  refreshNotes: panes.refreshNotes,
110
114
  refreshBrief: panes.refreshBrief,
115
+ refreshReport: panes.refreshReport,
111
116
  clearBacklogSelection: panes.clearBacklogSelection,
112
117
  applyHistorySearch: panes.applyHistorySearch,
113
118
  applyActivitySearch: panes.applyActivitySearch,
@@ -39,58 +39,70 @@ export function createActivityPane(session) {
39
39
  activityGrep = pattern;
40
40
  activityScroll = null;
41
41
  };
42
+ /** Ctrl+G arm: the Activity feed scopes over the same raw roster ids, so `feature-2` is its
43
+ * own slot; the filter reads exact role ids through eventRole. The scroll resets because its
44
+ * offset indexed the old (unfiltered or differently filtered) list. */
45
+ const activityScopeCycle = (ctx) => {
46
+ if (!promptLineFree(ctx.es))
47
+ return false;
48
+ const slots = [null, ...session.roleIds];
49
+ const slot = slots.indexOf(activityRole);
50
+ activityRole = slots[(slot + 1) % slots.length] ?? null;
51
+ activityScroll = null;
52
+ session.requestRender();
53
+ return true;
54
+ };
55
+ /** Ctrl+O arm: the Activity type/notable cycle: all → notable → first present type → … → last
56
+ * present type → all. `present` is built from the pre-type read (type `null`, no notable
57
+ * cull), so every type stays reachable once one is selected — the same reason the dashboard
58
+ * builds its chip set before its type filter. The scroll resets because its offset indexed
59
+ * the old filtered list. */
60
+ const activityTypeCycle = (ctx) => {
61
+ if (!promptLineFree(ctx.es))
62
+ return false;
63
+ const present = [...new Set(activityEvents(session.root, activityRole, activityGrep, null).map((e) => e.type))].sort();
64
+ const slots = [
65
+ { notable: false, type: null },
66
+ { notable: true, type: null },
67
+ ...present.map((t) => ({ notable: false, type: t })),
68
+ ];
69
+ const idx = slots.findIndex((s) => s.notable === activityNotable && s.type === activityType);
70
+ const next = slots[(idx + 1) % slots.length] ?? slots[0];
71
+ activityNotable = next.notable;
72
+ activityType = next.type;
73
+ activityScroll = null;
74
+ session.requestRender();
75
+ return true;
76
+ };
77
+ /** PgUp/PgDn arm: the Activity pane keeps the live tail by default; PgUp pauses follow and
78
+ * pages back through the loaded event tail, PgDn returns toward it and re-enables follow once
79
+ * the window reaches the tail. Both directions clamp to [0, total - rows], and a log with
80
+ * rows or fewer events stays on the tail. `rows` is the feed's window height — one body
81
+ * line short of eventBudget while the tally line is drawn (its paneBody twin, the
82
+ * `rows = Math.max(1, eventBudget - tallyLine.length)` in tui-pane.ts) — so each page
83
+ * advances exactly as far as the feed displays and no event is skipped or unreachable
84
+ * while paging. The read is the pane's own fixed tail term (and active role/text
85
+ * filter), so it hits readEvents' stat cache rather than rescanning the log. */
86
+ const activityTailPage = (page) => {
87
+ if (session.view !== 0)
88
+ return false;
89
+ const total = activityEvents(session.root, activityRole, activityGrep, activityType, activityNotable).length;
90
+ const rows = total === 0 ? session.eventBudget : Math.max(1, session.eventBudget - 1);
91
+ const maxStart = Math.max(0, total - rows);
92
+ const current = activityScroll ?? maxStart;
93
+ const next = stepEntryScroll(current, total, rows, page);
94
+ activityScroll = next >= maxStart ? null : next;
95
+ session.requestRender();
96
+ return true;
97
+ };
42
98
  const handleKey = (key, ctx) => {
43
- if (key.ctrl && key.name === "g" && promptLineFree(ctx.es)) {
44
- // The Activity feed scopes over the same raw roster ids, so `feature-2` is its own
45
- // slot; the filter reads exact role ids through eventRole. The scroll resets because
46
- // its offset indexed the old (unfiltered or differently filtered) list.
47
- const slots = [null, ...session.roleIds];
48
- const slot = slots.indexOf(activityRole);
49
- activityRole = slots[(slot + 1) % slots.length] ?? null;
50
- activityScroll = null;
51
- session.requestRender();
52
- return true;
53
- }
54
- if (key.ctrl && key.name === "o" && promptLineFree(ctx.es)) {
55
- // The Activity type/notable cycle: all → notable → first present type → … → last present
56
- // type → all. `present` is built from the pre-type read (type `null`, no notable cull), so
57
- // every type stays reachable once one is selected — the same reason the dashboard builds
58
- // its chip set before its type filter. The scroll resets because its offset indexed the
59
- // old filtered list.
60
- const present = [...new Set(activityEvents(session.root, activityRole, activityGrep, null).map((e) => e.type))].sort();
61
- const slots = [
62
- { notable: false, type: null },
63
- { notable: true, type: null },
64
- ...present.map((t) => ({ notable: false, type: t })),
65
- ];
66
- const idx = slots.findIndex((s) => s.notable === activityNotable && s.type === activityType);
67
- const next = slots[(idx + 1) % slots.length] ?? slots[0];
68
- activityNotable = next.notable;
69
- activityType = next.type;
70
- activityScroll = null;
71
- session.requestRender();
72
- return true;
73
- }
99
+ if (key.ctrl && key.name === "g")
100
+ return activityScopeCycle(ctx);
101
+ if (key.ctrl && key.name === "o")
102
+ return activityTypeCycle(ctx);
74
103
  const page = pageDir(key.name);
75
- if (page !== null && session.view === 0) {
76
- // The Activity pane keeps the live tail by default; PgUp pauses follow and pages back
77
- // through the loaded event tail, PgDn returns toward it and re-enables follow once the
78
- // window reaches the tail. Both directions clamp to [0, total - rows], and a log with
79
- // rows or fewer events stays on the tail. `rows` is the feed's window height — one body
80
- // line short of eventBudget while the tally line is drawn (its paneBody twin, the
81
- // `rows = Math.max(1, eventBudget - tallyLine.length)` in tui-pane.ts) — so each page
82
- // advances exactly as far as the feed displays and no event is skipped or unreachable
83
- // while paging. The read is the pane's own fixed tail term (and active role/text
84
- // filter), so it hits readEvents' stat cache rather than rescanning the log.
85
- const total = activityEvents(session.root, activityRole, activityGrep, activityType, activityNotable).length;
86
- const rows = total === 0 ? session.eventBudget : Math.max(1, session.eventBudget - 1);
87
- const maxStart = Math.max(0, total - rows);
88
- const current = activityScroll ?? maxStart;
89
- const next = stepEntryScroll(current, total, rows, page);
90
- activityScroll = next >= maxStart ? null : next;
91
- session.requestRender();
92
- return true;
93
- }
104
+ if (page !== null)
105
+ return activityTailPage(page);
94
106
  return false;
95
107
  };
96
108
  return {
@@ -137,7 +137,7 @@ export function createBacklogPane(session) {
137
137
  session.flashMessage(outcome.status === "released"
138
138
  ? `released ${kind} #${position}: ${outcome.title} (lifted ${liftedNotePhrase(outcome.lifted)})${heldTail}`
139
139
  : outcome.status === "not_refused"
140
- ? `that ${kind} carries no Refused, Waiting for the next failure, or held-for-operator note` // vocabulary-ok: the held-for-operator note's release wording
140
+ ? `that ${kind} carries no Refused note`
141
141
  : `that ${kind} was already removed or moved — nothing released`);
142
142
  }
143
143
  catch (err) {
@@ -202,7 +202,7 @@ export function createBacklogPane(session) {
202
202
  if (key.ctrl && key.name === "r" &&
203
203
  promptLineFree(ctx.es) &&
204
204
  session.view === session.roleIds.length + 1) {
205
- // Ctrl+R twice releases the Refused, Waiting-for-the-next-failure, or held-for-operator
205
+ // Ctrl+R twice releases the Refused
206
206
  // note on the selected plan or bug through the non-exiting core the dashboard drawer
207
207
  // uses; the title check inside tryReleaseEntry refuses a stale selection. Releasing is the
208
208
  // terminal twin of `tumwater plan|bug --release <n>`: it lifts the note, commits the file
@@ -212,8 +212,8 @@ export function createBacklogPane(session) {
212
212
  const kind = selected.kind;
213
213
  const position = selected.position;
214
214
  const target = { kind, position, title: selected.picked.title };
215
- if (selected.picked.hold !== "refused" && selected.picked.hold !== "waiting-for-failure" && selected.picked.hold !== "held-for-operator") { // vocabulary-ok: the held-for-operator hold's key
216
- session.flashMessage(`that ${kind} carries no Refused, Waiting for the next failure, or held-for-operator note`); // vocabulary-ok: the held-for-operator note's release wording
215
+ if (selected.picked.hold !== "refused") {
216
+ session.flashMessage(`that ${kind} carries no Refused note`);
217
217
  }
218
218
  else if (session.armedRelease !== null && session.armedRelease.kind === kind && session.armedRelease.position === position) {
219
219
  // The armed title (not the re-read one) goes to tryReleaseEntry, so an entry that
@@ -6,6 +6,6 @@
6
6
  export function promptLineFree(es) {
7
7
  return (!es.budgetMode && !es.rolePromptFor && !es.searchMode &&
8
8
  !es.pauseEditMode && !es.queuedEditFor && !es.answerFor && !es.entryEditFor &&
9
- !es.notesEditFor && !es.briefEditMode && !es.retireMode && !es.roleEnabledMode && !es.quietMode && !es.intervalMode &&
9
+ !es.notesEditFor && !es.briefEditMode && !es.retireMode && !es.releaseMode && !es.roleEnabledMode && !es.quietMode && !es.intervalMode &&
10
10
  !es.roleModelMode && !es.instructionsEditFor && !es.claudeReserveMode);
11
11
  }
@@ -5,11 +5,13 @@ import { runManualReclaim } from "../../operator/operator-intent.js";
5
5
  import { testNotifyHook } from "../../cli/notify-command.js";
6
6
  import { resyncRootInstall, rootInstallResyncLines } from "../../build/dep-install.js";
7
7
  import { brokenPinDropLines, leftoverRetireLines } from "../../doctor/doctor-contention-checks.js";
8
+ import { describeArchivedFile } from "../../backlog/backlog-archive.js";
9
+ import { shortSha } from "../../text/format.js";
8
10
  import { promptLineFree } from "./tui-pane-ctx.js";
9
11
  /** The Health pane (view roleIds.length + 7) of the TUI (src/ui/tui/tui.tsx): the problems-only
10
- * Ctrl+O toggle, the Ctrl+P image-model re-probe, the Ctrl+A commit-backlog, and the runDoctor
11
- * collector it re-reads on every entry. Split out of the pane-navigation monolith; it owns the
12
- * problems flag and the last resolved report. */
12
+ * Ctrl+O toggle, the Ctrl+A commit-backlog, the Ctrl+B
13
+ * backlog archive, and the runDoctor collector it re-reads on every entry. Split out of the
14
+ * pane-navigation monolith; it owns the problems flag and the last resolved report. */
13
15
  export function createHealthPane(session) {
14
16
  // Within the Health pane (Ctrl+O), whether the cached report paints only its warn and fail
15
17
  // checks, the same problems-only trim `tumwater doctor --problems` applies. Session-only
@@ -24,16 +26,14 @@ export function createHealthPane(session) {
24
26
  // current. Extracted so both the enter half of the Ctrl+T cycle and the Ctrl+R in-place
25
27
  // refresh run the same body; a run already in flight is superseded by the new read.
26
28
  let runGen = 0;
27
- // Whether a Ctrl+P re-probe is in flight, so a held key never spends a second round of probes.
28
- let reprobing = false;
29
- const run = (probeImages = "never") => {
29
+ const run = () => {
30
30
  // Generation guard: each run bumps the counter, and a resolution paints only if it is
31
31
  // the latest request's — Ctrl+R held twice, or a re-run raced with a re-entry, must not
32
32
  // let an out-of-order older resolution overwrite the newer report.
33
33
  const gen = ++runGen;
34
34
  session.paneCache = null;
35
35
  lastDoctorReport = null;
36
- return session.runDoctor(session.root, probeImages).then((report) => {
36
+ return session.runDoctor(session.root).then((report) => {
37
37
  if (gen !== runGen || session.view !== session.roleIds.length + 7)
38
38
  return;
39
39
  lastDoctorReport = report;
@@ -57,25 +57,6 @@ export function createHealthPane(session) {
57
57
  session.requestRender();
58
58
  return true;
59
59
  }
60
- if (key.ctrl && key.name === "p" && promptLineFree(ctx.es) && session.view === session.roleIds.length + 7) {
61
- // Ctrl+P on the Health pane (mnemonic: **P**robe) is the terminal twin of `tumwater doctor
62
- // --reprobe` and the dashboard's Re-probe models: it re-runs the checks with every claimed
63
- // pi image-input model re-probed, spending one model call each and rewriting
64
- // .tumwater/state/image-probes.json. Every other view keeps its own Ctrl+P.
65
- if (reprobing) {
66
- session.flashMessage("a re-probe is already running");
67
- session.requestRender();
68
- return true;
69
- }
70
- reprobing = true;
71
- session.flashMessage("re-probing image-input models (one model call each) …");
72
- void run("all").finally(() => {
73
- reprobing = false;
74
- });
75
- session.paneScroll = 0;
76
- session.requestRender();
77
- return true;
78
- }
79
60
  if (key.ctrl && key.name === "e" && promptLineFree(ctx.es) && session.view === session.roleIds.length + 7) {
80
61
  // Ctrl+E on the Health pane runs one manual reclaim pass now — the terminal twin of the
81
62
  // dashboard disk row's reclaim button and the core `tumwater reclaim`, both reached
@@ -253,6 +234,44 @@ export function createHealthPane(session) {
253
234
  });
254
235
  return true;
255
236
  }
237
+ if (key.ctrl && key.name === "b" && promptLineFree(ctx.es) && (ctx.es.input ?? "") === "" && session.view === session.roleIds.length + 7) {
238
+ // Ctrl+B on the Health pane, pressed twice, archives the finished entries still under
239
+ // PLANS.md's ## Done and BUGS.md's ## Fixed into PLANS-DONE.md and BUGS-FIXED.md and
240
+ // commits them on the base branch — the terminal twin of `tumwater archive` and the
241
+ // dashboard Health card's Archive backlog, all reached through the same archiveFinished-.
242
+ // OnMain. The first press only arms (beginKey disarms on any other key) and flashes what
243
+ // a second press does; the second flashes the same lines the CLI prints, joined with
244
+ // "; " (the nothing line verbatim when there is nothing to archive), then re-runs the
245
+ // checks so the `backlog archive` row reflects the move. A failure flashes instead of
246
+ // killing the TUI. Ctrl+B stays the prompt-line editor's line-kill (budget editor) while
247
+ // text is typed or an editor holds the line, and is unbound on every other view.
248
+ if (!session.armedArchiveBacklog) {
249
+ session.archiveBacklogArmed = true;
250
+ session.flashMessage("press Ctrl+B again to archive the finished entries under PLANS.md ## Done and BUGS.md ## Fixed — any other key keeps them");
251
+ session.requestRender();
252
+ return true;
253
+ }
254
+ session.flashMessage("archiving finished backlog entries …");
255
+ session.requestRender();
256
+ void session.archiveBacklog(session.root).then((outcome) => {
257
+ if (outcome.status === "nothing") {
258
+ session.flashMessage("nothing to archive: PLANS.md ## Done and BUGS.md ## Fixed hold no finished entries");
259
+ }
260
+ else if (outcome.status === "skipped") {
261
+ session.flashMessage(`nothing archived: ${outcome.reason}`);
262
+ }
263
+ else {
264
+ session.flashMessage([...outcome.moved.map(describeArchivedFile), `committed ${shortSha(outcome.commit)}`].join("; "));
265
+ }
266
+ void run();
267
+ session.paneScroll = 0;
268
+ session.requestRender();
269
+ }, (err) => {
270
+ session.flashError(err);
271
+ session.requestRender();
272
+ });
273
+ return true;
274
+ }
256
275
  if (key.ctrl && key.name === "o" && promptLineFree(ctx.es) && session.view === session.roleIds.length + 7) {
257
276
  // Ctrl+O on the Health pane toggles the problems-only view (mnemonic: **O**nly
258
277
  // problems) — the same warn/fail trim `tumwater doctor --problems` applies — so an