tumwater 0.1.2 → 0.2.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 (361) hide show
  1. package/README.md +74 -108
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +24 -0
  4. package/dist/src/agent/agent-backend.js +2 -2
  5. package/dist/src/agent/backend-traits.js +60 -0
  6. package/dist/src/agent/claude/claude-account.js +61 -8
  7. package/dist/src/agent/claude/claude-args.js +12 -15
  8. package/dist/src/agent/claude/claude-backend.js +51 -41
  9. package/dist/src/agent/claude/claude-env.js +7 -3
  10. package/dist/src/agent/claude/claude-stream.js +7 -16
  11. package/dist/src/agent/image-input.js +107 -0
  12. package/dist/src/agent/notes-shim.js +41 -0
  13. package/dist/src/agent/opencode/opencode-args.js +61 -0
  14. package/dist/src/agent/opencode/opencode-backend.js +255 -0
  15. package/dist/src/agent/opencode/opencode-catalog.js +184 -0
  16. package/dist/src/agent/opencode/opencode-config.js +188 -0
  17. package/dist/src/agent/opencode/opencode-env.js +36 -0
  18. package/dist/src/agent/opencode/opencode-session-id.js +37 -0
  19. package/dist/src/agent/opencode/opencode-stream.js +278 -0
  20. package/dist/src/agent/role-notes-write.js +16 -32
  21. package/dist/src/agent/run-agent.js +95 -18
  22. package/dist/src/agent/session-ledger.js +57 -1
  23. package/dist/src/backlog/backlog-archive.js +296 -0
  24. package/dist/src/backlog/backlog-cli.js +573 -0
  25. package/dist/src/backlog/backlog-commit.js +31 -0
  26. package/dist/src/backlog/backlog-eligibility.js +357 -33
  27. package/dist/src/backlog/backlog-md.js +98 -18
  28. package/dist/src/backlog/backlog-render.js +62 -11
  29. package/dist/src/backlog/backlog-structure.js +261 -58
  30. package/dist/src/backlog/backlog-write.js +197 -319
  31. package/dist/src/backlog/backlog.js +113 -16
  32. package/dist/src/backlog/question-answer.js +339 -0
  33. package/dist/src/baseline/main-baseline.js +75 -88
  34. package/dist/src/baseline/main-red.js +38 -30
  35. package/dist/src/brief/brief-edit.js +43 -0
  36. package/dist/src/{brief.js → brief/brief.js} +66 -10
  37. package/dist/src/budget/budget.js +32 -15
  38. package/dist/src/budget/fallback-breaker.js +8 -7
  39. package/dist/src/build/build-check-detect.js +8 -7
  40. package/dist/src/build/build-check-events.js +68 -22
  41. package/dist/src/build/build-check-report.js +22 -23
  42. package/dist/src/build/build-check-scoped.js +51 -62
  43. package/dist/src/build/build-check.js +15 -14
  44. package/dist/src/build/build-info.js +2 -1
  45. package/dist/src/build/build-stage.js +23 -22
  46. package/dist/src/build/dep-install.js +22 -21
  47. package/dist/src/build/host-sleep.js +1 -1
  48. package/dist/src/change/change-data.js +25 -14
  49. package/dist/src/change/change-render.js +11 -10
  50. package/dist/src/cli/brief-command.js +58 -0
  51. package/dist/src/cli/cli-args.js +25 -5
  52. package/dist/src/cli/cli-command-args.js +46 -14
  53. package/dist/src/cli/cli-editor.js +64 -0
  54. package/dist/src/cli/cli-flag-specs.js +54 -16
  55. package/dist/src/cli/cli-marker-commands.js +13 -10
  56. package/dist/src/cli/cli-output.js +9 -0
  57. package/dist/src/cli/cli-query-commands.js +458 -49
  58. package/dist/src/cli/cli-repo-gate.js +1 -1
  59. package/dist/src/cli/cli-run.js +208 -59
  60. package/dist/src/cli/completion.js +207 -0
  61. package/dist/src/cli/config-commands.js +14 -11
  62. package/dist/src/cli/help.js +305 -110
  63. package/dist/src/cli/log-commands.js +127 -21
  64. package/dist/src/cli/notify-command.js +33 -18
  65. package/dist/src/cli/question-commands.js +29 -224
  66. package/dist/src/cli.js +121 -40
  67. package/dist/src/collections.js +66 -16
  68. package/dist/src/concurrency/check-permit.js +16 -17
  69. package/dist/src/concurrency/lock.js +6 -5
  70. package/dist/src/concurrency/provider-lease.js +69 -0
  71. package/dist/src/concurrency/semaphore.js +258 -36
  72. package/dist/src/config/config-aliases.js +106 -0
  73. package/dist/src/config/config-editable-keys.js +3 -3
  74. package/dist/src/config/config-fallbacks.js +379 -0
  75. package/dist/src/config/config-field-checks.js +19 -10
  76. package/dist/src/config/config-keys.js +160 -0
  77. package/dist/src/config/config-live.js +64 -12
  78. package/dist/src/config/config-schema.js +34 -22
  79. package/dist/src/config/config-validation.js +132 -48
  80. package/dist/src/config/config-views.js +119 -337
  81. package/dist/src/config/config-write.js +138 -177
  82. package/dist/src/config/config.js +60 -49
  83. package/dist/src/config/model-selector.js +28 -2
  84. package/dist/src/doctor/doctor-backlog.js +546 -49
  85. package/dist/src/doctor/doctor-checks.js +534 -56
  86. package/dist/src/doctor/doctor-holds.js +193 -53
  87. package/dist/src/doctor/doctor-install.js +30 -0
  88. package/dist/src/doctor/doctor-launch-services.js +2 -2
  89. package/dist/src/doctor/doctor-model-checks.js +549 -157
  90. package/dist/src/doctor/doctor-notify.js +27 -0
  91. package/dist/src/doctor/doctor-orphans.js +10 -9
  92. package/dist/src/doctor/doctor-render.js +36 -9
  93. package/dist/src/doctor/doctor.js +80 -22
  94. package/dist/src/doctor/image-probe.js +244 -0
  95. package/dist/src/doctor/probe.js +64 -0
  96. package/dist/src/events/event-format.js +226 -102
  97. package/dist/src/events/event-labels.js +116 -0
  98. package/dist/src/events/event-legacy.js +140 -0
  99. package/dist/src/events/event-read.js +58 -22
  100. package/dist/src/events/event-window.js +27 -7
  101. package/dist/src/events/events.js +18 -14
  102. package/dist/src/events/notify.js +137 -38
  103. package/dist/src/failure/failure-cluster.js +12 -9
  104. package/dist/src/failure/failure-data.js +96 -48
  105. package/dist/src/failure/failure-render.js +69 -58
  106. package/dist/src/failure/failure-state-change.js +33 -26
  107. package/dist/src/failure/time-spend.js +79 -59
  108. package/dist/src/files/file-queue.js +16 -4
  109. package/dist/src/files/files.js +18 -6
  110. package/dist/src/files/json-object.js +18 -3
  111. package/dist/src/files/tail.js +1 -1
  112. package/dist/src/gates/bootstrap-gates.js +5 -4
  113. package/dist/src/gates/budget-gates.js +250 -34
  114. package/dist/src/gates/disk-gate.js +18 -18
  115. package/dist/src/gates/gate-polls.js +117 -69
  116. package/dist/src/gates/maintenance-quota.js +29 -29
  117. package/dist/src/gates/pause-gates.js +11 -11
  118. package/dist/src/gates/readiness.js +2 -1
  119. package/dist/src/gates/role-cap-gates.js +16 -21
  120. package/dist/src/gates/startup-gate.js +75 -31
  121. package/dist/src/gates/streak-gate.js +18 -16
  122. package/dist/src/git/commit-message.js +12 -5
  123. package/dist/src/git/git-diff.js +16 -16
  124. package/dist/src/git/git-run.js +8 -8
  125. package/dist/src/git/git.js +86 -55
  126. package/dist/src/git/slots-state.js +14 -5
  127. package/dist/src/git/worktree-pool.js +25 -18
  128. package/dist/src/git/worktree-use.js +15 -14
  129. package/dist/src/git/worktree.js +18 -18
  130. package/dist/src/gui/gui-args.js +10 -14
  131. package/dist/src/gui/gui-backlog-commands.js +273 -0
  132. package/dist/src/gui/gui-command.js +1 -1
  133. package/dist/src/gui/gui-config-commands.js +440 -0
  134. package/dist/src/gui/gui-endpoint-commands.js +16 -669
  135. package/dist/src/gui/gui-endpoint-common.js +49 -0
  136. package/dist/src/gui/gui-endpoints.js +116 -56
  137. package/dist/src/gui/gui-operator-commands.js +145 -0
  138. package/dist/src/gui/gui-prompt-commands.js +234 -0
  139. package/dist/src/gui/gui-server.js +28 -8
  140. package/dist/src/gui/gui-team-commands.js +164 -0
  141. package/dist/src/gui/http-body.js +17 -1
  142. package/dist/src/history/history-data.js +194 -98
  143. package/dist/src/history/history-render.js +76 -0
  144. package/dist/src/history/history.js +37 -58
  145. package/dist/src/inbox/inbox-attachments.js +75 -14
  146. package/dist/src/inbox/inbox-cancel.js +7 -5
  147. package/dist/src/inbox/inbox-edit.js +1 -1
  148. package/dist/src/inbox/inbox-submit.js +44 -4
  149. package/dist/src/inbox/inbox-top.js +83 -0
  150. package/dist/src/inbox/inbox.js +8 -2
  151. package/dist/src/inbox/pending-prompt.js +1 -1
  152. package/dist/src/inbox/prompt-commands.js +170 -80
  153. package/dist/src/init/init-templates.js +16 -14
  154. package/dist/src/init/init.js +12 -11
  155. package/dist/src/loop/leftover.js +28 -27
  156. package/dist/src/loop/loop-fallback.js +2 -2
  157. package/dist/src/loop/loop-pi.js +106 -52
  158. package/dist/src/loop/loop-recovery.js +15 -15
  159. package/dist/src/loop/loop-state.js +139 -13
  160. package/dist/src/loop/loop-tick.js +194 -86
  161. package/dist/src/loop/loop.js +72 -36
  162. package/dist/src/loop/model-fallback.js +3 -2
  163. package/dist/src/loop/revision.js +16 -7
  164. package/dist/src/merge/backlog-conflicts.js +155 -0
  165. package/dist/src/{landing/landing-batch.js → merge/merge-batch.js} +90 -89
  166. package/dist/src/{landing/landing-check-failures.js → merge/merge-check-failures.js} +87 -41
  167. package/dist/src/{landing/landing-core.js → merge/merge-core.js} +119 -95
  168. package/dist/src/{landing/landing-diff.js → merge/merge-diff.js} +1 -1
  169. package/dist/src/merge/merge-drain.js +212 -0
  170. package/dist/src/{landing/landing-git.js → merge/merge-git.js} +94 -21
  171. package/dist/src/merge/merge-pipeline.js +99 -0
  172. package/dist/src/{landing/landing-questions.js → merge/merge-questions.js} +9 -6
  173. package/dist/src/merge/merge-queue-render.js +75 -0
  174. package/dist/src/{landing/landing-queue.js → merge/merge-queue.js} +95 -36
  175. package/dist/src/merge/merge-slot.js +340 -0
  176. package/dist/src/{landing/landing-stack.js → merge/merge-stack.js} +66 -62
  177. package/dist/src/{landing/landing-merge.js → merge/merge-to-main.js} +72 -62
  178. package/dist/src/merge/merge-verify.js +212 -0
  179. package/dist/src/notes-cli.js +97 -0
  180. package/dist/src/operator/operator-commands.js +154 -47
  181. package/dist/src/operator/operator-intent.js +87 -49
  182. package/dist/src/operator/operator-requests.js +52 -52
  183. package/dist/src/operator/retire.js +62 -27
  184. package/dist/src/operator/revert.js +71 -0
  185. package/dist/src/orchestrator/orchestrator-launch.js +74 -14
  186. package/dist/src/orchestrator/orchestrator-scheduling.js +129 -44
  187. package/dist/src/orchestrator/orchestrator.js +158 -100
  188. package/dist/src/orchestrator/retention.js +29 -12
  189. package/dist/src/orchestrator/startup-migration-steps.js +51 -0
  190. package/dist/src/orchestrator/startup-migrations.js +40 -0
  191. package/dist/src/paths.js +128 -37
  192. package/dist/src/pi/pi-args.js +26 -3
  193. package/dist/src/pi/pi-backend.js +15 -23
  194. package/dist/src/pi/pi-bin.js +18 -12
  195. package/dist/src/pi/pi-event-line.js +17 -1
  196. package/dist/src/pi/pi-failure.js +87 -0
  197. package/dist/src/pi/pi-models.js +126 -38
  198. package/dist/src/pi/pi-run-result.js +1 -1
  199. package/dist/src/pi/pi-settings.js +86 -0
  200. package/dist/src/pi/pi-stream.js +131 -107
  201. package/dist/src/pi/pi-watchdogs.js +47 -43
  202. package/dist/src/pi-extension/bounded-output.js +20 -5
  203. package/dist/src/pi-extension/context-budget.js +3 -1
  204. package/dist/src/pi-extension/context-shake.js +54 -21
  205. package/dist/src/pi-extension/context-window.js +37 -0
  206. package/dist/src/pi-extension/full-output.js +2 -2
  207. package/dist/src/pi-extension/strip-thinking.js +80 -0
  208. package/dist/src/pi-extension/trim-system-prompt.js +38 -0
  209. package/dist/src/process/launch-services.js +11 -10
  210. package/dist/src/process/process-table.js +1 -1
  211. package/dist/src/process/process.js +7 -7
  212. package/dist/src/process/run-marker.js +29 -6
  213. package/dist/src/process/supervisor.js +47 -25
  214. package/dist/src/prompt/dialect.js +30 -10
  215. package/dist/src/prompt/gate-prompts.js +170 -67
  216. package/dist/src/prompt/principles.js +1 -0
  217. package/dist/src/prompt/prompt-followup.js +35 -20
  218. package/dist/src/prompt/prompt.js +249 -91
  219. package/dist/src/redeploy/redeploy-policy.js +10 -10
  220. package/dist/src/redeploy/redeploy.js +6 -4
  221. package/dist/src/redeploy/redeployer.js +63 -55
  222. package/dist/src/redeploy/self-reload.js +1 -1
  223. package/dist/src/report/report-data.js +264 -255
  224. package/dist/src/report/report-fold.js +353 -0
  225. package/dist/src/report/report-render.js +270 -33
  226. package/dist/src/request-timeouts.js +1 -1
  227. package/dist/src/review/known-flakes.js +34 -14
  228. package/dist/src/review/protected-edits.js +33 -0
  229. package/dist/src/review/review-followup.js +16 -4
  230. package/dist/src/review/review-precheck.js +14 -13
  231. package/dist/src/review/review-verdict.js +14 -0
  232. package/dist/src/review/review.js +81 -39
  233. package/dist/src/review/secret-leaks.js +127 -0
  234. package/dist/src/review/suite-rerun.js +4 -3
  235. package/dist/src/roles/legacy-role-ids.js +21 -0
  236. package/dist/src/roles/legacy-role-migration.js +62 -0
  237. package/dist/src/roles/loop-ids.js +19 -2
  238. package/dist/src/roles/role-catalog.js +125 -135
  239. package/dist/src/roles/role-guidance.js +64 -31
  240. package/dist/src/roles/role-labels.js +10 -0
  241. package/dist/src/roles/role-render.js +194 -18
  242. package/dist/src/roles/role-view.js +216 -17
  243. package/dist/src/roles/roles.js +18 -15
  244. package/dist/src/scheduling/backoff.js +29 -19
  245. package/dist/src/scheduling/claims.js +25 -9
  246. package/dist/src/scheduling/once-round.js +11 -11
  247. package/dist/src/scheduling/quiet-hours.js +25 -32
  248. package/dist/src/scheduling/scheduling.js +43 -24
  249. package/dist/src/scheduling/{work-landed-cache.js → work-merged-cache.js} +14 -14
  250. package/dist/src/status/status-budget.js +115 -0
  251. package/dist/src/status/status-data.js +77 -184
  252. package/dist/src/status/status-inbox.js +47 -0
  253. package/dist/src/status/status-install.js +27 -0
  254. package/dist/src/status/status-polls.js +68 -33
  255. package/dist/src/status/status-roles.js +105 -0
  256. package/dist/src/{fleet → team}/error-storm.js +10 -10
  257. package/dist/src/{fleet → team}/failure-spread.js +8 -8
  258. package/dist/src/{fleet → team}/orchestrator-info.js +86 -18
  259. package/dist/src/{fleet → team}/reclaim.js +5 -4
  260. package/dist/src/{fleet/fleet-hold.js → team/team-hold.js} +55 -21
  261. package/dist/src/{fleet/fleet-polls.js → team/team-polls.js} +116 -65
  262. package/dist/src/{fleet/fleet-state.js → team/team-state.js} +62 -37
  263. package/dist/src/text/datetime.js +42 -2
  264. package/dist/src/text/format.js +10 -1
  265. package/dist/src/text/markdown.js +1 -1
  266. package/dist/src/text/phrases.js +35 -34
  267. package/dist/src/text/suggest.js +3 -2
  268. package/dist/src/text/text.js +2 -2
  269. package/dist/src/tick/qa-coverage.js +2 -1
  270. package/dist/src/tick/stage-check.js +147 -26
  271. package/dist/src/tick/telemetry-digest.js +10 -9
  272. package/dist/src/tick/tick-apply.js +101 -72
  273. package/dist/src/tick/tick-detail-data.js +32 -18
  274. package/dist/src/tick/tick-detail.js +62 -21
  275. package/dist/src/tick/tick-finalize.js +29 -22
  276. package/dist/src/tick/tick-outcome.js +2 -1
  277. package/dist/src/tick/tick-prompt.js +94 -26
  278. package/dist/src/tick/tick-resume.js +29 -13
  279. package/dist/src/tick/tick-stage.js +91 -47
  280. package/dist/src/tick/tick-timing.js +60 -59
  281. package/dist/src/tick/tick-usage.js +53 -23
  282. package/dist/src/tick/tick-verdict.js +76 -10
  283. package/dist/src/ui/badges.js +118 -67
  284. package/dist/src/ui/gui/gui-client-boot.js +209 -30
  285. package/dist/src/ui/gui/gui-client-composer.js +102 -23
  286. package/dist/src/ui/gui/gui-client-drawer.js +330 -75
  287. package/dist/src/ui/gui/gui-client-history.js +96 -38
  288. package/dist/src/ui/gui/gui-client-labels.js +24 -0
  289. package/dist/src/ui/gui/gui-client-loops.js +94 -52
  290. package/dist/src/ui/gui/gui-client-markdown.js +1 -0
  291. package/dist/src/ui/gui/gui-client-model.js +63 -43
  292. package/dist/src/ui/gui/gui-client-operator.js +129 -39
  293. package/dist/src/ui/gui/gui-client-pending.js +36 -32
  294. package/dist/src/ui/gui/gui-client-report.js +339 -46
  295. package/dist/src/ui/gui/gui-client-settings.js +148 -14
  296. package/dist/src/ui/gui/gui-client-sound.js +2 -1
  297. package/dist/src/ui/gui/{gui-client-fleet.js → gui-client-team.js} +196 -72
  298. package/dist/src/ui/gui/gui-client.js +26 -20
  299. package/dist/src/ui/gui/gui-page.js +19 -19
  300. package/dist/src/ui/gui/gui-styles.js +12 -4
  301. package/dist/src/ui/progress-data.js +5 -3
  302. package/dist/src/ui/status-model.js +184 -122
  303. package/dist/src/ui/status-payload.js +119 -32
  304. package/dist/src/ui/status-render.js +122 -52
  305. package/dist/src/ui/team-alerts.js +341 -0
  306. package/dist/src/ui/tick-progress-model.js +14 -14
  307. package/dist/src/ui/tone.js +11 -8
  308. package/dist/src/ui/transcript-tail.js +1 -0
  309. package/dist/src/ui/transcript.js +23 -16
  310. package/dist/src/ui/tui/tui-app.js +1 -0
  311. package/dist/src/ui/tui/tui-backlog.js +85 -11
  312. package/dist/src/ui/tui/tui-editor-answer.js +97 -0
  313. package/dist/src/ui/tui/tui-editor-attach.js +107 -0
  314. package/dist/src/ui/tui/tui-editor-brief.js +107 -0
  315. package/dist/src/ui/tui/tui-editor-budget.js +155 -0
  316. package/dist/src/ui/tui/tui-editor-ctx.js +1 -0
  317. package/dist/src/ui/tui/tui-editor-entry.js +230 -0
  318. package/dist/src/ui/tui/tui-editor-interval.js +122 -0
  319. package/dist/src/ui/tui/tui-editor-notes.js +114 -0
  320. package/dist/src/ui/tui/tui-editor-pause.js +266 -0
  321. package/dist/src/ui/tui/tui-editor-prompt.js +95 -0
  322. package/dist/src/ui/tui/tui-editor-queued.js +98 -0
  323. package/dist/src/ui/tui/tui-editor-quiet.js +106 -0
  324. package/dist/src/ui/tui/tui-editor-retire.js +83 -0
  325. package/dist/src/ui/tui/tui-editor-search.js +73 -0
  326. package/dist/src/ui/tui/tui-editor-session.js +97 -0
  327. package/dist/src/ui/tui/tui-editors.js +152 -0
  328. package/dist/src/ui/tui/tui-frame.js +67 -55
  329. package/dist/src/ui/tui/tui-history.js +12 -7
  330. package/dist/src/ui/tui/tui-input.js +49 -44
  331. package/dist/src/ui/tui/tui-keymap.js +1 -0
  332. package/dist/src/ui/tui/tui-keys.js +91 -1704
  333. package/dist/src/ui/tui/tui-pane-activity.js +99 -0
  334. package/dist/src/ui/tui/tui-pane-backlog.js +224 -0
  335. package/dist/src/ui/tui/tui-pane-brief.js +30 -0
  336. package/dist/src/ui/tui/tui-pane-ctx.js +11 -0
  337. package/dist/src/ui/tui/tui-pane-health.js +124 -0
  338. package/dist/src/ui/tui/tui-pane-history.js +316 -0
  339. package/dist/src/ui/tui/tui-pane-pending.js +74 -0
  340. package/dist/src/ui/tui/tui-pane-queued.js +143 -0
  341. package/dist/src/ui/tui/tui-pane-report.js +122 -0
  342. package/dist/src/ui/tui/tui-pane-session.js +101 -0
  343. package/dist/src/ui/tui/tui-pane-transcript.js +150 -0
  344. package/dist/src/ui/tui/tui-pane.js +66 -28
  345. package/dist/src/ui/tui/tui-panes.js +294 -0
  346. package/dist/src/ui/tui/tui.js +45 -25
  347. package/dist/src/verdict/fix-claim.js +141 -38
  348. package/dist/src/verdict/no-change.js +19 -0
  349. package/dist/src/verdict/refusal.js +92 -8
  350. package/dist/src/verdict/reply-contract.js +17 -5
  351. package/dist/src/version.js +3 -2
  352. package/package.json +4 -2
  353. package/dist/src/agent/mcp/role-notes-server.js +0 -160
  354. package/dist/src/landing/backlog-conflicts.js +0 -116
  355. package/dist/src/landing/landing-drain.js +0 -204
  356. package/dist/src/landing/landing-pipeline.js +0 -96
  357. package/dist/src/landing/landing-queue-render.js +0 -31
  358. package/dist/src/landing/landing-slot.js +0 -246
  359. package/dist/src/landing/landing-vetting.js +0 -196
  360. package/dist/src/pi-extension/role-notes.js +0 -40
  361. package/dist/src/ui/fleet-alerts.js +0 -174
@@ -1,31 +1,15 @@
1
+ // vocabulary: plain
1
2
  import path from "node:path";
2
3
  import { describeCheck } from "../build/build-check-report.js";
3
4
  import { DECOMPOSITION_GUIDANCE, NEEDS_REPLAN_NOTE, NEEDS_REVIEW_NOTE, PLAN_SIZING } from "../roles/role-guidance.js";
4
- import { CLAIMS_RULE, REPLY_ENDINGS } from "../verdict/reply-contract.js";
5
+ import { CLAIMS_RULE, CLAIMS_RULE_COMPACT, REPLY_ENDINGS } from "../verdict/reply-contract.js";
5
6
  import { todayStamp } from "../budget/budget.js";
7
+ import { daysBetween } from "../text/datetime.js";
8
+ import { plural } from "../text/phrases.js";
6
9
  import { worktreePath } from "../paths.js";
7
10
  import { PI_DIALECT, dialectFor } from "./dialect.js";
8
- /** Prompt construction for the role loops' pi runs (tick and director). The reply contract those
9
- * runs must follow — the TUMWATER_NOTHING_TO_DO sentinel, the TUMWATER_REFUSED line, the
10
- * SUMMARY/WHY/RISK/VERIFIED block format, the three endings, and the claims rules — lives in
11
- * reply-contract.ts; this module weaves it into each prompt. The follow-up prompts that pick a
12
- * session back up — the resume bridge, the missing-summary recovery, and the fresh-tick cut-off
13
- * note — live in prompt-followup.ts. The landing gate's pi runs (conflict resolution, build fix,
14
- * review) build their prompts in gate-prompts.ts; assembling a reply into the tick's commit message
15
- * lives in git/commit-message.ts.
16
- *
17
- * The prose is tuned for the fleet's models (2026-10-01): the primary GLM-5.3-Flash (an 18B-active
18
- * MoE behind a ~1M window) and the budget fallback's local Qwen3.8-27B (~127k window). Both follow
19
- * short grouped rules with concrete numbers and literal commands far better than long dense
20
- * paragraphs, over-read when told merely to "work economically", make unchecked claims the reviewer
21
- * then rejects (the primary's leading rejection cause), and need to be told how a reply must end
22
- * — so the rules are grouped with one rule per bullet, budgets are numeric, role tasks are
23
- * numbered steps, claims carry the check that proves them, the three possible endings are a
24
- * mutually exclusive list, and that closing contract comes last, where a model attends to it most.
25
- * Nothing here is model-conditional. The PRINCIPLES.md reader the principles block is fed from
26
- * lives in principles.ts (readPrinciples, with its injection cap). */
27
11
  /** The context-budget rule every run carries. Under the old 87k window half of all ticks ended
28
- * at the ceiling landing nothing (308 of 733 in the autonomous fortnight; 216 of 245 no-change
12
+ * at the ceiling merging nothing (308 of 733 in the autonomous fortnight; 216 of 245 no-change
29
13
  * ticks in the first week of September), almost all of it whole-file reads: the model, told only
30
14
  * to "work economically", read the codebase file by file (277 whole-file reads against 6 ranged
31
15
  * ones in the bugfix/improve/clean logs). Every token read is prefill and dilutes the model's
@@ -54,12 +38,36 @@ export function contextBudgetRule(dialect = PI_DIALECT) {
54
38
  /** The pi-dialect reading-budget rule, byte-identical to the rule before the dialect split, for
55
39
  * the callers and tests that want the default text. */
56
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
+ return `- Everything you read stays in your context until the run ends and is re-sent on every turn;
46
+ a run that fills the window merges nothing. A whole read of a 500-line file costs ~5k tokens.
47
+ - Check size first (\`wc -l\`) and read files over ~150 lines in ranges: find the lines with
48
+ \`grep -n\`, then read them with ${dialect.readRange}.
49
+ - Cap command output with \`head\`/\`tail\`; never dump a file, a log, or a test run. Do not
50
+ re-read what you already saw.
51
+ - Issue lookups that do not depend on each other as separate tool calls in the same turn; keep
52
+ dependent steps sequential.
53
+ - ${dialect.oversizedOutput}`;
54
+ }
55
+ /** The process-kill rule every run that may run commands carries, word for word: the tick and
56
+ * director rules (full and compact, each adding its own reason), the conflict resolver, and the
57
+ * reviewer. The team's runs and tumwater's own checks share one host, so a kill by pattern
58
+ * reaches them all. On 2026-10-18 a feature run's `pkill -f "dist/test/test-runner.js"; pkill -f
59
+ * "npm run test"` killed two sibling review runs, and a plan run's `pkill -f "npm run test"`
60
+ * killed the coverage run and four lab pi runs (BUGS.md 2026-10-18). Moving pi's prompt from argv
61
+ * to stdin keeps prompt text out of pi's command line; check runs and test runners still match. */
62
+ export const KILL_BY_PID_RULE = `Never kill processes by name or pattern (\`pkill\`, \`killall\`,
63
+ \`pgrep … | xargs kill\`): stop only a process you started, by its PID (\`kill $!\` right after
64
+ backgrounding it).`;
57
65
  /** The test-runner rule every run that may run tests carries: the tick and director rules, the
58
- * conflict resolver, and the reviewer. Project-neutral on purpose — a fleet works on any codebase
66
+ * conflict resolver, and the reviewer. Project-neutral on purpose — a team works on any codebase
59
67
  * (npm, cargo, pytest, a configured check.command), so the rule names no ecosystem's runner as THE
60
68
  * right one: it points at the declared check or the framework already in use, and its one example
61
69
  * is labeled as a mismatch. A run that does not know the suite's runner guesses one: in the week
62
- * to 2026-10-05 the fleet's agents ran `npx vitest` 19 times in tumwater, a node:test repo, and
70
+ * to 2026-10-05 the team's agents ran `npx vitest` 19 times in tumwater, a node:test repo, and
63
71
  * vitest killed the compiled tests mid-run, so no cleanup hook ran (BUGS.md 2026-10-05: ~725 temp
64
72
  * run roots abandoned and test children orphaned by one conflict resolver's five runs). Even where
65
73
  * a guess cannot do that damage, it wastes turns on a runner that finds no tests. tumwater's own
@@ -68,12 +76,21 @@ export const CONTEXT_BUDGET_RULE = contextBudgetRule(PI_DIALECT);
68
76
  export const TEST_RUNNER_RULE = `- Run tests only through the project's declared check or the test framework it already uses —
69
77
  never a runner you guessed (say, \`npx vitest\` in a suite written for node:test): a mismatched
70
78
  runner finds no tests at best, and can kill them mid-run, leaking processes and temp files.`;
79
+ /** Render a dialect's `backendRules` as one bullet each, or nothing when the dialect has none
80
+ * (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. */
83
+ function backendRuleLines(rules) {
84
+ if (!rules || rules.length === 0)
85
+ return "";
86
+ return "\n" + rules.map((rule) => `- ${rule}`).join("\n");
87
+ }
71
88
  /** The date line every pi prompt carries — tick and director (via sharedPreamble) and the gate's
72
89
  * conflict and review runs — naming the local calendar day as YYYY-MM-DD. No prompt
73
90
  * used to say what day it is, so a run that had to write one (a BUGS.md heading's "(found by …
74
91
  * YYYY-MM-DD)", a Fixed or Refused date, NEEDS_REVIEW_NOTE's and NEEDS_REPLAN_NOTE's
75
92
  * <YYYY-MM-DD>, a plan deadline)
76
- * inferred it from the newest dates in the repo, which were themselves drifting: the fleet
93
+ * inferred it from the newest dates in the repo, which were themselves drifting: the team
77
94
  * stamped entries days into the future and each wrong date seeded the next. The day is
78
95
  * todayStamp's — the same local-day computation as the daily cost budget window — and every
79
96
  * builder takes it as an optional trailing `today` that falls through to this default, so tests
@@ -85,7 +102,7 @@ export function dateLine(today = todayStamp()) {
85
102
  * derived from worktreePath so the prompt cannot drift from the layout: the hand-written "two
86
103
  * levels up (`../../node_modules`)" it replaced pointed at .tumwater/ itself, and on 2026-09-29
87
104
  * a bugfix run that followed it (`cd ../..`, git output empty there) fell back to the primary
88
- * checkout's absolute path and ran the suite in the live fleet's own checkout (BUGS.md). */
105
+ * checkout's absolute path and ran the suite in the live team's own checkout (BUGS.md). */
89
106
  export const ROOT_FROM_WORKTREE = path.relative(worktreePath("/", "role"), "/");
90
107
  /** The Scope rules every run carries unless its role states its own (Role.scope): one small,
91
108
  * self-contained task, chosen fast, with a numeric size ceiling. The director always carries
@@ -101,8 +118,11 @@ export const DEFAULT_SCOPE = `- Do exactly ONE focused task, then stop. Small, c
101
118
  * in a repo with no node_modules anywhere (a Python, Rust, or Go repo, or one with no check
102
119
  * at all). Undefined keeps the generic wording, exactly as prompts read before a check could
103
120
  * be configured. `scope` is the Scope section's rules: DEFAULT_SCOPE unless the tick's role
104
- * states its own (Role.scope). */
105
- function commonRules(check, briefFile = "README.md", scope = DEFAULT_SCOPE, dialect = PI_DIALECT) {
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);
106
126
  const verify = check
107
127
  ? `verify with ${describeCheck(check)} (the project's declared check) — run it after your
108
128
  change and fix what you broke.`
@@ -118,14 +138,13 @@ Rules for this run:
118
138
 
119
139
  Orientation — read this much before choosing your task, and no more:
120
140
  - First read the project brief (${briefFile}) in full, plus QUESTIONS.md when present.
121
- - PLANS.md and BUGS.md grow without bound — never read them wholesale. Their actionable
122
- sections come first (## Planned before ## Done; ## Open before ## Fixed): the prompt's
123
- <backlog-index> block lists the actionable entries (PLANS.md ## Planned, BUGS.md ## Open,
124
- QUESTIONS.md ## Open) with each entry's 1-based line range, so read only the entries you need
125
- by those ranges — Planned plus recent Done entries, Open plus recent Fixed ones. Consult older
126
- history via git log or a targeted read only when a specific entry is needed. The steward role
127
- is the exception: it curates those files and must see them whole.
128
- - Your worktree starts clean at main, and the harness checks main's build before code roles
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
129
148
  start: do not run the build or test suite just to establish a baseline. Run it after your
130
149
  change, or when the task itself needs its output (reproducing a failure, counting the suite).
131
150
 
@@ -133,7 +152,7 @@ Scope:
133
152
  ${scope}
134
153
 
135
154
  Reading budget:
136
- ${contextBudgetRule(dialect)}
155
+ ${contextBudgetRule(dialect)}${backendRuleLines(dialect.backendRules)}
137
156
 
138
157
  Where you work:
139
158
  - Stay inside your worktree: run commands from it — the directory you start in, where
@@ -142,14 +161,16 @@ Where you work:
142
161
  - Other agents run concurrently on this machine: put scratch files under \`$TMPDIR\` (made with
143
162
  \`mktemp\`), never at a fixed \`/tmp\` path such as \`/tmp/check.log\`, so no other run overwrites
144
163
  or reads yours.
145
- - Never cd to the repo root (\`${ROOT_FROM_WORKTREE}\`): it is the primary checkout, which the harness
146
- lands main into while you work, and when this project is the harness itself its dist/ is the
147
- code the running fleet executes — never build, test, or edit files in that checkout.${modules}
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}
148
167
  - Never run an unbounded scan (\`find /\`, \`grep -r /\`, any recursive search rooted outside the
149
- repo): it runs for tens of minutes with no output until the harness kills your tick as hung.
168
+ repo): it runs for tens of minutes with no output until tumwater kills your run as hung.
150
169
  - Never run a command that can wait forever — interactive programs (TUIs, REPLs, editors,
151
170
  anything reading stdin), servers, or watch modes. To test such a program, background it with
152
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.
153
174
  - Leave the project working: ${verify} One run answers every question about the check: write
154
175
  its full output to a file in a scratch dir once (\`<check> > <scratch-dir>/check.log 2>&1\`),
155
176
  then pipe that file through \`tail\` — only the failures matter. To see any other part of the
@@ -157,15 +178,15 @@ Where you work:
157
178
  ${TEST_RUNNER_RULE}
158
179
 
159
180
  Boundaries:
160
- - Never create, amend, or revert git commits, branches, or merges — the harness handles all git
181
+ - Never create, amend, or revert git commits, branches, or merges — tumwater handles all git
161
182
  operations. Reading git history is fine.
162
183
  - Never touch the .tumwater directory or tumwater.json.
163
- - Never edit the initial prompt block in ${briefFile} (between the tumwater:prompt markers).
184
+ - Never edit the brief block in ${briefFile} (between the tumwater:prompt markers).
164
185
  - PRINCIPLES.md holds this project's design principles; only the director and steward roles may
165
186
  edit it. Treat it as read-only — to object to a principle, record the objection in PLANS.md.
166
187
  - docs/invariants.md states each subsystem's guarantees, pinned to the test that enforces them.
167
188
  Read the matching section before you reconstruct what the code you are changing is supposed
168
- to guarantee; when a landing adds or changes a guarantee, update the register in the same
189
+ to guarantee; when a merge adds or changes a guarantee, update the register in the same
169
190
  commit.
170
191
 
171
192
  When to ask, when to refuse:
@@ -173,7 +194,7 @@ When to ask, when to refuse:
173
194
  choice, taste), do not guess: append a question to QUESTIONS.md under ## Open with context,
174
195
  the options, and your own recommendation. Then continue with the parts that don't depend on
175
196
  it, or end the run. Never block on an unanswered question; check QUESTIONS.md for answers at
176
- the start of each tick and act on one that unblocks your work. Do not re-ask an open question.
197
+ the start of each run and act on one that unblocks your work. Do not re-ask an open question.
177
198
  - If partway in you conclude the task would harm the project — it violates PRINCIPLES.md, grows
178
199
  complexity without justification, or keeps fighting back — refuse it. Revert nothing; append
179
200
  this note directly under the entry's heading in PLANS.md (a planned feature) or BUGS.md:
@@ -185,12 +206,109 @@ When to ask, when to refuse:
185
206
 
186
207
  ${CLAIMS_RULE}
187
208
 
188
- How to end your reply — the harness parses it, so the form matters:
209
+ How to end your reply — tumwater parses it, so the form matters:
189
210
  - Your last message is plain text: never a tool call, and never an announcement of what you would
190
211
  do next ("Let me check…") — either make the call or finish. If a tool result only repeats what
191
212
  you already have, do not call it again.
192
213
  ${REPLY_ENDINGS}`;
193
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.`;
226
+ const modules = check?.kind === "npm"
227
+ ? `
228
+ - Your worktree has no node_modules; it borrows \`${ROOT_FROM_WORKTREE}/node_modules\`. Read a
229
+ dependency's types or source there.`
230
+ : "";
231
+ const orientation = assigned
232
+ ? `Orientation — read this much before you start, and no more:
233
+ - Read your assigned entry (at the end of this prompt) by its line range first. It replaces your
234
+ task's choosing steps, so this prompt carries no backlog index. Read the project brief
235
+ (${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.`
238
+ : `Orientation — read this much before choosing your task, and no more:
239
+ - 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.`;
244
+ return `
245
+ Rules for this run:
246
+
247
+ ${orientation}
248
+ - Your worktree starts clean at main and tumwater checks main's build first: do not run the
249
+ build or test suite for a baseline. Run it after your change, or when the task needs its output.
250
+
251
+ Scope:
252
+ ${scope}
253
+
254
+ Reading budget:
255
+ ${compactContextBudgetRule(dialect)}${backendRuleLines(dialect.backendRules)}
256
+
257
+ Where you work:
258
+ - Run commands from your worktree (where you start; \`git log\` and \`git show\` work there) or a
259
+ scratch directory. Put scratch files under \`$TMPDIR\` (made with \`mktemp\`), never at a fixed
260
+ \`/tmp\` path: other agents run concurrently on this machine.
261
+ - Never cd to the repo root (\`${ROOT_FROM_WORKTREE}\`): it is the primary checkout tumwater merges
262
+ into — never build, test, or edit files there.${modules}
263
+ - Never run an unbounded scan (\`find /\`, \`grep -r /\`, any recursive search rooted outside the
264
+ repo) or a command that can wait forever (TUIs, REPLs, editors, anything reading stdin,
265
+ servers, watch modes). To test such a program, background it with a hard time limit (at most
266
+ 30 minutes) and never give it a real TTY.
267
+ - ${KILL_BY_PID_RULE} A pattern also kills other agents' runs and checks.
268
+ - 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
270
+ re-run the check only after an edit changes the tree.
271
+ ${TEST_RUNNER_RULE}
272
+
273
+ Boundaries:
274
+ - Never create, amend, or revert git commits, branches, or merges — tumwater handles all git
275
+ operations. Reading git history is fine.
276
+ - Never touch the .tumwater directory or tumwater.json.
277
+ - Never edit the brief block in ${briefFile} (between the tumwater:prompt markers).
278
+ - PRINCIPLES.md is read-only to every role but the director and steward; record an objection to
279
+ a principle in PLANS.md.
280
+ - docs/invariants.md pins each subsystem's guarantees to tests: read the matching section before
281
+ changing that code, and update it in the same commit when a merge changes a guarantee.
282
+
283
+ When to ask, when to refuse:
284
+ - A fork that is genuinely the user's call (product direction, an irreversible choice, taste):
285
+ do not guess. Append a question to QUESTIONS.md under ## Open with context, the options, and
286
+ your recommendation, then continue with what does not depend on it, or end the run. Never
287
+ block on a question, act on an answer that unblocks your work, and do not re-ask an open one.
288
+ - A task that would harm the project (it violates PRINCIPLES.md, grows complexity without
289
+ justification, or keeps fighting back): refuse it. Revert nothing; append this note directly
290
+ under the entry's heading in PLANS.md (a planned feature) or BUGS.md:
291
+ **Refused <YYYY-MM-DD> by <role>: <one-line reason>**
292
+ That note is the only change a refusing run leaves.
293
+ - Skip entries carrying a Refused note: never pick or re-refuse them. A fully blocked backlog is
294
+ a legitimate nothing-to-do state.
295
+
296
+ ${CLAIMS_RULE_COMPACT}
297
+
298
+ How to end your reply — tumwater parses it, so the form matters:
299
+ - Your last message is plain text: never a tool call, and never an announcement of what you would
300
+ do next ("Let me check…") — either make the call or finish. If a tool result only repeats what
301
+ you already have, do not call it again.
302
+ ${REPLY_ENDINGS}`;
303
+ }
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. */
307
+ export function principleBullets(principles) {
308
+ const lines = principles.split("\n");
309
+ const first = lines.findIndex((line) => /^[-*] /.test(line));
310
+ return first <= 0 ? principles : lines.slice(first).join("\n");
311
+ }
194
312
  /** The <principles> block injected into every tick and director prompt: the project's codified
195
313
  * taste, phrased positively so loops follow it rather than merely avoid violations. The file
196
314
  * itself is read by principles.ts's readPrinciples. */
@@ -201,54 +319,83 @@ function principlesBlock(principles) {
201
319
  * the harness renders the digest from its own event log at the project root and hands it over, so
202
320
  * the role spends no tool calls acquiring evidence and cannot read a stale or foreign log. */
203
321
  function digestBlock(digest) {
204
- return `Runtime failure digest of this harness's own event log — your evidence base:\n<failure-digest>\n${digest}\n</failure-digest>`;
322
+ return `Runtime failure digest of tumwater's own event log — your evidence base:\n<failure-digest>\n${digest}\n</failure-digest>`;
323
+ }
324
+ /** The age clause opening the notes heading: `last saved today; `, `last saved <day>, N days
325
+ * ago; `, or `last saved <day>; ` for a day after today (clock skew). Empty without both days. */
326
+ function savedClause(savedDay, today) {
327
+ if (!savedDay || !today)
328
+ return "";
329
+ const age = daysBetween(savedDay, today);
330
+ if (age < 0)
331
+ return `last saved ${savedDay}; `;
332
+ if (age === 0)
333
+ return "last saved today; ";
334
+ return `last saved ${savedDay}, ${plural(age, "day")} ago; `;
205
335
  }
206
336
  /** The role's notebook as shown to it: its own earlier ticks' note, labeled as unverified.
207
337
  * Rides only when there is a non-empty note; the standing instruction to write one is always
208
338
  * present (roleNotesInstruction), so a role's FIRST tick — with no note yet — still learns the
209
- * tool exists and can seed the notebook. */
210
- function roleNotesBlock(notes) {
211
- return `Notes your role wrote in earlier ticks (yours, unverified — check against the code before
339
+ * command exists and can seed the notebook. */
340
+ function roleNotesBlock(notes, savedDay, today) {
341
+ return `Notes your role wrote in earlier runs (${savedClause(savedDay, today)}yours, unverified — check against the code before
212
342
  relying on them):\n<role-notes>\n${notes.trim()}\n</role-notes>`;
213
343
  }
214
344
  /** The standing instruction that makes the notebook seedable: every role tick is told it may
215
345
  * write one, so the first tick (when no note exists yet) can create it. The note content block
216
- * (roleNotesBlock) is what is omitted when there is nothing to show. */
217
- export function roleNotesInstruction(dialect = PI_DIALECT) {
218
- return `Before you end, if you learned something the next tick of your role should know (where
219
- things live, what you ruled out and why, what you would look at next), call ${dialect.notesTool} with
220
- the full replacement note (at most 4 KB). Do not copy backlog entries into it — PLANS.md and
221
- BUGS.md hold the work itself.`;
346
+ * (roleNotesBlock) is what is omitted when there is nothing to show. Every backend speaks the
347
+ * same shell command (plans/notes-command.md §3), and the director never reaches
348
+ * buildTickPrompt, so it never carries one. */
349
+ export function roleNotesInstruction() {
350
+ return `Before you end, if you learned something the next run of your role should know (where
351
+ things live, what you ruled out and why, what you would look at next), save the full replacement
352
+ note (at most 4 KB) by running:
353
+
354
+ tumwater-notes write <<'TUMWATER_NOTES'
355
+ <the note>
356
+ TUMWATER_NOTES
357
+
358
+ Do not copy backlog entries into it — PLANS.md and BUGS.md hold the work itself.`;
222
359
  }
360
+ /** The retired names a model still meets in code, git history and logs, mapped to today's terms
361
+ * (plans/terminology.md "Glossary"). The one place these words may appear in a prompt. */
362
+ const OLDER_NAMES_MAP = "loop = role, tick = run, fleet = team, landing/landed = merge/merged, vetted = approved, notebook = notes, initial prompt = brief"; // vocabulary-ok: maps retired names
363
+ /** The sentence every assembled prompt carries once: the older names and where all of them are
364
+ * listed. Interpolated into the shared preamble and the reviewer and resolver openers. */
365
+ 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.`;
223
366
  /** Shared opening of every loop prompt: where the run happens, what day it is, and why the
224
367
  * project exists. Defined once so the tick and director prompts cannot drift — and so the one
225
368
  * sentence telling every role to date its records from dateLine, not from the repo, reaches
226
369
  * each of them without editing any role text. */
227
370
  function sharedPreamble(initialPrompt, today) {
228
371
  const parts = [
229
- `You work in a dedicated git worktree of this project; your changes will be committed and merged to main by the harness after you finish.`,
230
- `${dateLine(today)} Stamp it on anything you record now — a new BUGS.md or PLANS.md heading's
231
- "(found by … YYYY-MM-DD)", a Fixed or Done date, a Refused or Needs-review note — and count plan
232
- deadlines from it; never infer the date from the repo.`,
372
+ `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
+ `${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
376
+ Needs-review note carries the date too — and count plan deadlines from it; never infer the date
377
+ from the repo.`,
378
+ OLDER_NAMES_SENTENCE,
233
379
  ];
234
380
  if (initialPrompt) {
235
- parts.push(`The project's initial prompt — its reason to exist — is:\n<project-prompt>\n${initialPrompt}\n</project-prompt>`);
381
+ parts.push(`The project's brief — its reason to exist — is:\n<project-prompt>\n${initialPrompt}\n</project-prompt>`);
236
382
  }
237
383
  return parts;
238
384
  }
239
- /** The full prompt for one role-loop tick. */
385
+ /** The full prompt for one role run. */
240
386
  export function buildTickPrompt(input) {
241
- const { role, initialPrompt, principles, digest, coverage, backlogStructure, backlogIndex, notes, extraInstructions, planTargetNote, check, briefFile, today, userRequest, agent } = input;
387
+ const { role, initialPrompt, principles, digest, coverage, backlogStructure, backlogIndex, notes, notesSavedDay, extraInstructions, planTargetNote, flakyChecks, check, briefFile, today, userRequest, agent, compact } = input;
242
388
  const dialect = dialectFor(agent);
389
+ const day = today ?? todayStamp();
243
390
  const parts = [
244
- `You are the "${role.id}" loop (${role.title}) of tumwater, an autonomous development harness.`,
245
- ...sharedPreamble(initialPrompt, today),
391
+ `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),
246
393
  ];
247
394
  if (principles)
248
- parts.push(principlesBlock(principles));
395
+ parts.push(principlesBlock(compact ? principleBullets(principles) : principles));
249
396
  if (userRequest)
250
- parts.push(`An explicit request from the user, aimed at this loop. Treat it as this tick's steering;
251
- your role's rules and the landing gate still apply unchanged. If the request is out of this
397
+ parts.push(`An explicit request from the user, aimed at this role. Treat it as this run's steering;
398
+ your role's rules and the merge gate still apply unchanged. If the request is out of this
252
399
  role's scope, say so in your reply instead of doing it anyway.\n<user-request>\n${userRequest.trim()}\n</user-request>`);
253
400
  if (coverage)
254
401
  parts.push(coverage);
@@ -259,14 +406,16 @@ role's scope, say so in your reply instead of doing it anyway.\n<user-request>\n
259
406
  if (backlogIndex)
260
407
  parts.push(backlogIndex);
261
408
  if (notes && notes.trim() !== "")
262
- parts.push(roleNotesBlock(notes));
409
+ parts.push(roleNotesBlock(notes, notesSavedDay, day));
263
410
  parts.push(`Your task this run:\n${role.find.trim()}`);
264
411
  if (planTargetNote)
265
412
  parts.push(planTargetNote.trim());
413
+ if (flakyChecks)
414
+ parts.push(flakyChecks.trim());
266
415
  if (extraInstructions)
267
416
  parts.push(`Additional standing instructions from the user:\n${extraInstructions.trim()}`);
268
- parts.push(roleNotesInstruction(dialect));
269
- parts.push(commonRules(check, briefFile, role.scope, dialect).trim());
417
+ parts.push(roleNotesInstruction());
418
+ parts.push(commonRules(check, briefFile, role.scope, dialect, compact).trim());
270
419
  return parts.join("\n\n");
271
420
  }
272
421
  /** The prompt for a director tick, which routes a user request into the project. */
@@ -283,27 +432,34 @@ today,
283
432
  backlogIndex,
284
433
  /** The agent backend the director resolves to, selecting the prompt dialect (./dialect.ts).
285
434
  * Omitted: pi's dialect. */
286
- agent) {
435
+ agent,
436
+ /** The compact variant every run gets; a director run is never assigned an entry. Omitted:
437
+ * the full prompt. */
438
+ compact,
439
+ /** The director's `roles.director.instructions` override — its run prompt is otherwise the
440
+ * user's request. Omitted: no `Additional standing instructions` block, exactly as today. */
441
+ extraInstructions) {
442
+ const variant = compact ? { assigned: false } : undefined;
287
443
  const parts = [
288
- `You are the "director" loop of tumwater, an autonomous development harness. The user steers
289
- the project by sending it requests; one has just arrived. Other specialist loops continuously
444
+ `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
+ the project by sending it requests; one has just arrived. Other specialist roles continuously
290
446
  implement planned features from PLANS.md and fix bugs from BUGS.md.`,
291
447
  ...sharedPreamble(initialPrompt, today),
292
448
  ];
293
449
  if (principles)
294
- parts.push(principlesBlock(principles));
450
+ parts.push(principlesBlock(variant ? principleBullets(principles) : principles));
295
451
  if (backlogIndex)
296
452
  parts.push(backlogIndex);
297
453
  parts.push(`The user's request:\n<user-request>\n${userPrompt.trim()}\n</user-request>`);
298
454
  parts.push(`Interpret the request as a project-level command and route it — do NOT implement substantial
299
455
  work yourself:
300
456
  - A feature request or substantial change: write a concrete plan for it in PLANS.md (goal,
301
- approach, files touched, acceptance criteria) so the feature loop implements it. Do not build
457
+ approach, files touched, acceptance criteria) so the feature role implements it. Do not build
302
458
  it now. ${PLAN_SIZING}
303
459
  - A bug report: record it in BUGS.md (symptom, how to reproduce, suspected cause — investigate
304
- briefly to sharpen the report) so the bugfix loop fixes it. Do not fix it now.
460
+ briefly to sharpen the report) so the bugfix role fixes it. Do not fix it now.
305
461
  - Guidance, a decision, or a constraint (e.g. "prefer X", "drop feature Y"): record it durably
306
- where future loops will see it — PRINCIPLES.md first for standing design guidance and taste;
462
+ where future roles will see it — PRINCIPLES.md first for standing design guidance and taste;
307
463
  README.md, PLANS.md, or BUGS.md otherwise — and remove anything it supersedes.
308
464
  - A question: answer it in your final reply, and record anything durable it surfaced.
309
465
  - An answer to an open question (e.g. "answer Q3: choose SQLite"): move that entry from
@@ -311,39 +467,41 @@ work yourself:
311
467
  or route any follow-on work it implies.
312
468
  - A decision about a refused entry (e.g. "clear the refusal on plan X", "reconsider plan Y"):
313
469
  clear its **Refused …** note from PLANS.md/BUGS.md — or revise the entry per the user's
314
- direction — so loops can pick it up again.
470
+ direction — so roles can pick it up again.
315
471
  - A decision about a marked plan (e.g. "split plan X", "keep plan X whole", "replan X"):
316
472
  split it into independently landable sub-plans per PLAN_SIZING, or clear the
317
473
  ${NEEDS_REVIEW_NOTE} note, or clear a ${NEEDS_REPLAN_NOTE} note and rewrite the entry — per
318
474
  the user's direction.
319
- - A request to manage user-defined loops ("add a loop named X that does Y", "remove X",
320
- "move X before Y"): write the FULL replacement customLoops array to
475
+ - A request to manage custom roles ("add a role named X that does Y", "remove X",
476
+ "move X before Y"): write the FULL replacement customRoles array to
321
477
  .tumwater-config-request.json at your worktree root — the shape is
322
- { "customLoops": [ { "name": "…", "task": "…" } ] }. Add appends an entry with a name in
323
- [a-z0-9_-] no built-in role uses and a task written as the loop's standing per-tick
478
+ { "customRoles": [ { "name": "…", "task": "…" } ] }. Add appends an entry with a name in
479
+ [a-z0-9_-] no built-in role uses and a task written as the role's standing per-run
324
480
  instruction (one clear paragraph); remove deletes the entry; move reorders entries (array
325
- order is display/scheduling order). The array REPLACES the current one — include every loop
326
- that should exist, not only the changed entries. Worked example — to add a loop named docs
481
+ order is display/scheduling order). The array REPLACES the current one — include every role
482
+ that should exist, not only the changed entries. Worked example — to add a role named docs
327
483
  that keeps the examples current and drop one named scrape, write the file containing exactly
328
484
  the surviving entries:
329
- { "customLoops": [ { "name": "docs", "task": "Keep the examples/ directory current with the latest API changes." } ] }.
330
- Write the file and stop: the harness validates it, applies it to the live config, and deletes
331
- it; the loops start on their own and the request file is never committed.
485
+ { "customRoles": [ { "name": "docs", "task": "Keep the examples/ directory current with the latest API changes." } ] }.
486
+ Write the file and stop: tumwater validates it, applies it to the live config, and deletes
487
+ it; the roles start on their own and the request file is never committed.
332
488
  - Only a trivially small direct edit (fix a typo, tweak a doc line, adjust a config value the
333
489
  user explicitly stated) may be done immediately instead of routed.
334
490
  - Investigate only as much as routing precisely needs — grep and ranged reads to name the right
335
491
  files, functions, and suspected cause — never a survey of the codebase, and never the
336
492
  implementation itself.
337
493
  - ${DECOMPOSITION_GUIDANCE}`);
494
+ if (extraInstructions)
495
+ parts.push(`Additional standing instructions from the user:\n${extraInstructions.trim()}`);
338
496
  // Before the shared rules, not after them: the closing reply contract stays the last thing
339
497
  // in the prompt, where a model attends to it most (the tick prompt's invariant too).
340
- parts.push(`Note on one boundary in the rules below, director only: user-defined-loop requests are
498
+ parts.push(`Note on one boundary in the rules below, director only: user-defined-role requests are
341
499
  executed by writing .tumwater-config-request.json in your worktree (shape and worked example
342
- above) — never by editing tumwater.json, which stays off-limits to you as to every role. The
343
- harness validates the request and applies only its customLoops array; a request that also names
500
+ above) — never by editing tumwater.json, which stays off-limits to you as to every role. Tumwater
501
+ validates the request and applies only its customRoles array; a request that also names
344
502
  any other setting (timeouts, budgets, role enablement, review settings) has that key discarded
345
503
  with a warning, so such a request is guidance to record per the routing rules, not an edit you
346
504
  can make.`);
347
- parts.push(commonRules(check, briefFile, undefined, dialectFor(agent)).trim());
505
+ parts.push(commonRules(check, briefFile, undefined, dialectFor(agent), variant).trim());
348
506
  return parts.join("\n\n");
349
507
  }
@@ -1,12 +1,12 @@
1
- /** Self-redeploy for a self-hosting fleet (see build/build-info.ts for why): when main's build
1
+ /** Self-redeploy for a self-hosting team (see build/build-info.ts for why): when main's build
2
2
  * inputs have moved past the running build, verify that main is green, compile it into a staging
3
- * dir, drain the fleet (no new ticks; in-flight ones finish or are aborted resumably after
3
+ * dir, drain the team (no new ticks; in-flight ones finish or are aborted resumably after
4
4
  * RESTART_DRAIN_MAX_MS), swap the compiled tree into dist/, and ask the supervisor
5
5
  * (src/process/supervisor.ts) to respawn the harness onto it by exiting RESTART_EXIT_CODE. Every
6
6
  * step is non-blocking from the orchestrator's poll: the green check and the compile run in the
7
7
  * background and are consulted on later polls, so a slow `npm test` never stalls scheduling.
8
8
  * Completed restarts are rate-limited to one per RESTART_COOLDOWN_MS (BUGS.md 2026-09-11), so
9
- * sustained main churn cannot halt the fleet for a drain over and over.
9
+ * sustained main churn cannot halt the team for a drain over and over.
10
10
  *
11
11
  * This module is the POLICY — the injectable effects interface (RedeployDeps), the
12
12
  * cooldown/escalation knobs the state machine decides with, and the RESTART_EXIT_CODE handshake
@@ -17,16 +17,16 @@
17
17
  * contract both halves serve:
18
18
  *
19
19
  * Nothing here is fail-open: a red main, a failed compile, or a swap error blocks the restart for
20
- * that head and the fleet keeps running the old build until main moves again. A green check that
20
+ * that head and the team keeps running the old build until main moves again. A green check that
21
21
  * REJECTS is the one non-verdict — it could not run, so it says nothing about the tree: the
22
22
  * pending head is dropped, one warning is logged per episode, and the next poll re-runs the
23
23
  * check (BUGS.md 2026-09-16). A block says so out loud — one warning event, and the reason in
24
24
  * BuildStatus.restartBlocked, which the dashboards and doctor render — because a stale build that
25
25
  * is about to be replaced and one that never will be look identical otherwise (BUGS.md
26
26
  * 2026-09-08). A green, compiled build can still be unable to START here — the environment, not
27
- * the code, fails `tumwater run`'s startup gate — so that gate is asked before the fleet is held
27
+ * the code, fails `tumwater run`'s startup gate — so that gate is asked before the team is held
28
28
  * and again right before the swap, and a failure refuses the restart with a `restart_refused`
29
- * event instead of trading a running fleet for a child that exits on its first line (BUGS.md
29
+ * event instead of trading a running team for a child that exits on its first line (BUGS.md
30
30
  * 2026-09-23). Every tick a restart interrupts resumes on the new build through the same resume
31
31
  * machinery a Ctrl+C uses, so a restart loses no work. */
32
32
  /** The exit code a supervised `tumwater run` child uses to say "rebuilt; respawn me" —
@@ -38,7 +38,7 @@ export const RESTART_EXIT_CODE = 75;
38
38
  * Redeployer.drainSince. Director ticks are exempt: an in-flight human prompt extends the hold
39
39
  * without any cap (see poll).
40
40
  *
41
- * The live window tracks the fleet's real tick duration: the orchestrator passes the p75 of recent
41
+ * The live window tracks the team's real tick duration: the orchestrator passes the p75 of recent
42
42
  * completed work-bearing role ticks (InFlightCounts.roleTickP75Ms) and poll uses it in place of
43
43
  * this constant once it has enough samples (BUGS.md 2026-09-18). The hand-set 30 minutes had not
44
44
  * been re-derived since an earlier backend; measured p50 was 46 min and p75 82 min, so the drain
@@ -47,15 +47,15 @@ export const RESTART_EXIT_CODE = 75;
47
47
  export const RESTART_DRAIN_MAX_MS = 30 * 60_000;
48
48
  /** How often a COMPLETED auto-restart may land — at most once per this window (BUGS.md
49
49
  * 2026-09-11): under sustained main churn every stale head would otherwise drive a full
50
- * hold+drain+swap episode back to back, halting the fleet for a drain over and over. Bounds
50
+ * hold+drain+swap episode back to back, halting the team for a drain over and over. Bounds
51
51
  * frequency across episodes; RESTART_DRAIN_MAX_MS bounds the drain within one. */
52
52
  export const RESTART_COOLDOWN_MS = 12 * 60 * 60_000;
53
53
  /** The cooldown's urgency carve-out (BUGS.md 2026-09-30): when the RUNNING build's own commit
54
54
  * carries a red baseline verdict, the deferral is cut to this window instead of the full 12 h —
55
55
  * a red running build is not churn but the exact predicament the self-redeploy exists to end,
56
- * and the rate limit protects against storms, not against a fleet knowingly executing code its
56
+ * and the rate limit protects against storms, not against a team knowingly executing code its
57
57
  * own tree fails. Still a real delay, so consecutive reds cannot storm: one episode per this
58
- * window at most, each swap giving the fleet a chance to land on green. */
58
+ * window at most, each swap giving the team a chance to merge on green. */
59
59
  export const RESTART_URGENT_COOLDOWN_MS = 15 * 60_000;
60
60
  /** The urgent carve-out in whole minutes — the one home of that ms→min division, beside the
61
61
  * constant it derives from, so the three redeployer.ts warnings that restate the length in