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,382 +1,13 @@
1
1
  /**
2
- * The dashboard's GET data endpoint handlers (src/gui/gui-server.ts routes to them): transcript,
3
- * prompt-text, backlog, report, failures, history, tick, diff, config — the read-only surface.
4
- * The POST
5
- * operator endpoints (prompt, prompt-role, prompt-cancel, prompt-edit, budget, config-set, pause,
6
- * wake, restart, abort, pause-role) and their body-discipline helpers live in
7
- * gui/gui-endpoint-commands.ts. Each handler answers its request and touches no socket beyond
8
- * its own `res`; server lifecycle, routing, the static page, and the token gate stay in
9
- * gui/gui-server.ts. The domain work itself lives one layer down (transcript.ts, backlog.ts,
10
- * report-data.ts, src/failure/failure-data.ts, history-data.ts, tick-detail-data.ts, config.ts)
11
- * — this module only adapts HTTP onto it.
2
+ * The dashboard's GET data endpoint handlers (src/gui/gui-server.ts routes to them), gathered
3
+ * from the per-family modules gui-history-endpoints (transcript, history, logs, run, role),
4
+ * gui-backlog-endpoints (backlog, backlog-match, diff), gui-report-endpoints (report,
5
+ * failures), and gui-admin-endpoints (prompt-text, doctor, config, retire-list, brief) — the
6
+ * read-only surface, split one family per file the way the POST side keeps a file per command
7
+ * domain. The per-family modules adapt HTTP onto the domain work a layer down; server
8
+ * lifecycle, routing, the static page, and the token gate stay in gui/gui-server.ts.
12
9
  */
13
- import { refusalNote, waitingNote } from "../backlog/backlog-eligibility.js";
14
- import { answeredQuestionEntries, donePlanEntries, fixedBugEntries, openBugEntries, openQuestionEntries, plannedPlanEntries } from "../backlog/backlog.js";
15
- import { runDoctor } from "../doctor/doctor.js";
16
- import { knownRoleIds, loadConfigSafe } from "../config/config.js";
17
- import { loopEnabled } from "../roles/loop-ids.js";
18
- import { artifactPhrase, previewRetire } from "../operator/retire.js";
19
- import { EDITABLE_CONFIG_KEYS } from "../config/config-editable-keys.js";
20
- import { collectReport, collectReportSince } from "../report/report-data.js";
21
- import { collectFailureReport, collectFailureReportSince } from "../failure/failure-data.js";
22
- import { renderFailureMarkdown } from "../failure/failure-render.js";
23
- import { renderSinceReportMarkdown } from "../report/report-render.js";
24
- import { REPORT_SINCE_MAX_MS, reportSinceMsg } from "../events/event-window.js";
25
- import { humanSeconds } from "../text/datetime.js";
26
- import { collectTeamChanges, collectRoleChange } from "../change/change-data.js";
27
- import { readTranscript } from "../ui/transcript.js";
28
- import { HISTORY_DEFAULT_TICKS, HISTORY_MAX_TICKS, readTickRows, readTickRowsSince, summarizeTickRows, tickRowsMatchingGrep } from "../history/history-data.js";
29
- import { LOGS_SINCE_MAX_MS, SPARSE_WINDOW_NOTE } from "../events/event-window.js";
30
- import { eventRole, readEvents } from "../events/event-read.js";
31
- import { eventFeedItem, eventMatchesGrep } from "../events/event-format.js";
32
- import { readTickDetail } from "../tick/tick-detail-data.js";
33
- import { renderTickDetail, tickDetailStat, tickNotFoundMessage } from "../tick/tick-detail.js";
34
- import { rolePayload } from "../roles/role-view.js";
35
- import { DIRECTOR_ROLE } from "../roles/roles.js";
36
- import { readQueuedPromptBody } from "../inbox/inbox-edit.js";
37
- import { intQuery, rejectBadQueueFileName, rejectBadRole, sinceMsQuery, windowDays } from "./gui-args.js";
38
- import { readBrief } from "../brief/brief.js";
39
- import { badRequest, okGone, sendJson } from "./http-body.js";
40
- import { typoSuffix } from "../text/suggest.js";
41
- /** Handle GET /api/transcript?role=<id>&n=N: rendered transcript lines for one loop's pi
42
- * log (same rendering as `tumwater logs --role <id>`). Unknown/missing role or a bad n → 400.
43
- * User-defined loops are valid targets too — the GUI marks them with an asterisk, so clicking
44
- * one must open its transcript: ids validate through rejectBadRole. The 400 message lists
45
- * exactly the ids accepted. The query arrives pre-parsed — the server parses the target
46
- * once (gui/gui-server.ts's parseRequestTarget) and threads it down, so the parse idiom lives
47
- * in one place and cannot disagree with the routing or the token gate about what the URL said. */
48
- export function handleTranscript(q, res, root) {
49
- const role = q.get("role");
50
- if (rejectBadRole(root, res, role))
51
- return;
52
- const n = intQuery(q, res, "n", "positive", 50);
53
- if (n === null)
54
- return;
55
- sendJson(res, 200, { lines: readTranscript(root, role, n) });
56
- }
57
- /** The /api/backlog `file` values and the open-section reader each addresses — one table so
58
- * the on-demand dispatch and the "valid values" list in both 400s cannot drift apart. A Map,
59
- * not an object literal, so no `file` spelling (`constructor`, `toString`, `__proto__`) can
60
- * reach an inherited member through the lookup. Insertion order is the list order. */
61
- const BACKLOG_FILE_READERS = new Map([
62
- ["plans", plannedPlanEntries],
63
- ["bugs", openBugEntries],
64
- ["questions", openQuestionEntries],
65
- ["done", donePlanEntries],
66
- ["fixed", fixedBugEntries],
67
- ["answered", (root) => answeredQuestionEntries(root).reverse()],
68
- ]);
69
- const BACKLOG_FILE_NAMES = [...BACKLOG_FILE_READERS.keys()];
70
- const BACKLOG_FILE_VALUES = BACKLOG_FILE_NAMES.join(", ");
71
- /** Handle GET /api/backlog?file=<plans|bugs|questions|done|fixed|answered>&index=N: one
72
- * backlog entry's full text ({title, body}), fetched on demand so multi-KB bodies (long repros,
73
- * whole plans) never ride the 1-second /api/status poll. index addresses the Nth entry of that
74
- * file's section in the same order statusPayload lists its titles — plans: PLANS.md ## Planned,
75
- * bugs: BUGS.md ## Open, questions: QUESTIONS.md ## Open, done: PLANS.md ## Done and fixed:
76
- * BUGS.md ## Fixed (both newest first, the order of the dashboard's recent rows) and answered:
77
- * QUESTIONS.md ## Answered, newest first (the file is appended to, so the reverse of file
78
- * order, unlike done/fixed) — zero-based.
79
- * Unknown/missing file, missing or bad index, and out-of-range index
80
- * → 400 JSON error via sendJson.
81
- * The query arrives pre-parsed (gui/gui-server.ts's parseRequestTarget), like every
82
- * GET-data handler here. */
83
- export function handleBacklog(q, res, root) {
84
- const file = q.get("file");
85
- if (file === null) {
86
- badRequest(res, `file required (valid values: ${BACKLOG_FILE_VALUES})`);
87
- return;
88
- }
89
- const reader = BACKLOG_FILE_READERS.get(file);
90
- if (!reader) {
91
- badRequest(res, `unknown file ${JSON.stringify(file)} (valid values: ${BACKLOG_FILE_VALUES})${typoSuffix(file, BACKLOG_FILE_NAMES)}`);
92
- return;
93
- }
94
- const entries = reader(root);
95
- const index = intQuery(q, res, "index", "non-negative");
96
- if (index === null)
97
- return;
98
- const entry = entries[index];
99
- if (!entry) {
100
- badRequest(res, `index ${index} out of range (${file} has ${entries.length} entries)`);
101
- return;
102
- }
103
- sendJson(res, 200, { title: entry.title, body: entry.body, refused: refusalNote(entry.body) !== null, waiting: waitingNote(entry.body) !== null });
104
- }
105
- /** Handle GET /api/diff[?role=<id>]: the unmerged-change view the CLI prints from the same
106
- * collectors — no `role` → collectTeamChanges (exactly `tumwater diff --json`'s document:
107
- * `{ mainBranch, roles }`, no per-role patch halves); `?role=<id>` → collectRoleChange (the
108
- * full per-role view with `diff`/`uncommittedDiff`, `tumwater diff --role <id> --json`). The
109
- * role is a target, not a filter, so it validates through rejectBadRole exactly like
110
- * /api/transcript and /api/run (unknown/missing → 400 naming the valid ids) — the same ids
111
- * `parseRoleFlag` accepts on the CLI, including disabled and user-defined loops. The
112
- * collectors degrade absent worktrees and missing base branches to their `state` instead of
113
- * throwing, so a fresh or half-built repo still answers 200. Reads git plumbing directly, so
114
- * it works whether or not the team is running. */
115
- export async function handleDiff(q, res, root) {
116
- const role = q.get("role");
117
- if (role === null) {
118
- sendJson(res, 200, await collectTeamChanges(root));
119
- return;
120
- }
121
- if (rejectBadRole(root, res, role))
122
- return;
123
- sendJson(res, 200, await collectRoleChange(root, role));
124
- }
125
- /** Handle GET /api/report?days=N[&role=<id>] or /api/report?sinceMs=<ms>[&role=<id>]: the
126
- * usage report — the day series (collectReport's ReportData) as JSON the dashboard's report tab
127
- * charts, or, when sinceMs is set, the trailing-window totals rendered through
128
- * renderSinceReportMarkdown as { markdown } (the CLI's `report --since` shape), bounded by the
129
- * reportSinceMs helper. The day window follows windowDays. An absent role is the team-wide
130
- * report exactly as before; a present one is a filter, validated through rejectBadRole (unknown,
131
- * including the empty string → 400 naming the valid ids) and handed to the collector's role
132
- * scope. Reads files directly, so it works whether or not the team is running. */
133
- export function handleReport(q, res, root) {
134
- const role = q.get("role");
135
- if (role !== null && rejectBadRole(root, res, role))
136
- return;
137
- if (q.get("sinceMs") !== null) {
138
- const sinceMs = reportSinceMs(q, res);
139
- if (sinceMs === null)
140
- return;
141
- sendJson(res, 200, { markdown: renderSinceReportMarkdown(collectReportSince(root, sinceMs, role ?? undefined)) });
142
- return;
143
- }
144
- sendJson(res, 200, collectReport(root, windowDays(q), role ?? undefined));
145
- }
146
- /** Parse and bound the report endpoints' `?sinceMs=` trailing-window length: a simultaneous
147
- * `?days=` is the CLI's rival shape (whole local days vs a trailing window) → 400 naming both;
148
- * otherwise the length rides sinceMsQuery (gui-args.ts) — positive-integer parse, then a 400
149
- * naming the cap rather than a silent clamp (an explicit window typo must not shrink to some
150
- * other window) — with the "must be between 1 and …" wording. Returns the parsed length, or
151
- * null once the 400 is sent. */
152
- function reportSinceMs(q, res) {
153
- if (q.get("days") !== null) {
154
- badRequest(res, "days and sinceMs are mutually exclusive; days counts whole local days, sinceMs totals a trailing window");
155
- return null;
156
- }
157
- return sinceMsQuery(q, res, REPORT_SINCE_MAX_MS, reportSinceMsg);
158
- }
159
- /** Handle GET /api/failures?days=N[&role=<id>] or /api/failures?sinceMs=<ms>[&role=<id>]: the
160
- * same bounded Markdown failure digest the telemetry loop feeds on and `tumwater report
161
- * --failures` prints, as JSON ({ markdown }) — the dashboard's failures tab renders it; with
162
- * sinceMs it is the trailing-window digest (collectFailureReportSince) through the same render,
163
- * the CLI's `report --failures --since` shape. The days window follows windowDays (so it can
164
- * never drift from /api/report's). role rides the same absent-team / present-filter rule as
165
- * handleReport: absent → the team digest, present → validated and scoped. Reads files
166
- * directly, so it works with no team running. */
167
- export function handleFailures(q, res, root) {
168
- const role = q.get("role");
169
- if (role !== null && rejectBadRole(root, res, role))
170
- return;
171
- if (q.get("sinceMs") !== null) {
172
- const sinceMs = reportSinceMs(q, res);
173
- if (sinceMs === null)
174
- return;
175
- sendJson(res, 200, { markdown: renderFailureMarkdown(collectFailureReportSince(root, sinceMs, role ?? undefined)) });
176
- return;
177
- }
178
- sendJson(res, 200, { markdown: renderFailureMarkdown(collectFailureReport(root, windowDays(q), role ?? undefined)) });
179
- }
180
- /** Handle GET /api/history?role=<id>&n=N[&grep=<text>] — or ?role=<id>&sinceMs=<ms> for a
181
- * trailing window: the per-tick rows the CLI's `tumwater history` derives from the event log,
182
- * the dashboard's history tab renders them. The count shape ({ rows }) rides readTickRows (the
183
- * shared scan, window growth included, so a role-filtered ask on a busy team cannot silently
184
- * come back short); n is optional: absent → HISTORY_DEFAULT_TICKS; present but not a plain
185
- * non-negative integer → 400 (the handleBacklog index discipline — an explicit count that does
186
- * not parse is a client error, unlike the report's degrade-to-default typo rule); then clamped
187
- * to [1, HISTORY_MAX_TICKS] — the GUI clamps where the CLI fails fast, its established
188
- * convention. grep is optional: absent or empty filters nothing; present → the rows are
189
- * filtered through history-data.ts's shared tickRowsMatchingGrep rule, the same
190
- * case-insensitive haystack rule `tumwater history --grep` uses (shared, not
191
- * re-implemented), after the n/role scan so n bounds the scanned window and not the
192
- * printed rows — the CLI's rule. The window shape ({ rows, covered, note }) rides
193
- * readTickRowsSince, the collector behind `history --since`,
194
- * so the browser and the CLI cannot disagree about the window's rows; a simultaneous n is a
195
- * rival shape → 400 (the CLI's count-vs-window rule), sinceMs parses and is capped through
196
- * sinceMsQuery (gui-args.ts, LOGS_SINCE_MAX_MS — the shared `history --since` cap), and note is
197
- * SPARSE_WINDOW_NOTE when covered is false, else "". role is optional: absent or empty → all
198
- * loops; present → passed straight through as the role filter with no config validation (a
199
- * filter is not a target — an id with no ticks legitimately yields zero rows), so the endpoint
200
- * reads nothing but files, like /api/report and /api/failures. Reads files directly, so it
201
- * present → passed straight through as the role filter with no config validation (a
202
- * filter is not a target — an id with no ticks legitimately yields zero rows), so the endpoint
203
- * reads nothing but files, like /api/report and /api/failures. summary is optional: absent /
204
- * empty adds nothing (the response is byte-for-byte today's); present (any value — the flag's
205
- * mere presence, a bad value ignored like a missing one) adds an additive `summary` folding the
206
- * exact served rows through the shared summarizeTickRows collector, the same tally
207
- * `tumwater history --summary` prints, so the dashboard Summary and the table cannot disagree
208
- * about what a tick was. Reads files directly, so it works whether or not the team is running. */
209
- export function handleHistory(q, res, root) {
210
- const role = q.get("role") || null; // "" and absent both read all loops
211
- const grep = q.get("grep") || null; // "" and absent both filter nothing
212
- const summary = q.get("summary") !== null; // the flag's presence, whatever its value
213
- if (q.get("sinceMs") !== null) {
214
- if (q.get("n") !== null) {
215
- badRequest(res, "sinceMs cannot be combined with n (a count and a window are rival shapes)");
216
- return;
217
- }
218
- const sinceMs = sinceMsQuery(q, res, LOGS_SINCE_MAX_MS, (ms) => `sinceMs is capped at ${humanSeconds(LOGS_SINCE_MAX_MS / 1000)} (got ${ms})`);
219
- if (sinceMs === null)
220
- return;
221
- const { rows, covered } = readTickRowsSince(root, sinceMs, role);
222
- const matched = grep === null ? rows : tickRowsMatchingGrep(rows, grep);
223
- sendJson(res, 200, { rows: matched, covered, note: covered ? "" : SPARSE_WINDOW_NOTE, ...(summary ? { summary: summarizeTickRows(matched) } : {}) });
224
- return;
225
- }
226
- const parsed = intQuery(q, res, "n", "non-negative", HISTORY_DEFAULT_TICKS);
227
- if (parsed === null)
228
- return;
229
- const n = Math.min(HISTORY_MAX_TICKS, Math.max(1, parsed));
230
- let rows = readTickRows(root, n, role);
231
- if (grep !== null)
232
- rows = tickRowsMatchingGrep(rows, grep);
233
- sendJson(res, 200, summary ? { rows, summary: summarizeTickRows(rows) } : { rows });
234
- }
235
- /** How many of the log's newest events `GET /api/logs` scans by default, and the most it will
236
- * scan however large `n` asks: the CLI's `-n` shape (n bounds the scanned tail, not the printed
237
- * rows), sized so the dashboard's search can reach well past the RECENT_EVENTS window the
238
- * status poll carries. */
239
- const LOGS_SEARCH_EVENTS = 500;
240
- const LOGS_SEARCH_MAX_EVENTS = 5000;
241
- /** Handle GET /api/logs?n=N[&grep=<text>][&role=<id>]: a slice of the raw event log scanned
242
- * from its newest end, filtered with the CLI's own rules and shaped as the activity feed's
243
- * items. `n` is optional (absent → LOGS_SEARCH_EVENTS) and clamps to [1, LOGS_SEARCH_MAX_EVENTS]
244
- * after intQuery's non-negative parse (an explicit count that does not parse is a 400, the
245
- * handleHistory discipline); it bounds the scanned tail, so a matching event older than the
246
- * last n events is absent — the same scan-then-filter rule `tumwater logs -n N --grep` applies.
247
- * grep (absent or empty filters nothing) matches through eventMatchesGrep, and role is a
248
- * filter, not a target: an id with no events legitimately yields an empty list, so it does not
249
- * validate through rejectBadRole (the handleHistory rule). The matched events answer
250
- * oldest-first as eventFeedItem objects — the same shape the status payload's eventItems carry.
251
- * Reads the tail through readEvents' stat-keyed cache (no covered byte end, unlike the CLI's
252
- * follow), so a dashboard search that repeats on an unchanged log — the debounced filter fires
253
- * one per filter change — costs one stat instead of re-reading and re-parsing the scanned tail.
254
- * Reads files directly, so it works whether or not the team is running. */
255
- export function handleLogs(q, res, root) {
256
- const parsed = intQuery(q, res, "n", "non-negative", LOGS_SEARCH_EVENTS);
257
- if (parsed === null)
258
- return;
259
- const n = Math.min(LOGS_SEARCH_MAX_EVENTS, Math.max(1, parsed));
260
- const role = q.get("role") || null; // "" and absent both read all loops
261
- const grep = q.get("grep") || null; // "" and absent both filter nothing
262
- const needle = grep === null ? null : grep.toLowerCase();
263
- const events = readEvents(root, n);
264
- const matched = events.filter((e) => (role === null || eventRole(e) === role) && (needle === null || eventMatchesGrep(e, needle)));
265
- sendJson(res, 200, { events: matched.map(eventFeedItem) });
266
- }
267
- /** Handle GET /api/run?role=<id>&run=N: one tick's full event trail — readTickDetail's
268
- * TickDetail, the collector `tumwater tick <role> <n>` prints from, plus `text`: the same
269
- * payload through renderTickDetail, pre-rendered server-side like /api/transcript's lines so
270
- * the browser shows the CLI's exact rendering instead of re-implementing the event formatting.
271
- * Serving the shared collector is the point: the CLI and this endpoint cannot drift on what one
272
- * tick's block contains. The role is a target here, not a filter, so it validates through
273
- * rejectBadRole (unknown/missing → 400 naming the valid ids); the tick is an explicit count
274
- * through intQuery (missing or not a positive integer → 400); and a tick the scanned window
275
- * does not hold — never ran, or rotation ate it — answers 404 with tickNotFoundMessage, the
276
- * CLI's exact not-found wording,
277
- * never a crash. Reads files directly, so it works whether or not the team is running. */
278
- export async function handleTick(q, res, root) {
279
- const role = q.get("role");
280
- if (rejectBadRole(root, res, role))
281
- return;
282
- const tick = intQuery(q, res, "run", "positive");
283
- if (tick === null)
284
- return;
285
- const detail = readTickDetail(root, role, tick);
286
- if (detail === null) {
287
- sendJson(res, 404, { error: tickNotFoundMessage(role, tick) });
288
- return;
289
- }
290
- sendJson(res, 200, { ...detail, text: renderTickDetail(detail), stat: await tickDetailStat(root, detail) });
291
- }
292
- /** Handle GET /api/role?role=<id>: one loop's inspection payload — rolePayload's object,
293
- * the same collection behind `tumwater role <id>` — so the drawer's Notebook and Next tick
294
- * prompt sections render what the CLI shows without re-implementing the assembly. The role
295
- * is a target here, so it validates through rejectBadRole exactly like /api/transcript and
296
- * /api/run (unknown/missing → the shared 400 naming the valid ids and correcting typos);
297
- * rolePayload is only reached with an id rejectBadRole accepted, so its own unknown-role
298
- * throw never fires. Reads files directly, so it works whether or not the team is running. */
299
- export function handleRole(q, res, root) {
300
- const role = q.get("role");
301
- if (rejectBadRole(root, res, role))
302
- return;
303
- sendJson(res, 200, rolePayload(root, role));
304
- }
305
- /** Handle GET /api/prompt-text?file=<name>[&role=<id>]: one queued prompt's editable body —
306
- * the text an edit replaces, for the dashboard's per-row edit affordance (PLANS.md
307
- * 2026-10-11). The prompt is addressed by its queue-file basename (the /api/prompt-cancel
308
- * address), so a stale poll's snapshot can never read the wrong entry; the not-before deferral
309
- * marker and the `[image attached: …]` reference block are stripped, both plumbing the edit
310
- * carries over unchanged. The role is optional (absent reads the director's queue) and
311
- * validates exactly like /api/transcript; a file name failing the shared
312
- * rejectBadQueueFileName guard is a user-input error answered 400 with nothing read. A
313
- * vanished file answers 200 { ok: true, status: "gone" } — the loop already dequeued
314
- * it, which is data for the UI, not a server fault. Reads one file directly, so it works
315
- * whether or not the team is running. */
316
- export function handlePromptText(q, res, root) {
317
- const roleParam = q.get("role");
318
- if (roleParam !== null && rejectBadRole(root, res, roleParam))
319
- return;
320
- const role = roleParam ?? DIRECTOR_ROLE;
321
- const file = q.get("file");
322
- if (rejectBadQueueFileName(res, file))
323
- return;
324
- const text = readQueuedPromptBody(root, role, file);
325
- if (text === null) {
326
- okGone(res);
327
- return;
328
- }
329
- sendJson(res, 200, { ok: true, text });
330
- }
331
- /** Handle GET /api/doctor: the same DoctorReport `tumwater doctor --json` prints, so the
332
- * Settings view's Health check card and the CLI cannot disagree. Read-only — the checks write
333
- * nothing (runDoctor's own contract) — and it works whether or not the team is running. `run`
334
- * exists so tests can skip the host probes; a throw reaches the server's outer 500. */
335
- export async function handleDoctor(res, root, run = (r) => runDoctor(r, undefined, undefined, "never")) {
336
- sendJson(res, 200, await run(root));
337
- }
338
- /** Handle GET /api/config: the Settings view's resolved values for exactly
339
- * EDITABLE_CONFIG_KEYS — read through the same load path `tumwater config get` uses
340
- * (loadConfigSafe, defaults merged in), so the page shows what the team would actually
341
- * load. A broken or invalid tumwater.json answers 500 with validateConfig's message instead
342
- * of serving a half-empty panel. */
343
- export function handleConfig(res, root) {
344
- const { config, error } = loadConfigSafe(root);
345
- if (config === undefined) {
346
- sendJson(res, 500, { error });
347
- return;
348
- }
349
- const record = config;
350
- const out = {};
351
- for (const key of EDITABLE_CONFIG_KEYS)
352
- out[key] = record[key] ?? null;
353
- sendJson(res, 200, out);
354
- }
355
- /** Handle GET /api/retire: the Settings view's "Disabled roles" card — every configured,
356
- * disabled, non-director role, with what `tumwater retire` would remove (previewRetire, so the
357
- * rails and wording are the CLI's) and the rails' objections. `wouldRemove` is empty when there
358
- * is nothing to retire (a role disabled with a clean checkout), so the card can also re-enable
359
- * a role via POST /api/role-enable. Read-only (previewRetire's contract) and works whether or
360
- * not the team runs; a throw reaches the server's outer 500, an unreadable tumwater.json
361
- * answers 500 like /api/config. */
362
- export async function handleRetireList(res, root) {
363
- const { config, error } = loadConfigSafe(root);
364
- if (config === undefined) {
365
- sendJson(res, 500, { error });
366
- return;
367
- }
368
- const roles = [];
369
- for (const role of knownRoleIds(config).filter((id) => id !== DIRECTOR_ROLE && !loopEnabled(config, id))) {
370
- const { wouldRemove, objections } = await previewRetire(root, role);
371
- roles.push({ role, wouldRemove, phrases: wouldRemove.map((a) => artifactPhrase(a, role)), objections });
372
- }
373
- sendJson(res, 200, { roles });
374
- }
375
- /** Handle GET /api/brief: the brief file's name and the current initial prompt (what
376
- * `tumwater brief --json` prints), or 404 when no brief carries a prompt block. */
377
- export function handleBrief(res, root) {
378
- const brief = readBrief(root);
379
- if (brief === null)
380
- return sendJson(res, 404, { error: "no project brief with a prompt block found — run `tumwater init` first" });
381
- sendJson(res, 200, { file: brief.file, prompt: brief.prompt });
382
- }
10
+ export { handleTranscript, handleHistory, handleLogs, handleRun, handleRole } from "./gui-history-endpoints.js";
11
+ export { handleBacklog, handleBacklogMatch, handleDiff } from "./gui-backlog-endpoints.js";
12
+ export { handleReport, handleFailures } from "./gui-report-endpoints.js";
13
+ export { handlePromptText, handleDoctor, handleConfig, handleRetireList, handleBrief } from "./gui-admin-endpoints.js";