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
@@ -1,5 +1,5 @@
1
1
  import { fail, say, sayJson, sayJsonLine } from "../cli/cli-output.js";
2
- import { durationLabel, failRivalShapes, failUntilShorterThanSince, flagValue, parseCountFlag, parseGrepFlag, parseRoleListScope, parseSinceFlag, rejectFlagLookingValue } from "../cli/cli-args.js";
2
+ import { betweenWindow, durationLabel, failRivalShapes, failUntilShorterThanSince, flagValue, parseCountFlag, parseGrepFlag, parseRoleListScope, parseSinceFlag, rejectFlagLookingValue } from "../cli/cli-args.js";
3
3
  import { HISTORY_DEFAULT_RUNS, HISTORY_MAX_RUNS, HISTORY_RESULT_FLAG_NAMES, HISTORY_SUMMARY_GROUPS, historyClass, parseHistoryResultClass, readRunRows, readRunRowsSince, runRows, summarizeRunRows, summarizeRunRowsByModel, runRowsMatchingGrep, runRowsOnModels } from "./history-data.js";
4
4
  import { renderModelSummary, renderRow, renderSummary, widenWidths, widestWidths } from "./history-render.js";
5
5
  import { LOGS_SINCE_MAX_MS, SPARSE_WINDOW_NOTE } from "../events/event-window.js";
@@ -69,16 +69,12 @@ export function parseHistoryResultList(raw) {
69
69
  }
70
70
  return parsed;
71
71
  }
72
- /** `tumwater history [--role <id>[,<id>...]] [-n N] [-f] [--since <duration>]
73
- * [--until <duration>] [--grep <text>] [--result <class>] [--model <selector>[,...]]`: print the
74
- * last N completed runs, newest first — or,
75
- * with `--since`, the runs of a bounded past window over the same record, the same shape
76
- * `logs --since` and `report --since` speak. `--grep` keeps rows whose rendered line matches,
77
- * `--result` narrows to the listed outcome classes (changes/none/problems, the dashboard History
78
- * filter's buckets) and `--model` keeps runs that started on the listed models; all compose
79
- * with --role, -n, and --since. Read-only: stdout only, no state file created — a missing or empty
80
- * event log prints `no runs yet` and exits 0. */
81
- export async function cmdHistory(root, args, seams = {}) {
72
+ /** Scan `history`'s flags: the `--grep` pattern, the raw `-n` value, the `-f`/`--since`
73
+ * rival shape checks, and the `--since`/`--until` window. These failures all fire here in
74
+ * the same order as before, so a bad value names the same command and flag; only the `-n`
75
+ * count is left raw (see collectHistoryRows), so its own parse and failure live there, in
76
+ * the deliberate spot after the `--since` rival check and before `--result`. */
77
+ function parseHistoryQuery(root, args) {
82
78
  // `--grep <text>` filters the row set case-insensitively against the WYSIWYG row line (plus a
83
79
  // `run_end` prefix, the same haystack rule logs --grep applies to its rendered lines), so
84
80
  // both observing views answer substring questions the same way. The flag scan itself — the
@@ -94,8 +90,6 @@ export async function cmdHistory(root, args, seams = {}) {
94
90
  // value) through parseSinceFlag, so the error wordings cannot drift. `--role` composes with
95
91
  // --since here: history's --role is a row filter, not the rival transcript view logs guards
96
92
  // against.
97
- let rows;
98
- let covered = true;
99
93
  const follow = hasFollowFlag(rest);
100
94
  if (follow) {
101
95
  if (rest.includes("--since"))
@@ -107,47 +101,48 @@ export async function cmdHistory(root, args, seams = {}) {
107
101
  const sinceMs = parseSinceFlag(rest, "history --since", LOGS_SINCE_MAX_MS);
108
102
  const untilMs = parseSinceFlag(rest, "history --until", LOGS_SINCE_MAX_MS, "--until");
109
103
  failUntilShorterThanSince("history", untilMs, sinceMs);
110
- if (followTail !== null) {
111
- const limit = nRaw !== null ? parseCountFlag("-n", nRaw, HISTORY_MAX_RUNS) : HISTORY_DEFAULT_RUNS;
104
+ return { rest, grepPattern, nRaw, followTail, sinceMs, untilMs };
105
+ }
106
+ /** Collect the row set the query names: the `-f` seed, the `--since` window, or the plain `-n`
107
+ * scan. The `-n` and `--since` rival checks live here so a bad count still fails after the
108
+ * `--since` check and before `--result` filtering. Returns the rows plus whether the scan
109
+ * covered the whole log (false when a bounded window hit the retained buffer's edge). */
110
+ function collectHistoryRows(root, q) {
111
+ let rows;
112
+ let covered = true;
113
+ if (q.followTail !== null) {
114
+ const limit = q.nRaw !== null ? parseCountFlag("-n", q.nRaw, HISTORY_MAX_RUNS) : HISTORY_DEFAULT_RUNS;
112
115
  // The seed goes through readRunRows, which widens its window until it finds `limit` rows;
113
116
  // the tail buffer only feeds the live follow.
114
- rows = readRunRows(root, limit, parseRoleListScope(root, rest));
117
+ rows = readRunRows(root, limit, parseRoleListScope(root, q.rest));
115
118
  }
116
- else if (sinceMs !== null) {
117
- if (nRaw !== null)
119
+ else if (q.sinceMs !== null) {
120
+ if (q.nRaw !== null)
118
121
  failRivalShapes("history --since", "-n", "a count and a window are rival shapes");
119
- const role = parseRoleListScope(root, rest);
120
- const windowed = readRunRowsSince(root, sinceMs, role, untilMs);
122
+ const role = parseRoleListScope(root, q.rest);
123
+ const windowed = readRunRowsSince(root, q.sinceMs, role, q.untilMs);
121
124
  rows = windowed.rows;
122
125
  covered = windowed.covered;
123
126
  }
124
127
  else {
125
128
  // parseCountFlag's max caps the scan window: a typo'd "999999" must not build a
126
129
  // HISTORY_MAX_RUNS-sized slice scan from a huge limit.
127
- const limit = nRaw !== null ? parseCountFlag("-n", nRaw, HISTORY_MAX_RUNS) : HISTORY_DEFAULT_RUNS;
130
+ const limit = q.nRaw !== null ? parseCountFlag("-n", q.nRaw, HISTORY_MAX_RUNS) : HISTORY_DEFAULT_RUNS;
128
131
  // The config is read (through loadConfigCached, never throwing) only when --role is present:
129
132
  // a read-only view must not refuse a transiently broken tumwater.json.
130
- const role = parseRoleListScope(root, rest);
133
+ const role = parseRoleListScope(root, q.rest);
131
134
  rows = readRunRows(root, limit, role);
132
135
  }
133
- // The filter runs on the collected rows, before rendering and before --json serialization:
134
- // the same rows the table would print, filtered the same way in both forms (the logs --grep
135
- // rule: -n bounds the scanned window, not the printed rows).
136
- if (grepPattern !== null)
137
- rows = runRowsMatchingGrep(rows, grepPattern);
138
- // The set --grep admitted, saved so the empty-state wording blames the filter that actually
139
- // emptied it: if the result filter below removes rows grep matched, naming the pattern would
140
- // be false (`no runs matching "widget"` when feature #7 does match). The GUI draws the same
141
- // line between a search and its outcome filter.
142
- const afterGrep = rows.length;
143
- // `--result <class>[,<class>...]` narrows to the listed outcome buckets, reading the same
144
- // canonical historyClass rule the dashboard's History filter applies (shared from
145
- // history-data.ts, not copied). It composes with --role, -n, --since, and --grep, so no
146
- // rival-shape rule: a missing or empty value fails with its own wording, and an unknown
147
- // class names the accepted ones (the gotSuffix shape the other flag validators use, plus a
148
- // did-you-mean hint for a near-miss of a real result — the same typoSuffix treatment
149
- // `logs --type` gives its codes). Applied after --grep, over the same rows both the table
150
- // and --json serialize.
136
+ return { rows, covered };
137
+ }
138
+ /** `--result <class>[,<class>...]` narrows the rows to the listed outcome buckets, reading the
139
+ * same canonical historyClass rule the dashboard's History filter applies (shared from
140
+ * history-data.ts, not copied). It composes with --role, -n, --since, and --grep, so no
141
+ * rival-shape rule: a missing or empty value fails with its own wording, and an unknown class
142
+ * names the accepted ones (the gotSuffix shape the other flag validators use, plus a did-you-mean
143
+ * hint for a near-miss of a real result — the same typoSuffix treatment `logs --type` gives its
144
+ * codes). Applied after --grep, over the same rows both the table and --json serialize. */
145
+ function applyHistoryResultFilter(rows, rest) {
151
146
  const resultRaw = rejectFlagLookingValue("history --result", flagValue(rest, "--result"));
152
147
  let resultClasses = null;
153
148
  if (resultRaw !== null) {
@@ -155,11 +150,13 @@ export async function cmdHistory(root, args, seams = {}) {
155
150
  resultClasses = wanted;
156
151
  rows = rows.filter((r) => wanted.includes(historyClass(r.result)));
157
152
  }
158
- // `--model <selector>[,...]` keeps the rows whose run_start named exactly one of the listed
159
- // selectors (case-sensitive, the strings `--summary --by model` prints); a null-model row never
160
- // matches. An unknown selector is not an error: models have no closed vocabulary and an old log
161
- // holds models the config has since dropped, so it simply keeps no rows.
162
- const afterResult = rows.length;
153
+ return { rows, classes: resultClasses };
154
+ }
155
+ /** `--model <selector>[,...]` keeps the rows whose run_start named exactly one of the listed
156
+ * selectors (case-sensitive, the strings `--summary --by model` prints); a null-model row never
157
+ * matches. An unknown selector is not an error: models have no closed vocabulary and an old log
158
+ * holds models the config has since dropped, so it simply keeps no rows. */
159
+ function applyHistoryModelFilter(rows, rest) {
163
160
  const modelRaw = rejectFlagLookingValue("history --model", flagValue(rest, "--model"));
164
161
  let models = null;
165
162
  if (modelRaw !== null) {
@@ -167,29 +164,32 @@ export async function cmdHistory(root, args, seams = {}) {
167
164
  models = pieces;
168
165
  rows = runRowsOnModels(rows, pieces);
169
166
  }
170
- if (followTail !== null) {
171
- await followHistory(root, rest, rows, followTail, (r) => rowKept(r, grepPattern, resultClasses, models), seams, rest.includes("--json"));
172
- return;
173
- }
174
- // --json swaps the renderer for the collector's own payload, exactly as status --json and
175
- // report --json: the same rows the table prints, each with ts/tokens/costUsd kept raw. A
176
- // JSON document even when the log is empty ({"rows":[]} — never the prose `no runs yet`,
177
- // the report --json precedent: the flag's output must be parseable in every exit-0 case).
167
+ return { rows, models };
168
+ }
169
+ /** Render the collected-and-filtered rows: the `--by`/`--summary` summary grouping, the `--json`
170
+ * document, the empty-state wording (blaming the filter that emptied the set, else the log or
171
+ * window), the aligned table, and the sparse-window note. A bad `--by` value fails here, after
172
+ * row collection and filtering and before `--json`. */
173
+ function sayHistory(rows, q, covered, filtered) {
178
174
  // `--by role|model` picks how --summary groups its lines (role by default).
179
- const byRaw = flagValue(rest, "--by");
175
+ const byRaw = flagValue(q.rest, "--by");
180
176
  let byModel = false;
181
- if (rest.includes("--by")) {
177
+ if (q.rest.includes("--by")) {
182
178
  if (!byRaw)
183
179
  return fail(HISTORY_BY_VALUE_ERROR);
184
180
  if (!HISTORY_SUMMARY_GROUPS.includes(byRaw)) {
185
181
  fail(`history --by must be one of ${HISTORY_SUMMARY_GROUPS.join(", ")}${gotSuffix(byRaw)}${typoSuffix(byRaw, [...HISTORY_SUMMARY_GROUPS])}`);
186
182
  }
187
- if (!rest.includes("--summary"))
183
+ if (!q.rest.includes("--summary"))
188
184
  fail("history --by needs --summary (it picks how the summary groups runs)");
189
185
  byModel = byRaw === "model";
190
186
  }
191
- const summary = rest.includes("--summary") && rows.length > 0;
192
- if (rest.includes("--json")) {
187
+ const summary = q.rest.includes("--summary") && rows.length > 0;
188
+ // --json swaps the renderer for the collector's own payload, exactly as status --json and
189
+ // report --json: the same rows the table prints, each with ts/tokens/costUsd kept raw. A
190
+ // JSON document even when the log is empty ({"rows":[]} — never the prose `no runs yet`,
191
+ // the report --json precedent: the flag's output must be parseable in every exit-0 case).
192
+ if (q.rest.includes("--json")) {
193
193
  sayJson(summary ? (byModel ? summarizeRunRowsByModel(rows) : summarizeRunRows(rows)) : { rows });
194
194
  return;
195
195
  }
@@ -205,17 +205,17 @@ export async function cmdHistory(root, args, seams = {}) {
205
205
  // empty log or window scanned no rows, so the result filter removed nothing and the
206
206
  // empty-log/window prose is the true one — the dashboard's History view draws the same
207
207
  // line. A grep that admitted nothing keeps its own wording regardless of --result.
208
- say(models !== null && afterResult > 0
209
- ? `no runs on model "${models.join(",")}"`
210
- : resultClasses !== null && afterGrep > 0
211
- ? `no runs with result "${resultClasses.join(",")}"`
212
- : grepPattern !== null
213
- ? `no runs matching "${grepPattern}"`
214
- : sinceMs === null
208
+ say(filtered.models !== null && filtered.afterResult > 0
209
+ ? `no runs on model "${filtered.models.join(",")}"`
210
+ : filtered.classes !== null && filtered.afterGrep > 0
211
+ ? `no runs with result "${filtered.classes.join(",")}"`
212
+ : q.grepPattern !== null
213
+ ? `no runs matching "${q.grepPattern}"`
214
+ : q.sinceMs === null
215
215
  ? "no runs yet"
216
- : untilMs === null
217
- ? `no runs in ${durationLabel(sinceMs)}`
218
- : `no runs between ${durationLabel(sinceMs)} and ${durationLabel(untilMs)} ago`);
216
+ : q.untilMs === null
217
+ ? `no runs in ${durationLabel(q.sinceMs)}`
218
+ : `no runs ${betweenWindow(q.sinceMs, q.untilMs)}`);
219
219
  return;
220
220
  }
221
221
  if (summary)
@@ -231,6 +231,41 @@ export async function cmdHistory(root, args, seams = {}) {
231
231
  if (!covered)
232
232
  say(SPARSE_WINDOW_NOTE);
233
233
  }
234
+ /** `tumwater history [--role <id>[,<id>...]] [-n N] [-f] [--since <duration>]
235
+ * [--until <duration>] [--grep <text>] [--result <class>] [--model <selector>[,...]]`: print the
236
+ * last N completed runs, newest first — or,
237
+ * with `--since`, the runs of a bounded past window over the same record, the same shape
238
+ * `logs --since` and `report --since` speak. `--grep` keeps rows whose rendered line matches,
239
+ * `--result` narrows to the listed outcome classes (changes/none/problems, the dashboard History
240
+ * filter's buckets) and `--model` keeps runs that started on the listed models; all compose
241
+ * with --role, -n, and --since. Read-only: stdout only, no state file created — a missing or empty
242
+ * event log prints `no runs yet` and exits 0. */
243
+ export async function cmdHistory(root, args, seams = {}) {
244
+ const q = parseHistoryQuery(root, args);
245
+ let { rows, covered } = collectHistoryRows(root, q);
246
+ // The filter runs on the collected rows, before rendering and before --json serialization:
247
+ // the same rows the table would print, filtered the same way in both forms (the logs --grep
248
+ // rule: -n bounds the scanned window, not the printed rows).
249
+ if (q.grepPattern !== null)
250
+ rows = runRowsMatchingGrep(rows, q.grepPattern);
251
+ // The set --grep admitted, saved so the empty-state wording blames the filter that actually
252
+ // emptied it: if the result filter below removes rows grep matched, naming the pattern would
253
+ // be false (`no runs matching "widget"` when feature #7 does match). The GUI draws the same
254
+ // line between a search and its outcome filter.
255
+ const afterGrep = rows.length;
256
+ // --result and --model each narrow the set further, and the counts saved here let the
257
+ // empty-state wording blame whichever filter actually emptied it.
258
+ const result = applyHistoryResultFilter(rows, q.rest);
259
+ rows = result.rows;
260
+ const afterResult = rows.length;
261
+ const model = applyHistoryModelFilter(rows, q.rest);
262
+ rows = model.rows;
263
+ if (q.followTail !== null) {
264
+ await followHistory(root, q.rest, rows, q.followTail, (r) => rowKept(r, q.grepPattern, result.classes, model.models), seams, q.rest.includes("--json"));
265
+ return;
266
+ }
267
+ sayHistory(rows, q, covered, { afterGrep, afterResult, classes: result.classes, models: model.models });
268
+ }
234
269
  /** The row filters `--grep`, `--result` and `--model` apply, as one predicate for followed rows. */
235
270
  function rowKept(row, grep, classes, models) {
236
271
  if (grep !== null && runRowsMatchingGrep([row], grep).length === 0)
@@ -8,7 +8,6 @@ import { tooLongMessage } from "../text/text.js";
8
8
  import { defaultConfig, loadConfigSafe } from "../config/config.js";
9
9
  import { roleImageInfo } from "../agent/image-input.js";
10
10
  import { readPiListedModels } from "../pi/pi-listed-models.js";
11
- import { readImageProbeCache } from "../doctor/image-probe.js";
12
11
  import { roleLabel } from "../roles/role-labels.js";
13
12
  /** The user-facing submission pipeline for the prompt queues (the store's mechanics —
14
13
  * listing, peeking, dequeuing, cancelling, and their race policy — live in src/inbox/inbox.ts,
@@ -46,10 +45,7 @@ export function promptLengthProblem(text, role = DIRECTOR_ROLE) {
46
45
  export function imagePromptGuidance(root, role) {
47
46
  const { config } = loadConfigSafe(root);
48
47
  const cfg = config ?? defaultConfig();
49
- // The same probe cache the status rows, role page, and images alert read (roleImageInfo's
50
- // fourth argument): a claimed pi model whose doctor probe failed reads text-only here too,
51
- // so the gate never grants image capability that the surfaces report as absent.
52
- const { imageInput, imageReroute } = roleImageInfo(cfg, role, undefined, readImageProbeCache(root), readPiListedModels(root));
48
+ const { imageInput, imageReroute } = roleImageInfo(cfg, role, undefined, readPiListedModels(root));
53
49
  if (imageInput !== false)
54
50
  return {};
55
51
  if (imageReroute) {
@@ -62,8 +62,9 @@ export class PendingPrompt {
62
62
  }
63
63
  /** Put an unfulfilled user prompt back in the queue it came from so the next run retries it
64
64
  * — the one place that policy lives, shared by every outcome that leaves the request undone
65
- * (abort, timeout, failure without changes, review abort, context-ceiling cut-off, red-main
66
- * gate). The queue is the role's own (the director's historical inbox for the director), so a
65
+ * (abort, timeout, failure without changes, review abort, context-ceiling cut-off, a
66
+ * merge-side `main_red` pin). The queue is the role's own (the director's historical
67
+ * inbox for the director), so a
67
68
  * re-queued per-role request never leaks across roles. A fulfilled no_change never reaches
68
69
  * here: re-queueing it would loop the prompt forever. Image attachments ride along: the
69
70
  * re-queue moves them to the new queue file's stem so a later cancel or fulfilling sweep