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,4 +1,3 @@
1
- // vocabulary: plain
2
1
  import { contextBudgetRule } from "./prompt.js";
3
2
  import { dialectFor } from "./dialect.js";
4
3
  import { NOTHING_TO_DO, REPLY_ENDINGS, SUMMARY_BLOCK } from "../verdict/reply-contract.js";
@@ -8,7 +7,7 @@ import { NOTHING_TO_DO, REPLY_ENDINGS, SUMMARY_BLOCK } from "../verdict/reply-co
8
7
  * the cut-off and timeout bridges deliberately phrase their own finish instructions instead. */
9
8
  const CONTINUE_TASK_BRIDGE = `Continue the SAME task you were working on and finish it. If the work so far turns out to be
10
9
  unusable, redo it — but stay on this task rather than picking a new one.`;
11
- /** The follow-up prompt for resuming an interrupted tick. It is sent into the SAME pi session as
10
+ /** The follow-up prompt for resuming an interrupted run. It is sent into the SAME pi session as
12
11
  * the interrupted run — which already carries the full original prompt, all rules, and the work
13
12
  * so far — so it only needs to bridge the gap. The bridge names the real cause: a run cut off at
14
13
  * the context ceiling needs to finish with the smallest change and read almost nothing more, not
@@ -63,11 +62,13 @@ ${contextBudgetRule(dialectFor(agent))}
63
62
  ${REPLY_ENDINGS}`;
64
63
  }
65
64
  /** The bridge for a `handoff` resume (plans/agent-backends.md "Sessions"): the newest session
66
- * in the worktree's session dir belongs to another agent, so this tick runs a FRESH session —
67
- * the full assembled tick prompt, plus the one fact a fresh session cannot know: an interrupted
65
+ * in the worktree's session dir belongs to another agent, so this run starts a FRESH session —
66
+ * the full assembled run prompt, plus the one fact a fresh session cannot know: an interrupted
68
67
  * run's uncommitted edits are sitting in the worktree. The worktree is NOT reset for a handoff,
69
- * so the bridge must send the new run to `git diff` before it starts its own work. */
70
- export function buildHandoffPrompt(roleId, tickPrompt, cause = "handoff",
68
+ * so the bridge must send the new run to `git diff` before it starts its own work. A `salvaged`
69
+ * handoff's edits were restored from a patch of a run the harness lost track of
70
+ * (src/git/slot-salvage.ts) rather than kept in place. */
71
+ export function buildHandoffPrompt(roleId, runPrompt, cause = "handoff",
71
72
  /** Named facts an image-reroute handoff note needs that the cause alone cannot carry: the
72
73
  * model the blind run was on and the image file it read. */
73
74
  imageReroute) {
@@ -80,12 +81,16 @@ discard them. This run starts on the model job the daily budget left you.`
80
81
  when it read ${imageReroute?.path ?? "an image"}, because that model cannot read images; its
81
82
  uncommitted edits are in your worktree — review \`git diff\`, then finish or discard them. This
82
83
  run is on a model that CAN read images, so read the image again.`
83
- : `An earlier run of the "${roleId}" role on another model was interrupted; its
84
+ : cause === "salvaged"
85
+ ? `An earlier run of the "${roleId}" role was cut short before tumwater could save its work;
86
+ its uncommitted edits were recovered and are in your worktree — review \`git status\` and
87
+ \`git diff\` (new files show as untracked), then finish or discard them.`
88
+ : `An earlier run of the "${roleId}" role on another model was interrupted; its
84
89
  uncommitted edits are in your worktree — review \`git diff\`, then finish or discard them.`;
85
- return `${tickPrompt}\n\n${note}`;
90
+ return `${runPrompt}\n\n${note}`;
86
91
  }
87
- /** The one-turn follow-up sent into a tick's OWN session (--continue) when the run changed files
88
- * but its reply carried no SUMMARY line — 73 of the first 670 commits merged as "<role> tick N"
92
+ /** The one-turn follow-up sent into a run's OWN session (--continue) when the run changed files
93
+ * but its reply carried no SUMMARY line — 73 of the first 670 commits merged as "<role> run N"
89
94
  * because of that, most of them the largest diffs in the repo. The session still holds everything
90
95
  * the run did, so one short reply recovers the subject and body the commit deserves; the caller
91
96
  * bounds the run tightly and falls back to a diff-derived subject if this too yields nothing. */
@@ -101,10 +106,10 @@ did not include the required closing block, so tumwater cannot describe the comm
101
106
  to make. Reply now with ONLY that block — no tool calls, no other text, one line each:
102
107
  ${SUMMARY_BLOCK}`;
103
108
  }
104
- /** The note injected into a role's next FRESH tick prompt after its previous run(s) were cut
105
- * off at the context ceiling without merging anything (the loop gave up resuming — see
106
- * tick-apply.ts's CUT_OFF_RESUME_LIMIT — or a cut-off director prompt is re-running). The only
107
- * cross-tick memory that the last attempt was too big for the window. */
109
+ /** The note injected into a role's next FRESH run prompt after its previous run(s) were cut
110
+ * off at the context ceiling without merging anything (the role gave up resuming — see
111
+ * run-apply.ts's CUT_OFF_RESUME_LIMIT — or a cut-off director prompt is re-running). The only
112
+ * cross-run memory that the last attempt was too big for the window. */
108
113
  export function buildCutOffNote(streak) {
109
114
  const runs = streak === 1 ? "run" : `${streak} runs`;
110
115
  return `Your previous ${runs} as this role ran out of context before merging anything. Pick a
@@ -1,47 +1,53 @@
1
- // vocabulary: plain
2
1
  import path from "node:path";
3
2
  import { describeCheck } from "../build/build-check-report.js";
4
- import { DECOMPOSITION_GUIDANCE, NEEDS_REPLAN_NOTE, NEEDS_REVIEW_NOTE, PLAN_SIZING } from "../roles/role-guidance.js";
5
- import { CLAIMS_RULE, CLAIMS_RULE_COMPACT, REPLY_ENDINGS } from "../verdict/reply-contract.js";
3
+ import { DECOMPOSITION_GUIDANCE, NEEDS_REPLAN_NOTE, NEEDS_REVIEW_NOTE, PLAN_QUEUE_GUIDANCE, PLAN_SIZING } from "../roles/role-guidance.js";
4
+ import { CLAIMS_RULE, REPLY_ENDINGS } from "../verdict/reply-contract.js";
6
5
  import { todayStamp } from "../budget/budget.js";
7
6
  import { daysBetween } from "../text/datetime.js";
8
7
  import { plural } from "../text/phrases.js";
9
8
  import { worktreePath } from "../paths.js";
10
9
  import { PI_DIALECT, dialectFor } from "./dialect.js";
11
- /** The context-budget rule every run carries. Under the old 87k window half of all ticks ended
10
+ /** Prompt construction for the roles' pi runs (run and director). The reply contract those
11
+ * runs must follow — the TUMWATER_NOTHING_TO_DO sentinel, the TUMWATER_REFUSED line, the
12
+ * SUMMARY/WHY/RISK/VERIFIED block format, the three endings, and the claims rules — lives in
13
+ * reply-contract.ts; this module weaves it into each prompt. The follow-up prompts that pick a
14
+ * session back up — the resume bridge, the missing-summary recovery, and the fresh-run cut-off
15
+ * note — live in prompt-followup.ts. The merge gate's pi runs (conflict resolution, build fix,
16
+ * review) build their prompts in gate-prompts.ts; assembling a reply into the run's commit message
17
+ * lives in git/commit-message.ts.
18
+ *
19
+ * The prose is tuned for the team's models (2026-10-01): the primary GLM-5.3-Flash (an 18B-active
20
+ * MoE behind a ~1M window) and the budget fallback's local Qwen3.8-27B (~127k window). Both follow
21
+ * short grouped rules with concrete numbers and literal commands far better than long dense
22
+ * paragraphs, over-read when told merely to "work economically", make unchecked claims the reviewer
23
+ * then rejects (the primary's leading rejection cause), and need to be told how a reply must end
24
+ * — so the rules are grouped with one rule per bullet, budgets are numeric, role tasks are
25
+ * numbered steps, claims carry the check that proves them, the three possible endings are a
26
+ * mutually exclusive list, and that closing contract comes last, where a model attends to it most.
27
+ * The prompt is compact: its first request is re-sent on every turn, so the rules keep their
28
+ * directives without the reasons, the principles block keeps only its bullets, the brief file is
29
+ * read rather than inlined, and an assigned run goes without the material its assignment makes
30
+ * redundant. It was written for models with a window of at most 128k (2026-10-17) and became
31
+ * every model's prompt on 2026-10-18 to cut the tokens each turn re-sends; the longer full
32
+ * variant was deleted after a day of the team's runs on it (2026-10-19). The PRINCIPLES.md reader
33
+ * the principles block is fed from lives in principles.ts (readPrinciples, with its injection
34
+ * cap). */
35
+ /** The context-budget rule every run carries. Under the old 87k window half of all runs ended
12
36
  * at the ceiling merging nothing (308 of 733 in the autonomous fortnight; 216 of 245 no-change
13
- * ticks in the first week of September), almost all of it whole-file reads: the model, told only
37
+ * runs in the first week of September), almost all of it whole-file reads: the model, told only
14
38
  * to "work economically", read the codebase file by file (277 whole-file reads against 6 ranged
15
39
  * ones in the bugfix/improve/clean logs). Every token read is prefill and dilutes the model's
16
40
  * attention, so the rule states the cost in numbers and the one habit that prevents it: check
17
- * size, then read in ranges. One rule per bullet (2026-10-01), so a small model can follow each
18
- * on its own. (Reasoning length is not addressed here: a "think briefly" bullet measured no drop in
19
- * the fallback model's thinking — the oMLX thinking-budget cap is the lever.) The bundled
20
- * context-budget extension
21
- * (src/pi-extension/context-budget.ts) complements this rule with the live fill level. Stated
22
- * once so the tick rules and prompt-followup.ts's resume bridge cannot drift. Must not contain
23
- * the phrase "ran out of context" — the loop tests use it to tell a fresh tick from a cut-off
24
- * resume. */
41
+ * size, then read in ranges. The range-read threshold is ~150 lines, what a 64k window's read cap
42
+ * holds (bounded-output.ts outputLimits), and the first bullet names the cost rather than the
43
+ * window's size, since every model gets it. One rule per bullet (2026-10-01), so a small model can
44
+ * follow each on its own. (Reasoning length is not addressed here: a "think briefly" bullet
45
+ * measured no drop in the fallback model's thinking — the oMLX thinking-budget cap is the lever.)
46
+ * The bundled context-budget extension (src/pi-extension/context-budget.ts) complements this rule
47
+ * with the live fill level. Stated once so the run rules and prompt-followup.ts's resume bridge
48
+ * cannot drift. Must not contain the phrase "ran out of context" — the loop tests use it to tell
49
+ * a fresh run from a cut-off resume. */
25
50
  export function contextBudgetRule(dialect = PI_DIALECT) {
26
- return `- Your context window is finite: everything you read stays in it until the run ends, and a
27
- run that fills the window lands nothing. A whole-file read of a 500-line module costs ~5k tokens.
28
- - Check size before reading (\`wc -l\`) and read files over ~300 lines in ranges: find the lines
29
- with \`grep -n\`, then read them with ${dialect.readRange}.
30
- - Cap command output with \`head\`/\`tail\`. Never dump a file, a log, or a test run wholesale.
31
- - Do not re-read what you already saw — re-read only a region you edited.
32
- - Each turn re-sends everything read so far, so issue lookups that do not depend on each other
33
- (a \`wc -l\` over several files, the ranges a \`grep -n\` found, a typecheck and a targeted
34
- test) as separate tool calls in the same turn. Keep anything that depends on a prior result
35
- sequential — do not batch an edit with the test that checks it.
36
- - ${dialect.oversizedOutput}`;
37
- }
38
- /** The pi-dialect reading-budget rule, byte-identical to the rule before the dialect split, for
39
- * the callers and tests that want the default text. */
40
- export const CONTEXT_BUDGET_RULE = contextBudgetRule(PI_DIALECT);
41
- /** contextBudgetRule for the compact prompt: the same habits, with the range-read threshold at
42
- * ~150 lines, which is what a 64k window's read cap holds (bounded-output.ts outputLimits). Its
43
- * first bullet names the cost rather than the window's size, since every model gets it. */
44
- export function compactContextBudgetRule(dialect = PI_DIALECT) {
45
51
  return `- Everything you read stays in your context until the run ends and is re-sent on every turn;
46
52
  a run that fills the window merges nothing. A whole read of a 500-line file costs ~5k tokens.
47
53
  - Check size first (\`wc -l\`) and read files over ~150 lines in ranges: find the lines with
@@ -52,7 +58,7 @@ export function compactContextBudgetRule(dialect = PI_DIALECT) {
52
58
  dependent steps sequential.
53
59
  - ${dialect.oversizedOutput}`;
54
60
  }
55
- /** The process-kill rule every run that may run commands carries, word for word: the tick and
61
+ /** The process-kill rule every run that may run commands carries, word for word: the run and
56
62
  * director rules (full and compact, each adding its own reason), the conflict resolver, and the
57
63
  * reviewer. The team's runs and tumwater's own checks share one host, so a kill by pattern
58
64
  * reaches them all. On 2026-10-18 a feature run's `pkill -f "dist/test/test-runner.js"; pkill -f
@@ -62,7 +68,7 @@ export function compactContextBudgetRule(dialect = PI_DIALECT) {
62
68
  export const KILL_BY_PID_RULE = `Never kill processes by name or pattern (\`pkill\`, \`killall\`,
63
69
  \`pgrep … | xargs kill\`): stop only a process you started, by its PID (\`kill $!\` right after
64
70
  backgrounding it).`;
65
- /** The test-runner rule every run that may run tests carries: the tick and director rules, the
71
+ /** The test-runner rule every run that may run tests carries: the run and director rules, the
66
72
  * conflict resolver, and the reviewer. Project-neutral on purpose — a team works on any codebase
67
73
  * (npm, cargo, pytest, a configured check.command), so the rule names no ecosystem's runner as THE
68
74
  * right one: it points at the declared check or the framework already in use, and its one example
@@ -78,14 +84,13 @@ export const TEST_RUNNER_RULE = `- Run tests only through the project's declared
78
84
  runner finds no tests at best, and can kill them mid-run, leaking processes and temp files.`;
79
85
  /** Render a dialect's `backendRules` as one bullet each, or nothing when the dialect has none
80
86
  * (pi and claude) — so a backend's extra shell/reasoning guidance rides the reading-budget
81
- * section right after the oversized-output rule and is present in both the full and compact
82
- * prompts. */
87
+ * section right after the oversized-output rule. */
83
88
  function backendRuleLines(rules) {
84
89
  if (!rules || rules.length === 0)
85
90
  return "";
86
91
  return "\n" + rules.map((rule) => `- ${rule}`).join("\n");
87
92
  }
88
- /** The date line every pi prompt carries — tick and director (via sharedPreamble) and the gate's
93
+ /** The date line every pi prompt carries — run and director (via sharedPreamble) and the gate's
89
94
  * conflict and review runs — naming the local calendar day as YYYY-MM-DD. No prompt
90
95
  * used to say what day it is, so a run that had to write one (a BUGS.md heading's "(found by …
91
96
  * YYYY-MM-DD)", a Fixed or Refused date, NEEDS_REVIEW_NOTE's and NEEDS_REPLAN_NOTE's
@@ -98,7 +103,7 @@ function backendRuleLines(rules) {
98
103
  export function dateLine(today = todayStamp()) {
99
104
  return `Today's date is ${today} (local time).`;
100
105
  }
101
- /** The repo root as a loop's worktree reaches it (`../../..`, out of .tumwater/worktrees/<role>),
106
+ /** The repo root as a role's worktree reaches it (`../../..`, out of .tumwater/worktrees/<role>),
102
107
  * derived from worktreePath so the prompt cannot drift from the layout: the hand-written "two
103
108
  * levels up (`../../node_modules`)" it replaced pointed at .tumwater/ itself, and on 2026-09-29
104
109
  * a bugfix run that followed it (`cd ../..`, git output empty there) fell back to the primary
@@ -111,118 +116,25 @@ export const DEFAULT_SCOPE = `- Do exactly ONE focused task, then stop. Small, c
111
116
  - Choose the task within your first ~15 tool calls, in a handful of turns. A task that would
112
117
  need more than roughly 60 tool calls, or most of the codebase in view, is too big for one
113
118
  run — take a smaller one.`;
114
- /** The rules every loop prompt carries — tick and director alike, so the two cannot drift.
119
+ /** The rules every role prompt carries — run and director alike, so the two cannot drift.
115
120
  * `check` — the project's resolved check (plans/portability.md §6/7) — names the actual
116
121
  * verification command in the Leave-the-project-working rule instead of asserting npm, and
117
122
  * only an npm check keeps the node_modules borrowing sentence: false and actively misleading
118
123
  * in a repo with no node_modules anywhere (a Python, Rust, or Go repo, or one with no check
119
124
  * at all). Undefined keeps the generic wording, exactly as prompts read before a check could
120
- * be configured. `scope` is the Scope section's rules: DEFAULT_SCOPE unless the tick's role
121
- * states its own (Role.scope). A rule added here goes in compactRules too, and its directive in
122
- * test/compact-prompt.test.ts's DIRECTIVES list, which checks that both forms state it. */
123
- function commonRules(check, briefFile = "README.md", scope = DEFAULT_SCOPE, dialect = PI_DIALECT, compact) {
124
- if (compact)
125
- return compactRules(check, briefFile, scope, dialect, compact.assigned);
126
- const verify = check
127
- ? `verify with ${describeCheck(check)} (the project's declared check) — run it after your
125
+ * be configured. `scope` is the Scope section's rules: DEFAULT_SCOPE unless the run's role
126
+ * states its own (Role.scope). Orientation differs for an `assigned` run, which reads its entry
127
+ * first and the brief only when the entry needs it — reading the brief in full cost a median
128
+ * ~2.5k tokens of every run before its first edit (2026-10-17 audit). */
129
+ function commonRules(check, briefFile = "README.md", scope = DEFAULT_SCOPE, dialect = PI_DIALECT, assigned = false, gateCommand) {
130
+ const verify = check && gateCommand
131
+ ? `verify with \`${gateCommand}\` (lint, compile and the tests your change affects) — run it
132
+ after your change and fix what you broke. Tumwater runs the full check (${describeCheck(check)})
133
+ itself; do not run it.`
134
+ : check
135
+ ? `verify with ${describeCheck(check)} (the project's declared check) — run it after your
128
136
  change and fix what you broke.`
129
- : `if it has a build or test command, run it after your change and fix what you broke.`;
130
- const modules = check?.kind === "npm"
131
- ? `
132
- - Your worktree has no node_modules of its own; it borrows the install at the repo root
133
- (\`${ROOT_FROM_WORKTREE}/node_modules\`). To inspect a dependency's types or source, read that
134
- directory directly instead of widening the search outward.`
135
- : "";
136
- return `
137
- Rules for this run:
138
-
139
- Orientation — read this much before choosing your task, and no more:
140
- - First read the project brief (${briefFile}) in full, plus QUESTIONS.md when present.
141
- - PLANS.md and BUGS.md hold open work: the prompt's <backlog-index> block lists their actionable
142
- entries (PLANS.md ## Planned, BUGS.md ## Open, QUESTIONS.md ## Open), each with its 1-based line
143
- range, so read only the entries you need by those ranges. Finished entries live in
144
- PLANS-DONE.md and BUGS-FIXED.md, newest first; the index lists the newest few with their
145
- ranges. Those archive files only grow — never read them whole: search them with grep, or read
146
- one entry by its range, and only when a specific finished entry matters to your task.
147
- - Your worktree starts clean at main, and tumwater checks main's build before code roles
148
- start: do not run the build or test suite just to establish a baseline. Run it after your
149
- change, or when the task itself needs its output (reproducing a failure, counting the suite).
150
-
151
- Scope:
152
- ${scope}
153
-
154
- Reading budget:
155
- ${contextBudgetRule(dialect)}${backendRuleLines(dialect.backendRules)}
156
-
157
- Where you work:
158
- - Stay inside your worktree: run commands from it — the directory you start in, where
159
- \`git log\` and \`git show\` work just the same — or from a scratch directory under the system
160
- temp.
161
- - Other agents run concurrently on this machine: put scratch files under \`$TMPDIR\` (made with
162
- \`mktemp\`), never at a fixed \`/tmp\` path such as \`/tmp/check.log\`, so no other run overwrites
163
- or reads yours.
164
- - Never cd to the repo root (\`${ROOT_FROM_WORKTREE}\`): it is the primary checkout, which tumwater
165
- merges main into while you work, and when this project is tumwater itself its dist/ is the
166
- code the running team executes — never build, test, or edit files in that checkout.${modules}
167
- - Never run an unbounded scan (\`find /\`, \`grep -r /\`, any recursive search rooted outside the
168
- repo): it runs for tens of minutes with no output until tumwater kills your run as hung.
169
- - Never run a command that can wait forever — interactive programs (TUIs, REPLs, editors,
170
- anything reading stdin), servers, or watch modes. To test such a program, background it with
171
- a hard time limit (kill it after at most 30 minutes) and never give it a real TTY.
172
- - ${KILL_BY_PID_RULE} A pattern naming your check or test runner also matches other
173
- agents' runs and tumwater's own checks on this machine, and kills them too.
174
- - Leave the project working: ${verify} One run answers every question about the check: write
175
- its full output to a file in a scratch dir once (\`<check> > <scratch-dir>/check.log 2>&1\`),
176
- then pipe that file through \`tail\` — only the failures matter. To see any other part of the
177
- output, grep or read the same file; re-run the check only after an edit changes the tree.
178
- ${TEST_RUNNER_RULE}
179
-
180
- Boundaries:
181
- - Never create, amend, or revert git commits, branches, or merges — tumwater handles all git
182
- operations. Reading git history is fine.
183
- - Never touch the .tumwater directory or tumwater.json.
184
- - Never edit the brief block in ${briefFile} (between the tumwater:prompt markers).
185
- - PRINCIPLES.md holds this project's design principles; only the director and steward roles may
186
- edit it. Treat it as read-only — to object to a principle, record the objection in PLANS.md.
187
- - docs/invariants.md states each subsystem's guarantees, pinned to the test that enforces them.
188
- Read the matching section before you reconstruct what the code you are changing is supposed
189
- to guarantee; when a merge adds or changes a guarantee, update the register in the same
190
- commit.
191
-
192
- When to ask, when to refuse:
193
- - When a fork in the road is genuinely the user's call (product direction, an irreversible
194
- choice, taste), do not guess: append a question to QUESTIONS.md under ## Open with context,
195
- the options, and your own recommendation. Then continue with the parts that don't depend on
196
- it, or end the run. Never block on an unanswered question; check QUESTIONS.md for answers at
197
- the start of each run and act on one that unblocks your work. Do not re-ask an open question.
198
- - If partway in you conclude the task would harm the project — it violates PRINCIPLES.md, grows
199
- complexity without justification, or keeps fighting back — refuse it. Revert nothing; append
200
- this note directly under the entry's heading in PLANS.md (a planned feature) or BUGS.md:
201
- **Refused <YYYY-MM-DD> by <role>: <one-line reason>**
202
- That note is the only change a refusing run leaves.
203
- - When choosing work, skip entries carrying a Refused note — do not pick them and do not
204
- re-refuse them. The objection stands until a human or the director edits the entry, so a
205
- fully blocked backlog is a legitimate nothing-to-do state.
206
-
207
- ${CLAIMS_RULE}
208
-
209
- How to end your reply — tumwater parses it, so the form matters:
210
- - Your last message is plain text: never a tool call, and never an announcement of what you would
211
- do next ("Let me check…") — either make the call or finish. If a tool result only repeats what
212
- you already have, do not call it again.
213
- ${REPLY_ENDINGS}`;
214
- }
215
- /** commonRules for the compact prompt (CompactPrompt): every directive of the full rules, in
216
- * fewer words. Orientation differs for an assigned run, which reads its entry first and the
217
- * brief only when the entry needs it — reading the brief in full cost a median ~2.5k tokens of
218
- * every run before its first edit (2026-10-17 audit). Scope, Boundaries' first three bullets,
219
- * TEST_RUNNER_RULE and the reply contract are the full prompt's own text. Keep it in step with
220
- * commonRules: test/compact-prompt.test.ts checks every directive both must state. */
221
- function compactRules(check, briefFile, scope, dialect, assigned) {
222
- const verify = check
223
- ? `verify with ${describeCheck(check)} (the project's declared check) — run it after your
224
- change and fix what you broke.`
225
- : `if it has a build or test command, run it after your change and fix what you broke.`;
137
+ : `if it has a build or test command, run it after your change and fix what you broke.`;
226
138
  const modules = check?.kind === "npm"
227
139
  ? `
228
140
  - Your worktree has no node_modules; it borrows \`${ROOT_FROM_WORKTREE}/node_modules\`. Read a
@@ -233,14 +145,15 @@ function compactRules(check, briefFile, scope, dialect, assigned) {
233
145
  - Read your assigned entry (at the end of this prompt) by its line range first. It replaces your
234
146
  task's choosing steps, so this prompt carries no backlog index. Read the project brief
235
147
  (${briefFile}) or QUESTIONS.md only when the entry needs them.
236
- - PLANS.md and BUGS.md grow without bound — never read them wholesale. Read another entry only
237
- by a range this prompt gives, or find it with \`grep -n\` first.`
148
+ - Never read PLANS.md, BUGS.md or their archives (PLANS-DONE.md, BUGS-FIXED.md) whole. Read
149
+ another entry only by a range this prompt gives, or find it with \`grep -n\` first.`
238
150
  : `Orientation — read this much before choosing your task, and no more:
239
151
  - First read the project brief (${briefFile}) in full, plus QUESTIONS.md when present.
240
- - PLANS.md and BUGS.md grow without bound — never read them wholesale. The <backlog-index> block
241
- gives each actionable entry and the newest closed ones with its 1-based line range: read only
242
- the entries you need, by range. The steward role is the exception: it curates those files and
243
- must see them whole.`;
152
+ - PLANS.md and BUGS.md hold open work: the <backlog-index> block gives each actionable entry
153
+ with its 1-based line range, so read only the entries you need, by range. Finished entries live
154
+ in PLANS-DONE.md and BUGS-FIXED.md, newest first (the index lists the newest few). Never read
155
+ those archives whole: grep them, or read one entry by its range, only when a finished entry
156
+ matters to your task.`;
244
157
  return `
245
158
  Rules for this run:
246
159
 
@@ -252,7 +165,7 @@ Scope:
252
165
  ${scope}
253
166
 
254
167
  Reading budget:
255
- ${compactContextBudgetRule(dialect)}${backendRuleLines(dialect.backendRules)}
168
+ ${contextBudgetRule(dialect)}${backendRuleLines(dialect.backendRules)}
256
169
 
257
170
  Where you work:
258
171
  - Run commands from your worktree (where you start; \`git log\` and \`git show\` work there) or a
@@ -266,7 +179,7 @@ Where you work:
266
179
  30 minutes) and never give it a real TTY.
267
180
  - ${KILL_BY_PID_RULE} A pattern also kills other agents' runs and checks.
268
181
  - Leave the project working: ${verify} Write the check's full output to a scratch file once
269
- (\`<check> > <scratch-dir>/check.log 2>&1\`) and \`tail\` it; grep that file for anything else, and
182
+ (\`${gateCommand ? "<gate>" : "<check>"} > <scratch-dir>/check.log 2>&1\`) and \`tail\` it; grep that file for anything else, and
270
183
  re-run the check only after an edit changes the tree.
271
184
  ${TEST_RUNNER_RULE}
272
185
 
@@ -289,11 +202,13 @@ When to ask, when to refuse:
289
202
  justification, or keeps fighting back): refuse it. Revert nothing; append this note directly
290
203
  under the entry's heading in PLANS.md (a planned feature) or BUGS.md:
291
204
  **Refused <YYYY-MM-DD> by <role>: <one-line reason>**
292
- That note is the only change a refusing run leaves.
205
+ The one-line reason must name what in this entry makes it harmful; another entry's hold, or
206
+ your own notes, is never a reason — skip that entry instead. That note is the only change a
207
+ refusing run leaves.
293
208
  - Skip entries carrying a Refused note: never pick or re-refuse them. A fully blocked backlog is
294
209
  a legitimate nothing-to-do state.
295
210
 
296
- ${CLAIMS_RULE_COMPACT}
211
+ ${CLAIMS_RULE}
297
212
 
298
213
  How to end your reply — tumwater parses it, so the form matters:
299
214
  - Your last message is plain text: never a tool call, and never an announcement of what you would
@@ -301,21 +216,21 @@ How to end your reply — tumwater parses it, so the form matters:
301
216
  you already have, do not call it again.
302
217
  ${REPLY_ENDINGS}`;
303
218
  }
304
- /** The principles block's compact form: PRINCIPLES.md from its first list item on, dropping the
305
- * file's own introduction (which restates the block's heading). A file with no list item is
306
- * kept whole. */
219
+ /** What the principles block carries of PRINCIPLES.md: the file from its first list item on,
220
+ * dropping its own introduction (which restates the block's heading). A file with no list item
221
+ * is kept whole. */
307
222
  export function principleBullets(principles) {
308
223
  const lines = principles.split("\n");
309
224
  const first = lines.findIndex((line) => /^[-*] /.test(line));
310
225
  return first <= 0 ? principles : lines.slice(first).join("\n");
311
226
  }
312
- /** The <principles> block injected into every tick and director prompt: the project's codified
313
- * taste, phrased positively so loops follow it rather than merely avoid violations. The file
314
- * itself is read by principles.ts's readPrinciples. */
227
+ /** The <principles> block injected into every run and director prompt: the project's codified
228
+ * taste, phrased positively so roles follow it rather than merely avoid violations — its bullets
229
+ * only (principleBullets). The file itself is read by principles.ts's readPrinciples. */
315
230
  function principlesBlock(principles) {
316
- return `Design principles this project holds — uphold them in everything you produce:\n<principles>\n${principles}\n</principles>`;
231
+ return `Design principles this project holds — uphold them in everything you produce:\n<principles>\n${principleBullets(principles)}\n</principles>`;
317
232
  }
318
- /** The <failure-digest> block injected into the telemetry role's prompt (plans/telemetry-role.md):
233
+ /** The <failure-digest> block injected into the selfcheck role's prompt (plans/telemetry-role.md):
319
234
  * the harness renders the digest from its own event log at the project root and hands it over, so
320
235
  * the role spends no tool calls acquiring evidence and cannot read a stale or foreign log. */
321
236
  function digestBlock(digest) {
@@ -333,19 +248,29 @@ function savedClause(savedDay, today) {
333
248
  return "last saved today; ";
334
249
  return `last saved ${savedDay}, ${plural(age, "day")} ago; `;
335
250
  }
336
- /** The role's notebook as shown to it: its own earlier ticks' note, labeled as unverified.
251
+ /** The role's notes as shown to it: its own earlier runs' note, labeled as unverified.
337
252
  * Rides only when there is a non-empty note; the standing instruction to write one is always
338
- * present (roleNotesInstruction), so a role's FIRST tick — with no note yet — still learns the
339
- * command exists and can seed the notebook. */
340
- function roleNotesBlock(notes, savedDay, today) {
253
+ * present (roleNotesInstruction), so a role's FIRST run — with no note yet — still learns the
254
+ * command exists and can seed the notes. */
255
+ function roleNotesBlock(notes, hasBacklogIndex, savedDay, today) {
256
+ const liveSource = hasBacklogIndex
257
+ ? `the <backlog-index> block above is the live
258
+ source of what is currently true of each entry and series, so check it (and the entry itself) before acting on a
259
+ note's say-so.`
260
+ : `the backlog is the live
261
+ source of what is currently true of each entry and series, so check the entry itself (and, when this run carries
262
+ one, its line in the backlog) before acting on a note's say-so.`;
341
263
  return `Notes your role wrote in earlier runs (${savedClause(savedDay, today)}yours, unverified — check against the code before
342
- relying on them):\n<role-notes>\n${notes.trim()}\n</role-notes>`;
264
+ relying on them). A claim they make about an entry's backlog state — refused, blocked, abandoned, needs-replan,
265
+ done, behind another entry — is a snapshot of the past, not the present: ${liveSource}
266
+ A note that tells you to skip or never re-author an entry is always overridden by the backlog.
267
+ <role-notes>\n${notes.trim()}\n</role-notes>`;
343
268
  }
344
- /** The standing instruction that makes the notebook seedable: every role tick is told it may
345
- * write one, so the first tick (when no note exists yet) can create it. The note content block
269
+ /** The standing instruction that makes the notes seedable: every role run is told it may
270
+ * write one, so the first run (when no note exists yet) can create it. The note content block
346
271
  * (roleNotesBlock) is what is omitted when there is nothing to show. Every backend speaks the
347
272
  * same shell command (plans/notes-command.md §3), and the director never reaches
348
- * buildTickPrompt, so it never carries one. */
273
+ * buildRunPrompt, so it never carries one. */
349
274
  export function roleNotesInstruction() {
350
275
  return `Before you end, if you learned something the next run of your role should know (where
351
276
  things live, what you ruled out and why, what you would look at next), save the full replacement
@@ -363,42 +288,43 @@ const OLDER_NAMES_MAP = "loop = role, tick = run, fleet = team, landing/landed =
363
288
  /** The sentence every assembled prompt carries once: the older names and where all of them are
364
289
  * listed. Interpolated into the shared preamble and the reviewer and resolver openers. */
365
290
  export const OLDER_NAMES_SENTENCE = `Older tumwater names still appear in code, git history and logs: ${OLDER_NAMES_MAP}; docs/glossary.md in tumwater's repository lists them all.`;
366
- /** Shared opening of every loop prompt: where the run happens, what day it is, and why the
367
- * project exists. Defined once so the tick and director prompts cannot drift — and so the one
368
- * sentence telling every role to date its records from dateLine, not from the repo, reaches
369
- * each of them without editing any role text. */
370
- function sharedPreamble(initialPrompt, today) {
371
- const parts = [
291
+ /** Shared opening of every role prompt: where the run happens and what day it is. The brief is
292
+ * not inlined: the rules send each run to the brief file, which holds the same text. Defined
293
+ * once so the run and director prompts cannot drift — and so the one sentence telling every
294
+ * role to date its records from dateLine, not from the repo, reaches each of them without
295
+ * editing any role text. */
296
+ function sharedPreamble(today) {
297
+ return [
372
298
  `You work in a dedicated git worktree of this project; your changes will be committed and merged to main by tumwater after you finish.`,
373
299
  `${dateLine(today)} Stamp it on anything you record now — a new BUGS.md heading ends
374
- "(found by the <role> role YYYY-MM-DD)", a closing date reads "— fixed YYYY-MM-DD by the <role> role",
375
- a new PLANS.md heading reads "(planned YYYY-MM-DD by the <role> role …)", and a Refused or
300
+ "(found by the <role> role YYYY-MM-DD)", a BUGS.md record closes with "— fixed YYYY-MM-DD by the <role> role",
301
+ a finished plan's Done stamp reads "— done YYYY-MM-DD by the <role> role" appended after its
302
+ "(planned YYYY-MM-DD by the <role> role …)" parenthetical, and a Refused or
376
303
  Needs-review note carries the date too — and count plan deadlines from it; never infer the date
377
304
  from the repo.`,
378
305
  OLDER_NAMES_SENTENCE,
379
306
  ];
380
- if (initialPrompt) {
381
- parts.push(`The project's brief — its reason to exist — is:\n<project-prompt>\n${initialPrompt}\n</project-prompt>`);
382
- }
383
- return parts;
384
307
  }
385
- /** The full prompt for one role run. */
386
- export function buildTickPrompt(input) {
387
- const { role, initialPrompt, principles, digest, coverage, backlogStructure, backlogIndex, notes, notesSavedDay, extraInstructions, planTargetNote, flakyChecks, check, briefFile, today, userRequest, agent, compact } = input;
308
+ /** The prompt for one role run. */
309
+ export function buildRunPrompt(input) {
310
+ const { role, rejectedReview, principles, digest, coverage, filings, backlogStructure, backlogIndex, notes, notesSavedDay, notesBacklog, extraInstructions, planTargetNote, flakyChecks, check, gateCommand, briefFile, today, userRequest, agent, assigned } = input;
388
311
  const dialect = dialectFor(agent);
389
312
  const day = today ?? todayStamp();
390
313
  const parts = [
314
+ ...(rejectedReview ? [rejectedReview.trim()] : []),
391
315
  `You are the "${role.id}" role (${role.title}) on this project's tumwater team — tumwater runs a team of AI roles that develop the project.`,
392
- ...sharedPreamble(initialPrompt, day),
316
+ ...sharedPreamble(day),
393
317
  ];
394
318
  if (principles)
395
- parts.push(principlesBlock(compact ? principleBullets(principles) : principles));
319
+ parts.push(principlesBlock(principles));
396
320
  if (userRequest)
397
321
  parts.push(`An explicit request from the user, aimed at this role. Treat it as this run's steering;
398
322
  your role's rules and the merge gate still apply unchanged. If the request is out of this
399
323
  role's scope, say so in your reply instead of doing it anyway.\n<user-request>\n${userRequest.trim()}\n</user-request>`);
400
324
  if (coverage)
401
325
  parts.push(coverage);
326
+ if (filings)
327
+ parts.push(filings);
402
328
  if (digest)
403
329
  parts.push(digestBlock(digest));
404
330
  if (backlogStructure)
@@ -406,7 +332,9 @@ role's scope, say so in your reply instead of doing it anyway.\n<user-request>\n
406
332
  if (backlogIndex)
407
333
  parts.push(backlogIndex);
408
334
  if (notes && notes.trim() !== "")
409
- parts.push(roleNotesBlock(notes, notesSavedDay, day));
335
+ parts.push(roleNotesBlock(notes, Boolean(backlogIndex), notesSavedDay, day));
336
+ if (notesBacklog)
337
+ parts.push(notesBacklog);
410
338
  parts.push(`Your task this run:\n${role.find.trim()}`);
411
339
  if (planTargetNote)
412
340
  parts.push(planTargetNote.trim());
@@ -415,39 +343,35 @@ role's scope, say so in your reply instead of doing it anyway.\n<user-request>\n
415
343
  if (extraInstructions)
416
344
  parts.push(`Additional standing instructions from the user:\n${extraInstructions.trim()}`);
417
345
  parts.push(roleNotesInstruction());
418
- parts.push(commonRules(check, briefFile, role.scope, dialect, compact).trim());
346
+ parts.push(commonRules(check, briefFile, role.scope, dialect, assigned, gateCommand).trim());
419
347
  return parts.join("\n\n");
420
348
  }
421
- /** The prompt for a director tick, which routes a user request into the project. */
422
- export function buildDirectorPrompt(userPrompt, initialPrompt, principles,
423
- /** The project's resolved check (plans/portability.md §6/7) — same threading as the tick
349
+ /** The prompt for a director run, which routes a user request into the project. */
350
+ export function buildDirectorPrompt(userPrompt, principles,
351
+ /** The project's resolved check (plans/portability.md §6/7) — same threading as the run
424
352
  * prompt, since commonRules is shared by both builders. */
425
353
  check,
426
354
  /** The resolved brief file's name (plans/portability.md §7a/7) — same threading again. */
427
355
  briefFile,
428
356
  /** Today's local date (see dateLine). Omitted: todayStamp() — tests pin it. */
429
357
  today,
430
- /** Rendered actionable backlog index (see backlog-structure.ts); the tick prompt carries it
358
+ /** Rendered actionable backlog index (see backlog-index.ts); the run prompt carries it
431
359
  * too, so the director can route against the same bounded view of the backlog. */
432
360
  backlogIndex,
433
- /** The agent backend the director resolves to, selecting the prompt dialect (./dialect.ts).
361
+ /** The backend the director resolves to, selecting the prompt dialect (./dialect.ts).
434
362
  * Omitted: pi's dialect. */
435
- agent,
436
- /** The compact variant every run gets; a director run is never assigned an entry. Omitted:
437
- * the full prompt. */
438
- compact,
363
+ backend,
439
364
  /** The director's `roles.director.instructions` override — its run prompt is otherwise the
440
365
  * user's request. Omitted: no `Additional standing instructions` block, exactly as today. */
441
366
  extraInstructions) {
442
- const variant = compact ? { assigned: false } : undefined;
443
367
  const parts = [
444
368
  `You are the "director" role on this project's tumwater team — tumwater runs a team of AI roles that develop the project. The user steers
445
369
  the project by sending it requests; one has just arrived. Other specialist roles continuously
446
370
  implement planned features from PLANS.md and fix bugs from BUGS.md.`,
447
- ...sharedPreamble(initialPrompt, today),
371
+ ...sharedPreamble(today),
448
372
  ];
449
373
  if (principles)
450
- parts.push(principlesBlock(variant ? principleBullets(principles) : principles));
374
+ parts.push(principlesBlock(principles));
451
375
  if (backlogIndex)
452
376
  parts.push(backlogIndex);
453
377
  parts.push(`The user's request:\n<user-request>\n${userPrompt.trim()}\n</user-request>`);
@@ -455,7 +379,7 @@ implement planned features from PLANS.md and fix bugs from BUGS.md.`,
455
379
  work yourself:
456
380
  - A feature request or substantial change: write a concrete plan for it in PLANS.md (goal,
457
381
  approach, files touched, acceptance criteria) so the feature role implements it. Do not build
458
- it now. ${PLAN_SIZING}
382
+ it now. ${PLAN_SIZING} ${PLAN_QUEUE_GUIDANCE}
459
383
  - A bug report: record it in BUGS.md (symptom, how to reproduce, suspected cause — investigate
460
384
  briefly to sharpen the report) so the bugfix role fixes it. Do not fix it now.
461
385
  - Guidance, a decision, or a constraint (e.g. "prefer X", "drop feature Y"): record it durably
@@ -494,7 +418,7 @@ work yourself:
494
418
  if (extraInstructions)
495
419
  parts.push(`Additional standing instructions from the user:\n${extraInstructions.trim()}`);
496
420
  // Before the shared rules, not after them: the closing reply contract stays the last thing
497
- // in the prompt, where a model attends to it most (the tick prompt's invariant too).
421
+ // in the prompt, where a model attends to it most (the run prompt's invariant too).
498
422
  parts.push(`Note on one boundary in the rules below, director only: user-defined-role requests are
499
423
  executed by writing .tumwater-config-request.json in your worktree (shape and worked example
500
424
  above) — never by editing tumwater.json, which stays off-limits to you as to every role. Tumwater
@@ -502,6 +426,7 @@ validates the request and applies only its customRoles array; a request that als
502
426
  any other setting (timeouts, budgets, role enablement, review settings) has that key discarded
503
427
  with a warning, so such a request is guidance to record per the routing rules, not an edit you
504
428
  can make.`);
505
- parts.push(commonRules(check, briefFile, undefined, dialectFor(agent), variant).trim());
429
+ // A director run is never assigned an entry.
430
+ parts.push(commonRules(check, briefFile, undefined, dialectFor(backend)).trim());
506
431
  return parts.join("\n\n");
507
432
  }