tumwater 0.2.0 → 0.4.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 (464) 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 +55 -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 +6 -4
  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 +404 -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 +281 -55
  41. package/dist/src/backlog/backlog-cli-view.js +255 -0
  42. package/dist/src/backlog/backlog-cli.js +91 -335
  43. package/dist/src/backlog/backlog-commit.js +20 -13
  44. package/dist/src/backlog/backlog-eligibility.js +193 -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 +264 -0
  48. package/dist/src/backlog/backlog-release.js +134 -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-guardrails.js +136 -0
  56. package/dist/src/baseline/main-red.js +153 -36
  57. package/dist/src/baseline/main-watch.js +232 -0
  58. package/dist/src/brief/brief-edit.js +12 -12
  59. package/dist/src/brief/brief.js +31 -51
  60. package/dist/src/budget/budget-fallback-view.js +288 -0
  61. package/dist/src/budget/budget-wiring.js +158 -0
  62. package/dist/src/budget/budget.js +31 -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 +65 -3
  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 +181 -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 +183 -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 +291 -598
  82. package/dist/src/cli/cli-repo-gate.js +11 -1
  83. package/dist/src/cli/cli-review-commands.js +113 -0
  84. package/dist/src/cli/cli-role-command.js +525 -0
  85. package/dist/src/cli/cli-start-summary.js +167 -0
  86. package/dist/src/cli/{cli-run.js → cli-start.js} +193 -237
  87. package/dist/src/cli/completion.js +169 -22
  88. package/dist/src/cli/config-commands.js +58 -26
  89. package/dist/src/cli/follow-view.js +75 -0
  90. package/dist/src/cli/help.js +258 -82
  91. package/dist/src/cli/log-commands.js +164 -85
  92. package/dist/src/cli/question-commands.js +211 -10
  93. package/dist/src/cli.js +173 -45
  94. package/dist/src/collections.js +56 -13
  95. package/dist/src/concurrency/check-permit.js +20 -3
  96. package/dist/src/concurrency/lock.js +17 -13
  97. package/dist/src/concurrency/provider-lease.js +44 -8
  98. package/dist/src/concurrency/semaphore.js +102 -26
  99. package/dist/src/config/config-aliases.js +0 -1
  100. package/dist/src/config/config-editable-keys.js +16 -1
  101. package/dist/src/config/config-fallbacks.js +94 -56
  102. package/dist/src/config/config-field-checks.js +40 -10
  103. package/dist/src/config/config-keys.js +30 -8
  104. package/dist/src/config/config-live.js +63 -24
  105. package/dist/src/config/config-request.js +137 -0
  106. package/dist/src/config/config-schema.js +31 -24
  107. package/dist/src/config/config-selector-checks.js +123 -0
  108. package/dist/src/config/config-validation-agents.js +80 -0
  109. package/dist/src/config/config-validation-model.js +116 -0
  110. package/dist/src/config/config-validation.js +169 -222
  111. package/dist/src/config/config-views.js +136 -54
  112. package/dist/src/config/config-write-core.js +135 -0
  113. package/dist/src/config/config-write.js +164 -265
  114. package/dist/src/config/config.js +53 -38
  115. package/dist/src/config/model-selector.js +34 -34
  116. package/dist/src/doctor/doctor-backlog-bugs.js +330 -0
  117. package/dist/src/doctor/doctor-backlog-plans.js +398 -0
  118. package/dist/src/doctor/doctor-backlog.js +249 -512
  119. package/dist/src/doctor/doctor-check-names.js +85 -0
  120. package/dist/src/doctor/doctor-checks.js +61 -593
  121. package/dist/src/doctor/doctor-claude-checks.js +142 -0
  122. package/dist/src/doctor/doctor-codex-checks.js +276 -0
  123. package/dist/src/doctor/doctor-contention-checks.js +506 -0
  124. package/dist/src/doctor/doctor-fallback-checks.js +130 -0
  125. package/dist/src/doctor/doctor-holds.js +80 -66
  126. package/dist/src/doctor/doctor-install.js +2 -1
  127. package/dist/src/doctor/doctor-model-checks.js +216 -537
  128. package/dist/src/doctor/doctor-notify.js +9 -5
  129. package/dist/src/doctor/doctor-opencode-checks.js +164 -0
  130. package/dist/src/doctor/doctor-orphans.js +182 -63
  131. package/dist/src/doctor/doctor-pi-checks.js +62 -0
  132. package/dist/src/doctor/doctor-project-checks.js +397 -0
  133. package/dist/src/doctor/doctor.js +126 -77
  134. package/dist/src/doctor/image-probe.js +21 -8
  135. package/dist/src/doctor/outcome.js +3 -2
  136. package/dist/src/doctor/probe.js +21 -12
  137. package/dist/src/events/event-format.js +185 -89
  138. package/dist/src/events/event-labels.js +11 -5
  139. package/dist/src/events/event-legacy.js +39 -15
  140. package/dist/src/events/event-read.js +52 -39
  141. package/dist/src/events/event-tally.js +16 -0
  142. package/dist/src/events/event-window.js +80 -26
  143. package/dist/src/events/events.js +30 -13
  144. package/dist/src/events/notify.js +7 -7
  145. package/dist/src/failure/failure-cluster.js +12 -12
  146. package/dist/src/failure/failure-data.js +65 -60
  147. package/dist/src/failure/failure-render.js +29 -21
  148. package/dist/src/failure/failure-state-change.js +17 -12
  149. package/dist/src/failure/time-spend.js +35 -35
  150. package/dist/src/files/file-queue.js +17 -0
  151. package/dist/src/files/files.js +111 -35
  152. package/dist/src/files/json-files.js +3 -3
  153. package/dist/src/files/json-object.js +28 -7
  154. package/dist/src/files/stat-cache.js +2 -2
  155. package/dist/src/files/tail.js +1 -1
  156. package/dist/src/gates/bootstrap-gates.js +4 -5
  157. package/dist/src/gates/budget-gates.js +88 -225
  158. package/dist/src/gates/disk-gate.js +1 -1
  159. package/dist/src/gates/gate-polls.js +101 -143
  160. package/dist/src/gates/maintenance-quota.js +37 -30
  161. package/dist/src/gates/pause-gates.js +28 -10
  162. package/dist/src/gates/readiness.js +7 -3
  163. package/dist/src/gates/role-cap-gates.js +8 -8
  164. package/dist/src/gates/startup-gate.js +94 -32
  165. package/dist/src/gates/streak-gate.js +54 -30
  166. package/dist/src/git/commit-message.js +89 -46
  167. package/dist/src/git/git-diff.js +16 -10
  168. package/dist/src/git/git-run.js +79 -38
  169. package/dist/src/git/git.js +54 -34
  170. package/dist/src/git/slot-salvage.js +253 -0
  171. package/dist/src/git/slots-state.js +6 -6
  172. package/dist/src/git/worktree-pool.js +132 -38
  173. package/dist/src/git/worktree-use.js +4 -11
  174. package/dist/src/git/worktree.js +94 -37
  175. package/dist/src/gui/gui-admin-endpoints.js +96 -0
  176. package/dist/src/gui/gui-args.js +79 -8
  177. package/dist/src/gui/gui-backlog-commands.js +43 -19
  178. package/dist/src/gui/gui-backlog-endpoints.js +156 -0
  179. package/dist/src/gui/gui-command.js +26 -13
  180. package/dist/src/gui/gui-config-commands.js +168 -80
  181. package/dist/src/gui/gui-endpoint-commands.js +5 -4
  182. package/dist/src/gui/gui-endpoint-common.js +4 -4
  183. package/dist/src/gui/gui-endpoints.js +11 -380
  184. package/dist/src/gui/gui-history-endpoints.js +301 -0
  185. package/dist/src/gui/gui-operator-commands.js +235 -13
  186. package/dist/src/gui/gui-prompt-commands.js +26 -20
  187. package/dist/src/gui/gui-report-endpoints.js +97 -0
  188. package/dist/src/gui/gui-server.js +26 -5
  189. package/dist/src/gui/gui-team-commands.js +7 -7
  190. package/dist/src/gui/http-body.js +1 -1
  191. package/dist/src/history/history-data.js +122 -234
  192. package/dist/src/history/history-queued.js +155 -0
  193. package/dist/src/history/history-render.js +42 -22
  194. package/dist/src/history/history.js +190 -49
  195. package/dist/src/inbox/inbox-attachments.js +29 -30
  196. package/dist/src/inbox/inbox-cancel.js +10 -11
  197. package/dist/src/inbox/inbox-edit.js +9 -9
  198. package/dist/src/inbox/inbox-submit.js +18 -18
  199. package/dist/src/inbox/inbox-top.js +28 -17
  200. package/dist/src/inbox/inbox.js +42 -43
  201. package/dist/src/inbox/pending-prompt.js +26 -26
  202. package/dist/src/inbox/prompt-commands.js +62 -49
  203. package/dist/src/inbox/prompt-not-before.js +2 -2
  204. package/dist/src/init/init-templates.js +1 -2
  205. package/dist/src/init/init.js +28 -23
  206. package/dist/src/merge/backlog-conflicts.js +215 -35
  207. package/dist/src/merge/merge-batch.js +14 -8
  208. package/dist/src/merge/merge-cancel.js +53 -0
  209. package/dist/src/merge/merge-check-failures.js +44 -22
  210. package/dist/src/merge/merge-core.js +58 -32
  211. package/dist/src/merge/merge-diff.js +5 -4
  212. package/dist/src/merge/merge-drain.js +45 -19
  213. package/dist/src/merge/merge-git.js +177 -38
  214. package/dist/src/merge/merge-pipeline.js +15 -3
  215. package/dist/src/merge/merge-post-review.js +214 -0
  216. package/dist/src/merge/merge-queue-render.js +48 -11
  217. package/dist/src/merge/merge-queue.js +52 -15
  218. package/dist/src/merge/merge-requeue.js +103 -0
  219. package/dist/src/merge/merge-slot.js +61 -16
  220. package/dist/src/merge/merge-stack.js +27 -11
  221. package/dist/src/merge/merge-to-main.js +77 -26
  222. package/dist/src/merge/merge-top.js +83 -0
  223. package/dist/src/merge/merge-verify.js +75 -18
  224. package/dist/src/notes-cli.js +35 -15
  225. package/dist/src/operator/operator-commands.js +192 -81
  226. package/dist/src/operator/operator-intent.js +69 -42
  227. package/dist/src/operator/operator-requests.js +35 -40
  228. package/dist/src/operator/retire.js +5 -6
  229. package/dist/src/operator/revert.js +142 -34
  230. package/dist/src/orchestrator/orchestrator-launch.js +60 -52
  231. package/dist/src/orchestrator/orchestrator-poll.js +352 -0
  232. package/dist/src/orchestrator/orchestrator-scheduling.js +71 -370
  233. package/dist/src/orchestrator/orchestrator.js +129 -369
  234. package/dist/src/orchestrator/retention.js +42 -8
  235. package/dist/src/orchestrator/scheduling-admit.js +116 -0
  236. package/dist/src/orchestrator/scheduling-scope.js +245 -0
  237. package/dist/src/orchestrator/scheduling-skip.js +195 -0
  238. package/dist/src/orchestrator/startup-migration-steps.js +8 -7
  239. package/dist/src/orchestrator/startup-migrations.js +11 -7
  240. package/dist/src/paths.js +126 -49
  241. package/dist/src/pi/command-shape.js +12 -1
  242. package/dist/src/pi/pi-args.js +6 -4
  243. package/dist/src/pi/pi-backend.js +6 -6
  244. package/dist/src/pi/pi-bin.js +54 -63
  245. package/dist/src/pi/pi-listed-models.js +53 -0
  246. package/dist/src/pi/pi-models.js +63 -30
  247. package/dist/src/pi/pi-run-result.js +1 -1
  248. package/dist/src/pi/pi-settings.js +1 -1
  249. package/dist/src/pi/pi-stream.js +6 -6
  250. package/dist/src/pi/pi-watchdogs.js +18 -19
  251. package/dist/src/pi-extension/bounded-output.js +5 -3
  252. package/dist/src/pi-extension/context-budget.js +2 -2
  253. package/dist/src/pi-extension/context-shake.js +1 -1
  254. package/dist/src/pi-extension/context-window.js +0 -1
  255. package/dist/src/pi-extension/full-output.js +3 -10
  256. package/dist/src/pi-extension/strip-thinking.js +0 -1
  257. package/dist/src/pi-extension/trim-system-prompt.js +3 -4
  258. package/dist/src/pi-extension/write-guard.js +37 -0
  259. package/dist/src/process/launch-services.js +1 -2
  260. package/dist/src/process/node-bin.js +16 -0
  261. package/dist/src/process/process-group.js +8 -18
  262. package/dist/src/process/process.js +41 -2
  263. package/dist/src/process/run-marker.js +26 -6
  264. package/dist/src/process/supervisor.js +22 -21
  265. package/dist/src/prompt/dialect.js +17 -2
  266. package/dist/src/prompt/gate-notes.js +203 -0
  267. package/dist/src/prompt/gate-prompts.js +150 -217
  268. package/dist/src/prompt/notes-backlog.js +184 -0
  269. package/dist/src/prompt/principles.js +1 -2
  270. package/dist/src/prompt/prompt-followup.js +19 -14
  271. package/dist/src/prompt/prompt.js +131 -204
  272. package/dist/src/redeploy/redeploy-policy.js +24 -19
  273. package/dist/src/redeploy/redeploy-probes.js +1 -1
  274. package/dist/src/redeploy/redeploy.js +2 -3
  275. package/dist/src/redeploy/redeployer.js +20 -20
  276. package/dist/src/redeploy/self-reload.js +2 -1
  277. package/dist/src/report/report-data.js +188 -124
  278. package/dist/src/report/report-fold.js +130 -54
  279. package/dist/src/report/report-main-red.js +55 -0
  280. package/dist/src/report/report-render.js +136 -65
  281. package/dist/src/report/report-types.js +1 -0
  282. package/dist/src/report/report.js +23 -9
  283. package/dist/src/request-timeouts.js +2 -2
  284. package/dist/src/review/diff-rules.js +53 -0
  285. package/dist/src/review/known-flakes.js +112 -1
  286. package/dist/src/review/post-merge-review-accept.js +51 -0
  287. package/dist/src/review/post-merge-review-render.js +112 -0
  288. package/dist/src/review/post-merge-review.js +239 -0
  289. package/dist/src/review/protected-edits.js +24 -10
  290. package/dist/src/review/quarantine-render.js +65 -0
  291. package/dist/src/review/review-followup.js +8 -8
  292. package/dist/src/review/review-precheck.js +66 -23
  293. package/dist/src/review/review-run.js +395 -0
  294. package/dist/src/review/review-verdict.js +2 -2
  295. package/dist/src/review/review.js +180 -401
  296. package/dist/src/review/secret-leaks.js +36 -16
  297. package/dist/src/review/suite-rerun.js +0 -1
  298. package/dist/src/roles/legacy-role-migration.js +80 -7
  299. package/dist/src/roles/role-catalog.js +127 -12
  300. package/dist/src/roles/role-guidance.js +65 -20
  301. package/dist/src/roles/role-labels.js +7 -5
  302. package/dist/src/roles/role-render.js +108 -56
  303. package/dist/src/roles/role-view.js +95 -56
  304. package/dist/src/roles/roles.js +33 -33
  305. package/dist/src/roles/runner-ids.js +97 -0
  306. package/dist/src/run/acceptance-greps.js +119 -0
  307. package/dist/src/run/ci-parity.js +121 -0
  308. package/dist/src/run/feedback-filings.js +51 -0
  309. package/dist/src/run/qa-coverage.js +125 -0
  310. package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
  311. package/dist/src/run/run-detail-data.js +175 -0
  312. package/dist/src/run/run-detail-render.js +91 -0
  313. package/dist/src/run/run-detail.js +168 -0
  314. package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +73 -60
  315. package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
  316. package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +122 -73
  317. package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
  318. package/dist/src/{tick/tick-stage.js → run/run-stage.js} +170 -83
  319. package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
  320. package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
  321. package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +105 -57
  322. package/dist/src/run/selfcheck-digest.js +40 -0
  323. package/dist/src/run/stage-backlog-checks.js +249 -0
  324. package/dist/src/run/stage-check.js +178 -0
  325. package/dist/src/run/stage-checks-shared.js +65 -0
  326. package/dist/src/run/stage-name-checks.js +204 -0
  327. package/dist/src/run/stage-path-checks.js +231 -0
  328. package/dist/src/run/stage-prose-checks.js +261 -0
  329. package/dist/src/runner/leftover.js +216 -0
  330. package/dist/src/{loop → runner}/model-fallback.js +12 -12
  331. package/dist/src/runner/revision.js +191 -0
  332. package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
  333. package/dist/src/runner/runner-outcome.js +109 -0
  334. package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
  335. package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +39 -18
  336. package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +236 -245
  337. package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
  338. package/dist/src/runner/runner-state.js +103 -0
  339. package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
  340. package/dist/src/scheduling/backoff.js +38 -38
  341. package/dist/src/scheduling/claim-entries.js +49 -0
  342. package/dist/src/scheduling/claims.js +35 -22
  343. package/dist/src/scheduling/once-round.js +22 -22
  344. package/dist/src/scheduling/quiet-hours.js +5 -6
  345. package/dist/src/scheduling/scheduling.js +74 -64
  346. package/dist/src/scheduling/work-merged-cache.js +2 -2
  347. package/dist/src/status/status-budget.js +55 -47
  348. package/dist/src/status/status-data.js +49 -17
  349. package/dist/src/status/status-inbox.js +2 -2
  350. package/dist/src/status/status-install.js +0 -1
  351. package/dist/src/status/status-polls.js +43 -28
  352. package/dist/src/status/status-roles.js +50 -13
  353. package/dist/src/team/error-storm.js +8 -8
  354. package/dist/src/team/failure-spread.js +2 -2
  355. package/dist/src/team/orchestrator-info.js +15 -9
  356. package/dist/src/team/reclaim.js +7 -8
  357. package/dist/src/team/team-hold.js +7 -7
  358. package/dist/src/team/team-polls.js +91 -50
  359. package/dist/src/team/team-state.js +8 -2
  360. package/dist/src/text/datetime.js +65 -18
  361. package/dist/src/text/format.js +48 -11
  362. package/dist/src/text/markdown.js +8 -5
  363. package/dist/src/text/phrases.js +60 -23
  364. package/dist/src/text/suggest.js +1 -1
  365. package/dist/src/text/text.js +78 -13
  366. package/dist/src/ui/badges.js +99 -38
  367. package/dist/src/ui/gui/gui-client-backlog.js +314 -0
  368. package/dist/src/ui/gui/gui-client-boot.js +89 -61
  369. package/dist/src/ui/gui/gui-client-composer.js +8 -9
  370. package/dist/src/ui/gui/gui-client-drawer-config.js +254 -0
  371. package/dist/src/ui/gui/gui-client-drawer.js +155 -289
  372. package/dist/src/ui/gui/gui-client-feed.js +271 -0
  373. package/dist/src/ui/gui/gui-client-history.js +437 -68
  374. package/dist/src/ui/gui/gui-client-labels.js +6 -3
  375. package/dist/src/ui/gui/gui-client-markdown.js +1 -2
  376. package/dist/src/ui/gui/gui-client-model.js +27 -36
  377. package/dist/src/ui/gui/gui-client-operator.js +60 -52
  378. package/dist/src/ui/gui/gui-client-pending.js +405 -24
  379. package/dist/src/ui/gui/gui-client-report-chart.js +492 -0
  380. package/dist/src/ui/gui/gui-client-report.js +105 -430
  381. package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
  382. package/dist/src/ui/gui/gui-client-settings.js +163 -5
  383. package/dist/src/ui/gui/gui-client-team.js +36 -465
  384. package/dist/src/ui/gui/gui-client.js +38 -26
  385. package/dist/src/ui/gui/gui-icons.js +0 -2
  386. package/dist/src/ui/gui/gui-page.js +10 -7
  387. package/dist/src/ui/gui/gui-styles.js +38 -34
  388. package/dist/src/ui/progress-data.js +9 -11
  389. package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +33 -32
  390. package/dist/src/ui/status-model.js +133 -125
  391. package/dist/src/ui/status-payload.js +364 -49
  392. package/dist/src/ui/status-render.js +109 -82
  393. package/dist/src/ui/team-alerts.js +728 -62
  394. package/dist/src/ui/tone.js +12 -6
  395. package/dist/src/ui/transcript-run.js +58 -0
  396. package/dist/src/ui/transcript-tail.js +28 -20
  397. package/dist/src/ui/transcript.js +63 -23
  398. package/dist/src/ui/tui/tui-app.js +0 -1
  399. package/dist/src/ui/tui/tui-backlog.js +53 -14
  400. package/dist/src/ui/tui/tui-editor-answer.js +27 -31
  401. package/dist/src/ui/tui/tui-editor-attach.js +4 -12
  402. package/dist/src/ui/tui/tui-editor-backends.js +127 -0
  403. package/dist/src/ui/tui/tui-editor-brief.js +14 -36
  404. package/dist/src/ui/tui/tui-editor-budget.js +36 -39
  405. package/dist/src/ui/tui/tui-editor-check.js +29 -0
  406. package/dist/src/ui/tui/tui-editor-checks.js +28 -0
  407. package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
  408. package/dist/src/ui/tui/tui-editor-concurrency.js +25 -0
  409. package/dist/src/ui/tui/tui-editor-config.js +99 -0
  410. package/dist/src/ui/tui/tui-editor-entry.js +39 -64
  411. package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
  412. package/dist/src/ui/tui/tui-editor-instances.js +46 -0
  413. package/dist/src/ui/tui/tui-editor-instructions.js +117 -0
  414. package/dist/src/ui/tui/tui-editor-interval.js +29 -16
  415. package/dist/src/ui/tui/tui-editor-model.js +29 -0
  416. package/dist/src/ui/tui/tui-editor-notes.js +9 -29
  417. package/dist/src/ui/tui/tui-editor-notify.js +25 -0
  418. package/dist/src/ui/tui/tui-editor-pause.js +48 -59
  419. package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
  420. package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
  421. package/dist/src/ui/tui/tui-editor-queued.js +5 -9
  422. package/dist/src/ui/tui/tui-editor-quiet.js +95 -41
  423. package/dist/src/ui/tui/tui-editor-retire.js +5 -13
  424. package/dist/src/ui/tui/tui-editor-role-enabled.js +126 -0
  425. package/dist/src/ui/tui/tui-editor-role-key.js +71 -0
  426. package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
  427. package/dist/src/ui/tui/tui-editor-search.js +45 -33
  428. package/dist/src/ui/tui/tui-editor-session.js +101 -34
  429. package/dist/src/ui/tui/tui-editor-slots.js +24 -0
  430. package/dist/src/ui/tui/tui-editors.js +44 -2
  431. package/dist/src/ui/tui/tui-frame.js +68 -11
  432. package/dist/src/ui/tui/tui-history.js +8 -9
  433. package/dist/src/ui/tui/tui-input.js +13 -11
  434. package/dist/src/ui/tui/tui-keymap.js +0 -1
  435. package/dist/src/ui/tui/tui-keys.js +33 -9
  436. package/dist/src/ui/tui/tui-pane-activity.js +35 -19
  437. package/dist/src/ui/tui/tui-pane-backlog.js +198 -126
  438. package/dist/src/ui/tui/tui-pane-brief.js +0 -1
  439. package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
  440. package/dist/src/ui/tui/tui-pane-health.js +168 -8
  441. package/dist/src/ui/tui/tui-pane-history.js +446 -82
  442. package/dist/src/ui/tui/tui-pane-pending.js +539 -15
  443. package/dist/src/ui/tui/tui-pane-queued.js +78 -57
  444. package/dist/src/ui/tui/tui-pane-report.js +54 -16
  445. package/dist/src/ui/tui/tui-pane-session.js +34 -6
  446. package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
  447. package/dist/src/ui/tui/tui-pane.js +91 -28
  448. package/dist/src/ui/tui/tui-panes.js +153 -34
  449. package/dist/src/ui/tui/tui.js +18 -15
  450. package/dist/src/verdict/fix-claim.js +129 -33
  451. package/dist/src/verdict/no-change.js +4 -4
  452. package/dist/src/verdict/refusal.js +40 -16
  453. package/dist/src/verdict/reply-contract.js +77 -31
  454. package/dist/src/version.js +0 -1
  455. package/package.json +2 -1
  456. package/dist/src/loop/leftover.js +0 -130
  457. package/dist/src/loop/revision.js +0 -70
  458. package/dist/src/roles/loop-ids.js +0 -77
  459. package/dist/src/tick/qa-coverage.js +0 -108
  460. package/dist/src/tick/stage-check.js +0 -419
  461. package/dist/src/tick/telemetry-digest.js +0 -25
  462. package/dist/src/tick/tick-detail-data.js +0 -108
  463. package/dist/src/tick/tick-detail.js +0 -163
  464. package/dist/src/ui/gui/gui-client-sound.js +0 -53
@@ -1,7 +1,6 @@
1
- // vocabulary: plain
2
1
  import fs from "node:fs";
3
2
  import { allRoleIds } from "../roles/roles.js";
4
- import { loopIds } from "../roles/loop-ids.js";
3
+ import { runnerIds } from "../roles/runner-ids.js";
5
4
  import { cachedByStat } from "../files/stat-cache.js";
6
5
  import { CONFIG_BASENAME, configPath } from "../paths.js";
7
6
  import { errorMessage } from "../text/text.js";
@@ -31,7 +30,7 @@ export function parseJsonConfig(file, basename) {
31
30
  return { raw, renamed };
32
31
  }
33
32
  /** Build the default TumwaterConfig: every role enabled except the opt-in selfcheck (steward on its
34
- * slow ~6 h tick, qa and selfcheck on ~2 h, readme on 30 min, plan and organize on 1 h), with
33
+ * slow ~6 h run, qa and selfcheck on ~2 h, readme on 30 min, plan and organize on 1 h), with
35
34
  * defaults for concurrency, timeouts, log size, retention, thrash detection, idle backoff,
36
35
  * self-redeploy, and review settings. The return type is the resolved view (config-views.ts): a
37
36
  * freshly defaulted config has no model map to resolve — only a loaded file can carry one — so
@@ -47,18 +46,26 @@ export function defaultConfig() {
47
46
  roles.steward = { enabled: true, minRunIntervalSeconds: 21600 };
48
47
  // QA exercises the product like a user on a ~2 h clock: user flows change slower than code.
49
48
  roles.qa = { enabled: true, minRunIntervalSeconds: 7200 };
50
- // Selfcheck reads the event log on the same slow clock: the digest is a windowed view, so a
51
- // tick a couple of hours apart reads a new window rather than re-filing the same cluster.
52
- // It is the one opt-in role: its evidence is this harness's own event log and its charter
53
- // files only harness misbehavior, so on any other project every finding is a tumwater bug
54
- // filed into the wrong backlog, which that project's bugfix loop rightly rejects. Tumwater's
55
- // own tumwater.example.json enables it, so this repo's checkouts still get it at `init`.
49
+ // CI verifies main's parity with the workflow on the same ~2 h clock: a full suite plus e2e
50
+ // is minutes of work, so it must be at most an hourly-ish cost, and the parity block makes
51
+ // the no-op a single model run when main has not moved (part 3/3).
52
+ roles.ci = { enabled: true, minRunIntervalSeconds: 7200 };
53
+ // Selfcheck and feedback are the two opt-in roles, both on the same ~2 h clock: the digest is
54
+ // a windowed view, so a run a couple of hours apart reads a new window rather than re-filing
55
+ // the same cluster. Selfcheck's evidence is this harness's own event log and its charter files
56
+ // only harness misbehavior, so on any other project every finding is a tumwater bug filed into
57
+ // the wrong backlog, which that project's bugfix role rightly rejects — that is why it is
58
+ // opt-in. Feedback files the same digest's findings as GitHub issues on znation/tumwater, so
59
+ // nothing leaves the machine until the operator opts in (plans/feedback-role.md). Tumwater's
60
+ // own tumwater.example.json keeps selfcheck (dogfood) and not feedback, so this repo's
61
+ // checkouts still get their findings at `init` and file nothing upstream.
56
62
  roles.selfcheck = { enabled: false, minRunIntervalSeconds: 7200 };
63
+ roles.feedback = { enabled: false, minRunIntervalSeconds: 7200 };
57
64
  // The bookkeeping roles run on slower clocks too. In dogfood readme (95 commits, 82 of them
58
65
  // status syncs) and plan (80, 44 of them refine/re-audit notes) were 30% of every commit and
59
- // 20% of every tick: readme woke on every merge to restamp one line, and plan re-audited plans
66
+ // 20% of every run: readme woke on every merge to restamp one line, and plan re-audited plans
60
67
  // nobody had touched. A 30 min readme clock batches a burst of merges into one sync; a 1 h
61
- // plan clock leaves the slots to the loops that ship. Both still wake early when main moves —
68
+ // plan clock leaves the slots to the roles that ship. Both still wake early when main moves —
62
69
  // the clock only bounds how often.
63
70
  roles.readme = { enabled: true, minRunIntervalSeconds: 1800 };
64
71
  roles.plan = { enabled: true, minRunIntervalSeconds: 3600 };
@@ -74,7 +81,7 @@ export function defaultConfig() {
74
81
  piArgs: [],
75
82
  // Agent backends (plans/agent-backends.md, part 4a/7): empty until a file names one.
76
83
  // Keeping the map always present means read sites need no `?? {}`; the legacy
77
- // agentBin/piArgs are read as pi's entry through resolvedAgentConfig (config-views.ts).
84
+ // agentBin/piArgs are read as pi's entry through resolvedBackendConfig (config-views.ts).
78
85
  backends: {},
79
86
  maxConcurrent: 6,
80
87
  // Three is the team's realistic concurrent-role count: a busy queue coalesces the
@@ -84,7 +91,7 @@ export function defaultConfig() {
84
91
  // without a burst stacking every check on the host beside the authors' own test runs.
85
92
  maxConcurrentChecks: 2,
86
93
  minRunIntervalSeconds: 20,
87
- // Four hours: the largest tick a hosted model legitimately took and still merged work
94
+ // Four hours: the largest run a hosted model legitimately took and still merged work
88
95
  // measured 185 minutes (BUGS.md 2026-09-29), so 30 minutes discarded real work and burned
89
96
  // every resume before the task could finish. A run that stops making progress is still
90
97
  // bounded earlier by the quiet watchdog, not by this cap.
@@ -103,12 +110,12 @@ export function defaultConfig() {
103
110
  maxDailyCostUsd: 50,
104
111
  // Work ratio 1a/4 and 1b/4 (plans/work-ratio.md): the maintenance allowance is
105
112
  // maintenancePerWorkMerge × work merges + 12 per rolling 24 h, and the scheduler holds
106
- // maintenance loops once the window reaches it. Two keeps roughly two upkeep merges per
113
+ // maintenance roles once the window reaches it. Two keeps roughly two upkeep merges per
107
114
  // shipped change while the fixed floor lets hygiene run on an empty backlog.
108
115
  maintenancePerWorkMerge: 2,
109
116
  // Disk floor (plans/disk-floor.md, part 1/4): when the volume the worktrees live on drops
110
117
  // below this many GB free, the team holds new work until it climbs 5 GB back above it.
111
- // 10 GB leaves room for an in-flight tick to finish and a state write to land. 0 disables.
118
+ // 10 GB leaves room for an in-flight run to finish and a state write to land. 0 disables.
112
119
  diskHoldGB: 10,
113
120
  // Pressure reclaim (plans/disk-floor.md, part 2/4): below this many GB free, a background
114
121
  // pass deletes gitignored build outputs from idle worktrees before the 10 GB hold engages.
@@ -119,9 +126,9 @@ export function defaultConfig() {
119
126
  // whatever the free space. A day catches paused, retired and rarely due roles without
120
127
  // sweeping a worktree that merely slept through the night. 0 disables idle reclaim.
121
128
  worktreeIdleReclaimHours: 24,
122
- // Friction is flagged only when a changed tick burns BOTH thresholds
123
- // (src/tick/tick-stage.ts): the absolute turn count alone measures model speed, so a fast
124
- // model's ordinary 40+ turn / few-minute tick stays unflagged, while a genuinely hard tick
129
+ // Friction is flagged only when a changed run burns BOTH thresholds
130
+ // (src/run/run-stage.ts): the absolute turn count alone measures model speed, so a fast
131
+ // model's ordinary 40+ turn / few-minute run stays unflagged, while a genuinely hard run
125
132
  // that burned 40+ turns over half an hour or more still is (BUGS.md 2026-09-19). Tuned for
126
133
  // the ~27B local model at 24-35 tok/s, where 40 turns is half an hour of work.
127
134
  thrashTurns: 40,
@@ -134,9 +141,17 @@ export function defaultConfig() {
134
141
  // writes a request file the harness applies to the live config before any commit, so no
135
142
  // diff can ever touch tumwater.json and the exemption — once the only thing keeping an
136
143
  // explicit user instruction from being discarded by a rejected review — has nothing left
137
- // to exempt. Removing it also closes the path where a director tick could merge a mixed
144
+ // to exempt. Removing it also closes the path where a director run could merge a mixed
138
145
  // doc-plus-config diff unreviewed.
139
- review: { enabled: true, exemptPaths: ["*.md", "docs/**"] },
146
+ // Reviewer as a regular role (part 1/4): the high-volume, low-blast-radius roles — bugfix,
147
+ // readme, organize, plus clean, dedupe, coverage — are reviewed AFTER they merge, on their
148
+ // merged commit (src/review/post-merge-review.ts), instead of holding the merge queue for a
149
+ // reviewer run on their (usually trivial) diff; a rejection keeps the commit on main
150
+ // with the reviewer's reasons. feature, director, perf, improve and custom roles stay
151
+ // pre-merge. A
152
+ // config that omits `review.afterMerge` inherits this, and `"afterMerge": []` opts out (back
153
+ // to reviewing before merging).
154
+ review: { enabled: true, exemptPaths: ["*.md", "docs/**"], afterMerge: ["bugfix", "clean", "coverage", "dedupe", "organize", "readme"] },
140
155
  customRoles: [],
141
156
  roles,
142
157
  };
@@ -161,7 +176,7 @@ export function overlayDefaults(base, cfg) {
161
176
  review: { ...base.review, ...(cfg.review ?? {}) },
162
177
  piArgs: cfg.piArgs ?? base.piArgs,
163
178
  // Clone each backend entry (and its args array) so the result owns its data like every
164
- // other section. The legacy agentBin/piArgs stay beside it; resolvedAgentConfig picks
179
+ // other section. The legacy agentBin/piArgs stay beside it; resolvedBackendConfig picks
165
180
  // between them.
166
181
  backends: Object.fromEntries(Object.entries(cfg.backends ?? base.backends ?? {}).map(([id, a]) => [
167
182
  id,
@@ -172,16 +187,16 @@ export function overlayDefaults(base, cfg) {
172
187
  customRoles: (cfg.customRoles ?? []).map((c) => ({ ...c })),
173
188
  roles: { ...base.roles },
174
189
  };
175
- // Seed each user-defined loop into roles BEFORE the overlay loop so a file's `roles` section
190
+ // Seed each user-defined role into roles BEFORE the overlay loop so a file's `roles` section
176
191
  // overrides per-key for a custom exactly like for built-ins (enabled, instructions,
177
- // tickInterval, provider/model); seeding after would silently ignore those overrides.
192
+ // runInterval, provider/model); seeding after would silently ignore those overrides.
178
193
  // A custom absent from `roles` stays enabled. Insertion order = built-ins then customs in
179
194
  // array order — the display and startup tie-break order.
180
195
  for (const c of merged.customRoles) {
181
196
  // Own-property test, not a bare truthiness read: the custom name `constructor` passes the
182
197
  // lowercase charset check and collides with an Object.prototype key, whose inherited value
183
- // is truthy — a bare `!merged.roles[c.name]` would skip the seed and the loop would never
184
- // become an own key, so loopIds silently dropped it.
198
+ // is truthy — a bare `!merged.roles[c.name]` would skip the seed and the role would never
199
+ // become an own key, so runnerIds silently dropped it.
185
200
  if (!Object.hasOwn(merged.roles, c.name))
186
201
  merged.roles[c.name] = { enabled: true };
187
202
  }
@@ -295,7 +310,7 @@ export function liveConfig(root) {
295
310
  * dropped from the serialized candidate. A merged config always carries the default `piArgs`,
296
311
  * so without this a whole-config write would put both spellings on disk and the next load's
297
312
  * raw pass would reject the file; a config that uses only the legacy spellings is unchanged. */
298
- export function collapseAgentAliases(config) {
313
+ export function collapseBackendAliases(config) {
299
314
  const pi = config.backends?.pi;
300
315
  if (pi === undefined)
301
316
  return config;
@@ -312,11 +327,11 @@ export function collapseAgentAliases(config) {
312
327
  return next;
313
328
  }
314
329
  /** Persist a config after validating it, so an invalid tumwater.json can never be written.
315
- * Written atomically (writeJsonAtomic, trailing newline — the POSIX-newline convention here
316
- * and at config-write.ts, the file's other writers): the config-live poller re-reads the file
330
+ * Written atomically (writeJsonAtomic, trailing newline — the POSIX-newline convention shared
331
+ * with config-write.ts and config-request.ts): the config-live poller re-reads the file
317
332
  * every ~2 s, so a plain overwrite could hand it a torn read mid-write. */
318
333
  export function saveConfig(root, config) {
319
- const collapsed = collapseAgentAliases(config);
334
+ const collapsed = collapseBackendAliases(config);
320
335
  validateConfig(collapsed);
321
336
  writeJsonAtomic(configPath(root), collapsed, true);
322
337
  }
@@ -359,36 +374,36 @@ export function changedConfigKeys(prev, next) {
359
374
  }
360
375
  return keys.sort();
361
376
  }
362
- /** The names of the user-defined loops, in array order (plans/user-defined-loops.md).
377
+ /** The names of the user-defined roles, in array order (plans/user-defined-loops.md).
363
378
  * One source of truth for "which ids are user-defined" so consumers cannot drift. */
364
- export function customLoopNames(config) {
379
+ export function customRoleNames(config) {
365
380
  return config.customRoles.map((c) => c.name);
366
381
  }
367
- /** True when `id` is a user-defined loop rather than a catalog role. */
382
+ /** True when `id` is a user-defined role rather than a catalog role. */
368
383
  export function isCustomRole(config, id) {
369
384
  return config.customRoles.some((c) => c.name === id);
370
385
  }
371
- /** Every valid role id — the catalog plus the user-defined loops (the one answer for "which
386
+ /** Every valid role id — the catalog plus the user-defined roles (the one answer for "which
372
387
  * ids exist", so consumers cannot drift from each other). */
373
388
  export function knownRoleIds(config) {
374
- const ids = [...allRoleIds(), ...customLoopNames(config)];
389
+ const ids = [...allRoleIds(), ...customRoleNames(config)];
375
390
  const seen = new Set(ids);
376
- // Parallel work instances (part 5b/7): an enabled role's instance loop ids (`feature-2`) are
391
+ // Parallel work instances (part 5b/7): an enabled role's instance role ids (`feature-2`) are
377
392
  // valid CLI and GUI targets even though they are not catalog roles. The bare ids are already
378
393
  // present, so this only appends the extras; deduping keeps the catalog+customs order intact.
379
- for (const id of loopIds(config))
394
+ for (const id of runnerIds(config))
380
395
  if (!seen.has(id)) {
381
396
  ids.push(id);
382
397
  seen.add(id);
383
398
  }
384
399
  return ids;
385
400
  }
386
- /** Every valid role id a READ-ONLY surface accepts: the catalog plus the user-defined loops,
401
+ /** Every valid role id a READ-ONLY surface accepts: the catalog plus the user-defined roles,
387
402
  * read through loadConfigCached (which never throws), so a transiently broken tumwater.json
388
403
  * falls back to the built-in catalog instead of taking the view down or refusing every id.
389
404
  * The one home of that fallback rule — every read-only role-id consumer resolves through it:
390
405
  * parseRoleScope (cmdLogs/cmdHistory), validRoleIds (the GUI endpoints), cmdDiff's role flag,
391
- * cmdTick's positional, cmdPrompt's --list and list-wide --cancel/--edit scope, and
406
+ * cmdRunDetail's positional, cmdPrompt's --list and list-wide --cancel/--edit scope, and
392
407
  * collectTeamChanges' roster — so they cannot drift. (Read-only deliberately: state-changing
393
408
  * commands resolve ids through loadConfig and fail loudly, because the operator is owed the
394
409
  * config error before a write.) */
@@ -1,10 +1,9 @@
1
- // vocabulary: plain
2
1
  /** Parsing model selector strings — `provider/id[:thinking]`, the form pi's own `--model`
3
2
  * accepts (plans/model-tiers.md "Selectors"). Tumwater parses them itself because pricing
4
3
  * (src/pi/pi-models.ts) needs the provider and id apart, while the argv builder
5
4
  * (src/pi/pi-args.ts) still passes `--provider` / `--model` / `--thinking` separately. Pure
6
5
  * string algebra — no I/O, no config knowledge beyond THINKING_LEVELS. */
7
- import { AGENT_IDS, THINKING_LEVELS } from "./config-schema.js";
6
+ import { BACKEND_IDS, THINKING_LEVELS } from "./config-schema.js";
8
7
  /** Parse one selector string. Rules:
9
8
  * 1. A trailing `:x` is a thinking level only when `x` is in THINKING_LEVELS, so
10
9
  * `…:together` (a provider pin) survives on the id while `…:together:low` strips to
@@ -36,27 +35,25 @@ export function parseModelSelector(s, legacyProvider) {
36
35
  })();
37
36
  return thinking ? { ...base, thinking } : base;
38
37
  }
39
- /** True when `s` names a registered agent backend — the reserved selector provider namespace
40
- * (`claude/<id>` always runs on claude). Validation restricts the config's `agent` key to the
41
- * same ids; a `provider/id` selector's provider is checked here. */
42
- export function isAgentId(s) {
43
- return s !== undefined && AGENT_IDS.includes(s);
38
+ /** True when `s` names a registered agent backend. Validation restricts the config's `backend`
39
+ * key to these ids; a model id never names one. */
40
+ export function isBackendId(s) {
41
+ return s !== undefined && BACKEND_IDS.includes(s);
44
42
  }
45
43
  /** Resolve a seam's backend identity: its own `agent` when it carries one (configForRole,
46
- * reviewConfig and resolverConfig all do), else `provider` when it names an agent backend (a
47
- * `claude/<id>` selector runs on claude), else pi — the one backend with providers, the
48
- * default a selector routes to when neither an agent nor a reserved provider prefix names one.
44
+ * reviewConfig, resolverConfig and a fallback pair's `backend` all do), else pi — the one
45
+ * backend with providers.
49
46
  * The single home of the rule: the budget gate's claude check, pi's free-ness and
50
47
  * context-window lookups, and the image-input seam all read a seam's backend through it, so
51
48
  * no two sites can disagree on which backend a selector runs. */
52
49
  export function seamAgent(seam) {
53
- return seam.agent ?? (isAgentId(seam.provider) ? seam.provider : "pi");
50
+ return seam.agent ?? "pi";
54
51
  }
55
52
  /** Each agent backend's per-tier default model, applied only when the config resolves no model
56
53
  * id for a seam (plans/agent-backends.md "Config" decision 5). pi has none — its own default
57
54
  * applies. It lives here, in the pure config layer, because `routeModel` runs at config-
58
55
  * resolution time and must not import the runtime backend registry. */
59
- const AGENT_DEFAULT_MODELS = {
56
+ const BACKEND_DEFAULT_MODELS = {
60
57
  pi: {},
61
58
  claude: {
62
59
  small: "claude-haiku-5-5",
@@ -67,30 +64,33 @@ const AGENT_DEFAULT_MODELS = {
67
64
  // applies, exactly as pi's does. The free model is a time-limited promotion, so its id
68
65
  // belongs in tumwater.json, not in code.
69
66
  opencode: {},
67
+ // codex's tier defaults (plans/codex-backend.md decision 4), from the catalog's own
68
+ // descriptions (design finding 14): strong is `gpt-6-astra`, default is `gpt-6.1-sol` (Codex's
69
+ // own default), small is `gpt-6-luna`. The director runs the strong tier, as on claude.
70
+ codex: {
71
+ small: "gpt-6-luna",
72
+ default: "gpt-6.1-sol",
73
+ strong: "gpt-6-astra",
74
+ },
70
75
  };
71
76
  /** The backend and concrete model a resolved seam's provider/model pair routes to
72
- * (plans/agent-backends.md "Config"). The provider segment decides the backend: a provider
73
- * naming a registered agent id runs that agent (and its provider field is cleared, since it is
74
- * a namespace, not a pi provider); any other provider runs on pi, the one backend with
75
- * providers; no provider at all runs on the config's `agent`. When no model id survives the
76
- * resolution, the backend's tier default fills in (pi supplies none).
77
- *
78
- * Rule 3a (plans/provider-concurrency.md "Routing a bare Claude model id to claude"): a bare id
79
- * (no provider) that starts with `claude-` runs on claude, whatever `configAgent` says — the one
80
- * prefix where the model id decides the backend. A named provider still wins
81
- * (`anthropic/claude-…` stays pi); bare aliases (`sonnet`, `opus`) keep today's routing. */
82
- export function routeModel(provider, model, configAgent, tier) {
83
- const agent = provider === undefined
84
- ? model?.startsWith("claude-")
85
- ? "claude"
86
- : configAgent
87
- : isAgentId(provider)
88
- ? provider
89
- : "pi";
90
- if (model !== undefined)
91
- return { agent, model };
92
- const def = AGENT_DEFAULT_MODELS[agent]?.[tier];
93
- return def !== undefined ? { agent, model: def } : { agent };
77
+ * (plans/agent-backends.md "Config"). The backend is the `explicitBackend` (a tier entry's,
78
+ * role's or review's `backend` key), else the config's `backend`, else pi — a model id never
79
+ * names one. pi keeps the parsed provider (the caller's); claude and opencode take the whole
80
+ * `provider/model` id with no provider, verbatim; codex takes the bare model as written (there
81
+ * is no `codex/<id>` spelling — a model id never names a backend, and a whole `codex/<id>`
82
+ * parses to provider `codex` which codex, like pi, drops, leaving the model half). When no
83
+ * model id survives the resolution, the
84
+ * backend's tier default fills in (pi and opencode supply none). */
85
+ export function routeModel(provider, model, configBackend, tier, explicitBackend) {
86
+ const backend = explicitBackend ?? configBackend;
87
+ const whole = model !== undefined && provider !== undefined && backend !== "pi" && backend !== "codex"
88
+ ? `${provider}/${model}`
89
+ : model;
90
+ if (whole !== undefined)
91
+ return { agent: backend, model: whole };
92
+ const def = BACKEND_DEFAULT_MODELS[backend]?.[tier];
93
+ return def !== undefined ? { agent: backend, model: def } : { agent: backend };
94
94
  }
95
95
  /** The inverse of parseModelSelector (in its no-legacyProvider form): the selector string a
96
96
  * triple round-trips through, for messages and future writers. */