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,18 +1,39 @@
1
- import { commitAll, foldCommitsSince, headOf } from "../git/git.js";
1
+ import { commitAll, foldCommitsSince, headOf, isDirty } from "../git/git.js";
2
2
  import { changedFiles } from "../git/git-diff.js";
3
- import { buildCommitMessage, commitTrailer, extractCommitBody, extractSummary, summaryOverCap, fallbackSummary, formatCommitBody, mergeCommitBody, stampedSubject, } from "../git/commit-message.js";
3
+ import { buildCommitMessage, commitTrailer, extractCommitBody, extractSummary, summaryOverCap, fallbackSummary, formatCommitBody, appendSelfCheckNotes, stampedSubject, } from "../git/commit-message.js";
4
4
  import { logEventBestEffort } from "../events/events.js";
5
5
  import { enqueueMerge, entryRevisionFields } from "../merge/merge-queue.js";
6
6
  import { plural } from "../text/phrases.js";
7
+ import { errorMessage, squash } from "../text/text.js";
7
8
  import { recordFlow } from "./qa-coverage.js";
8
- import { INSTANCE_ROLES, baseRoleOf, configuredInstances } from "../roles/loop-ids.js";
9
+ import { recordFiled } from "./feedback-filings.js";
10
+ import { INSTANCE_ROLES, baseRoleOf, configuredInstances } from "../roles/runner-ids.js";
9
11
  import { stagedMovedEntries } from "../scheduling/claims.js";
10
- import { entryKey } from "../backlog/backlog-eligibility.js";
11
- import { assignedMovedFinding, heldMovedFinding } from "./stage-check.js";
12
+ import { entryKey } from "../backlog/backlog-parts.js";
13
+ import { assignedMovedFinding, heldMovedFinding } from "./stage-backlog-checks.js";
12
14
  import { archiveNewFinishedEntries } from "../backlog/backlog-archive.js";
13
- /** Fold commits an agent run made since `base` into the tick's tree, keeping the harness commit
14
- * the tick's only one (INV-H1); warns when any were folded. The one home of the fold-and-warn
15
- * policy and its wording, shared by the two fold sites — the authoring run (loop-tick.ts) and
15
+ /** The most findings one self-check outcome event carries. */
16
+ const SELF_CHECK_LOGGED_FINDINGS = 12;
17
+ /** The cap on each logged finding's text. */
18
+ const SELF_CHECK_FINDING_CHARS = 300;
19
+ /** The findings as the outcome event records them: capped in count and in each text's length. */
20
+ function findingDigest(findings) {
21
+ return findings.slice(0, SELF_CHECK_LOGGED_FINDINGS).map((f) => squash(f, SELF_CHECK_FINDING_CHARS));
22
+ }
23
+ /** The self-check's outcome warning. It bypasses `ctx.warn` because it carries fields beyond a
24
+ * message (the finding texts, and those still open); the event shape is otherwise the same. */
25
+ function logSelfCheckOutcome(ctx, message, findings, remaining = []) {
26
+ logEventBestEffort(ctx.root, {
27
+ source: ctx.role,
28
+ type: "warning",
29
+ message,
30
+ findings: findingDigest(findings),
31
+ ...(remaining.length > 0 ? { open: findingDigest(remaining) } : {}),
32
+ });
33
+ }
34
+ /** Fold commits an agent run made since `base` into the run's tree, keeping the harness commit
35
+ * the run's only one (INV-H1); warns when any were folded. The one home of the fold-and-warn
36
+ * policy and its wording, shared by the two fold sites — the authoring run (runner-run.ts) and
16
37
  * this module's follow-up turns — so they cannot drift. */
17
38
  export async function foldAgentCommitsSince(ctx, wt, base) {
18
39
  const folded = await foldCommitsSince(wt, base);
@@ -20,24 +41,24 @@ export async function foldAgentCommitsSince(ctx, wt, base) {
20
41
  ctx.warn(`agent made ${plural(folded, "commit")} during the run; folded into tumwater's commit`);
21
42
  }
22
43
  /** Stage a changed worktree for the merge queue: derive the commit message from the reply's
23
- * SUMMARY block (one bounded follow-up turn when the run left none), flag high-friction ticks,
44
+ * SUMMARY block (one bounded follow-up turn when the run left none), flag high-friction runs,
24
45
  * commit, pin the commit by the role's merge ref, and enqueue the merge. Extracted from
25
- * LoopRunner.runTick so the tick's own method stays the outcome classifier and this module is
26
- * the commit-staging layer — the only place a fresh tick's work becomes a queued merge.
27
- * Returns the tick's outcome; a failed pin or an aborted follow-up returns an error/aborted
46
+ * RoleRunner.runOnce so the run's own method stays the outcome classifier and this module is
47
+ * the commit-staging layer — the only place a fresh run's work becomes a queued merge.
48
+ * Returns the run's outcome; a failed pin or an aborted follow-up returns an error/aborted
28
49
  * outcome without anything queued. */
29
- export async function stageTickMerge(ctx) {
50
+ export async function stageRunMerge(ctx) {
30
51
  const s = ctx.state;
31
- // A revision tick carries the round it applied on its context (loop-tick.ts): it rides onto the
52
+ // A revision run carries the round it applied on its context (runner-run.ts): it rides onto the
32
53
  // queued merge so a rejection of this change records the NEXT round (plans/revise-rejected.md).
33
- // Undefined for a fresh change — including a user-request tick that leaves a pending revision
34
- // untouched — so only the tick that actually held the rejected diff is labeled a revision.
54
+ // Undefined for a fresh change — including a user-request run that leaves a pending revision
55
+ // untouched — so only the run that actually held the rejected diff is labeled a revision.
35
56
  const revisionRound = ctx.revisionRound;
36
57
  // A revision merge carries the review that rejected the change (plans/revise-rejected.md
37
58
  // part 2/2): the prior head, its numbered objections, and each objection's persistence count
38
59
  // ride the queued entry so the re-review can check each one against the interdiff and the next
39
- // rejection can resolve a `(prior N)` restatement (BUGS.md 2026-10-14). Only a tick that
40
- // actually held the rejected diff attaches it; a fresh change — including a user-request tick
60
+ // rejection can resolve a `(prior N)` restatement (BUGS.md 2026-10-14). Only a run that
61
+ // actually held the rejected diff attaches it; a fresh change — including a user-request run
41
62
  // that leaves a pending revision untouched — and a recovery merge carry none.
42
63
  const priorReview = revisionRound !== undefined && s.lastReview?.head
43
64
  ? {
@@ -52,18 +73,22 @@ export async function stageTickMerge(ctx) {
52
73
  // only if that too yields nothing is the subject derived from the changed paths.
53
74
  let summary = extractSummary(ctx.finalText);
54
75
  let body = extractCommitBody(ctx.finalText);
76
+ // True when no reply named the change, so the subject below is derived from its paths.
77
+ let summaryDerived = false;
55
78
  if (summary === null) {
56
79
  const base = await headOf(ctx.wt, "HEAD");
57
80
  const followUp = await ctx.requestSummary(ctx.wt);
58
81
  await foldAgentCommitsSince(ctx, ctx.wt, base);
59
82
  if (followUp?.aborted)
60
- return ctx.finishAbortedTick();
83
+ return ctx.finishAbortedRun();
61
84
  if (followUp) {
62
85
  summary = extractSummary(followUp.finalText);
63
86
  body = body ?? extractCommitBody(followUp.finalText);
64
87
  }
65
- if (summary === null)
88
+ if (summary === null) {
66
89
  summary = fallbackSummary(await changedFiles(ctx.wt), ctx.role, s.runs);
90
+ summaryDerived = true;
91
+ }
67
92
  ctx.warn(`reply had no SUMMARY line — ` +
68
93
  (followUp && extractSummary(followUp.finalText) !== null
69
94
  ? "recovered it with a follow-up turn"
@@ -82,7 +107,7 @@ export async function stageTickMerge(ctx) {
82
107
  const followUp = await ctx.requestSummary(ctx.wt, over);
83
108
  await foldAgentCommitsSince(ctx, ctx.wt, base);
84
109
  if (followUp?.aborted)
85
- return ctx.finishAbortedTick();
110
+ return ctx.finishAbortedRun();
86
111
  const short = followUp && summaryOverCap(followUp.finalText) === null ? extractSummary(followUp.finalText) : null;
87
112
  if (short !== null) {
88
113
  summary = short;
@@ -99,11 +124,11 @@ export async function stageTickMerge(ctx) {
99
124
  // author's own session gets to fix them; the gate still has the final say, and the change commits
100
125
  // and queues either way.
101
126
  // Claims made at staging (plans/parallel-work-instances.md "Claims", part 4/7): an
102
- // unassigned tick of a MULTI-instance base whose diff moved a backlog entry out of its
127
+ // unassigned run of a MULTI-instance base whose diff moved a backlog entry out of its
103
128
  // actionable section records the claim so a sibling is not assigned it while this change
104
129
  // merges. A single-runner base has no sibling to hold the entry from, so it stages nothing
105
- // and is unchanged by claims. An assigned tick that moved a DIFFERENT entry gets a finding,
106
- // so the one fix-up turn can put it back. So does an unassigned tick that moved an entry a
130
+ // and is unchanged by claims. An assigned run that moved a DIFFERENT entry gets a finding,
131
+ // so the one fix-up turn can put it back. So does an unassigned run that moved an entry a
107
132
  // sibling holds (the poll's heldElsewhere, which its prompt listed as off-limits): that entry
108
133
  // is never claimed a second time, and the fix-up turn is asked to drop the duplicate work
109
134
  // (BUGS.md 2026-10-12). An unreadable file or git hiccup yields no move and no finding.
@@ -119,7 +144,7 @@ export async function stageTickMerge(ctx) {
119
144
  const first = claimable[0];
120
145
  s.claim = { file: first.file, key: first.key, title: first.title, at: Date.now(), source: "staged" };
121
146
  // The assignment is durable in state before this event; an unwritable feed must not reject
122
- // the tick and make a claim that did take effect look unapplied (logEventBestEffort).
147
+ // the run and make a claim that did take effect look unapplied (logEventBestEffort).
123
148
  logEventBestEffort(ctx.root, {
124
149
  source: ctx.role,
125
150
  type: "claim",
@@ -138,50 +163,59 @@ export async function stageTickMerge(ctx) {
138
163
  if (findings.length > 0) {
139
164
  const before = findings.length;
140
165
  const fixBase = await headOf(ctx.wt, "HEAD");
141
- const fixUp = await ctx.requestStageFix(ctx.wt, findings);
166
+ const fixUp = await ctx.requestStageFix(ctx.wt, findings, {
167
+ summary: summaryDerived ? null : summary,
168
+ body: body ? formatCommitBody(body) : "",
169
+ });
142
170
  await foldAgentCommitsSince(ctx, ctx.wt, fixBase);
143
171
  if (fixUp?.aborted)
144
- return ctx.finishAbortedTick();
172
+ return ctx.finishAbortedRun();
145
173
  if (fixUp) {
146
- summary = extractSummary(fixUp.finalText) ?? summary;
147
- // Field-by-field: a fix-up reply need only restate the fields it changed, so the
148
- // authoring run's other WHY/RISK/VERIFIED lines survive it.
149
- body = mergeCommitBody(body, extractCommitBody(fixUp.finalText));
174
+ // The fix-up reply answers the findings, not the change, so it never replaces the
175
+ // change's description (BUGS.md 2026-10-21): its SUMMARY is taken only over a subject
176
+ // derived from the paths, and its RISK/VERIFIED are appended as self-check notes.
177
+ const fixSummary = summaryDerived ? extractSummary(fixUp.finalText) : null;
178
+ if (fixSummary !== null)
179
+ summary = fixSummary;
180
+ body = appendSelfCheckNotes(body, extractCommitBody(fixUp.finalText));
150
181
  }
151
182
  // A fix-up turn may resolve its findings by reverting the whole change. With nothing left
152
- // in the worktree there is nothing to commit or queue: end the tick no_change here instead
183
+ // in the worktree there is nothing to commit or queue: end the run no_change here instead
153
184
  // of handing a clean tree to commitAll. The worktree is left clean and no pin is taken.
154
- if ((await changedFiles(ctx.wt)).length === 0) {
155
- ctx.warn("stage self-check: the follow-up turn left no change; nothing to land");
185
+ // isDirty, not changedFiles: a `git status` that fails must throw, not read as clean — on
186
+ // 2026-10-21 a spawn failure here ended bugfix-2's 17-minute run no_change and released
187
+ // its edits unpinned; thrown, the run ends unsettled and its next run gets them back.
188
+ if (!(await isDirty(ctx.wt))) {
189
+ logSelfCheckOutcome(ctx, "stage self-check: the follow-up turn left no change; nothing to land", findings);
156
190
  return { result: "no_change" };
157
191
  }
158
192
  const remaining = await ctx.stageCheck(ctx.wt);
159
- ctx.warn(remaining.length === 0
193
+ logSelfCheckOutcome(ctx, remaining.length === 0
160
194
  ? `stage self-check: ${plural(before, "finding")} — fixed by the follow-up turn`
161
- : `stage self-check: ${plural(before, "finding")}; ${remaining.length} still open, queued for the gate`);
195
+ : `stage self-check: ${plural(before, "finding")}; ${remaining.length} still open, queued for the gate`, findings, remaining);
162
196
  }
163
- // Friction as a signal (plans/refusal-and-thrash.md): a changed tick that burned BOTH more
197
+ // Friction as a signal (plans/refusal-and-thrash.md): a changed run that burned BOTH more
164
198
  // than thrashTurns turns and thrashMinutes of wall clock is flagged high-friction —
165
199
  // difficulty suggests the work may not fit, so it goes to review marked and leaves a warning
166
200
  // event. Requiring both (not either) keeps the absolute turn count from measuring model
167
201
  // speed: a fast model emits 40+ turns in a few minutes, which is ordinary work, not
168
- // difficulty (BUGS.md 2026-09-19). Measured over this tick's main authoring run (a transient
202
+ // difficulty (BUGS.md 2026-09-19). Measured over this run's main authoring run (a transient
169
203
  // retry included via runAuthoringPi), like the trailer; conflict-resolution runs happen later
170
204
  // inside merge().
171
205
  const minutes = (Date.now() - ctx.piStartedAt) / 60_000;
172
- // Friction is measured over this tick's authoring runs only — the review gate folds its
173
- // run into tickTurns AFTER the commit, so every friction artifact (flag, warning event,
206
+ // Friction is measured over this run's authoring runs only — the review gate folds its
207
+ // run into runTurns AFTER the commit, so every friction artifact (flag, warning event,
174
208
  // trailer line, final summary) reads this pre-gate snapshot instead of the live counter.
175
- const authoringTurns = ctx.tickTurns;
209
+ const authoringTurns = ctx.runTurns;
176
210
  const highFriction = authoringTurns > ctx.config.thrashTurns && minutes > ctx.config.thrashMinutes;
177
211
  if (highFriction) {
178
212
  ctx.warn(`high-friction run: ${authoringTurns} turns in ${Math.round(minutes)} min ` +
179
213
  `(thresholds: ${ctx.config.thrashTurns} turns / ${ctx.config.thrashMinutes} min)`);
180
214
  }
181
- // The trailer is harness-stamped truth: turns and peak ctx over this tick's pre-commit
215
+ // The trailer is harness-stamped truth: turns and peak ctx over this run's pre-commit
182
216
  // runs only (conflict-resolution and review runs fold after the commit). A high-friction
183
- // tick appends its Friction line here — both values are already computed above.
184
- const message = buildCommitMessage(stampedSubject(baseRoleOf(ctx.role), summary), body, commitTrailer(ctx.role, s.runs, authoringTurns, s.peakContextTokens, highFriction ? minutes : undefined, revisionRound));
217
+ // run appends its Friction line here — both values are already computed above.
218
+ const message = buildCommitMessage(stampedSubject(baseRoleOf(ctx.role), summary), body, commitTrailer(ctx.role, s.runs, authoringTurns, s.peakContextTokens, highFriction ? minutes : undefined, revisionRound, ctx.modelLine));
185
219
  // The backlog archive (plans/backlog-archive.md): an entry this change pasted under PLANS.md's
186
220
  // ## Done or BUGS.md's ## Fixed moves to the top of PLANS-DONE.md or BUGS-FIXED.md now, after
187
221
  // the self-check and its fix-up turn worked on the tree the author wrote and before the
@@ -193,19 +227,19 @@ export async function stageTickMerge(ctx) {
193
227
  // Pin the commit by its merge ref BEFORE freeing the worktree (invariant 4), then hand it
194
228
  // to the merge queue: from here on the review gate and the rebase run in a verification's
195
229
  // leased pool slot, never in this worktree (plans/merge-queue.md 2/5). A failed pin defers to
196
- // next-tick recovery — merge without a pin would lose the ref lifecycle this whole flow depends
230
+ // next-run recovery — merge without a pin would lose the ref lifecycle this whole flow depends
197
231
  // on. The reviewer checks the author's claimed WHY/VERIFIED against the actual diff; no diff
198
232
  // reaches main unreviewed.
199
233
  if (!(await ctx.pinAndReset(ctx.wt, commit))) {
200
234
  s.lastError = "failed to pin the merge ref; left for the next run's recovery";
201
235
  return { result: "error", summary: s.lastError };
202
236
  }
203
- // The commit is pinned and the worktree is free: enqueue the merge and END the tick — the
237
+ // The commit is pinned and the worktree is free: enqueue the merge and END the run — the
204
238
  // orchestrator drains the queue on its single merge slot, outside the author semaphore, so
205
239
  // this slot is free the moment the work is committed (plans/merge-queue.md 3/5). The merge
206
240
  // runs the same gate + merge flow through runMergePi/foldMergeUsage on this same state
207
241
  // object — recording the commit count, the outcome, and the reviewer spend into it — and logs
208
- // merge_done/merge_failed; a non-terminal outcome keeps the pin for next-tick leftover recovery.
242
+ // merge_done/merge_failed; a non-terminal outcome keeps the pin for next-run leftover recovery.
209
243
  // `commits` was NOT incremented above: it counts merged changes only.
210
244
  enqueueMerge(ctx.root, {
211
245
  role: ctx.role,
@@ -224,14 +258,38 @@ export async function stageTickMerge(ctx) {
224
258
  if (revisionRound !== undefined)
225
259
  s.revision = undefined;
226
260
  // The merge sits in the durable queue above and the commit is pinned; the event is
227
- // observability, so an unwritable feed must not reject a tick whose work is already queued.
261
+ // observability, so an unwritable feed must not reject a run whose work is already queued.
228
262
  logEventBestEffort(ctx.root, { source: ctx.role, type: "merge_queued", commit, summary });
229
263
  // The flag's durable record is the Friction trailer line stamped on the commit above;
230
264
  // lastSummary and the run_end event carry it too for dashboards and logs.
231
265
  const finalSummary = highFriction
232
266
  ? `${summary} (high friction: ${authoringTurns} turns / ${Math.round(minutes)}m)`
233
267
  : summary;
234
- if (ctx.flow)
235
- recordFlow(ctx.root, ctx.flow.flow, ctx.flow.result, ctx.flow.result === "bug" ? summary : undefined);
268
+ if (ctx.flow) {
269
+ // The merge is already queued and the ledger only orders the qa menu, so a failed write
270
+ // warns rather than failing the run.
271
+ try {
272
+ recordFlow(ctx.root, ctx.flow.flow, ctx.flow.result, ctx.flow.result === "bug" ? summary : undefined);
273
+ }
274
+ catch (err) {
275
+ ctx.warn(`qa flow "${ctx.flow.flow}" not recorded: ${errorMessage(err)}`);
276
+ }
277
+ }
278
+ // A feedback run that somehow changed the tree still records its filing, exactly as the
279
+ // qa flow does above — a filing is a result independent of the run leaving files behind.
280
+ if (ctx.filed) {
281
+ logEventBestEffort(ctx.root, {
282
+ source: ctx.role,
283
+ type: "issue_filed",
284
+ issue: ctx.filed.issue,
285
+ title: ctx.filed.title,
286
+ });
287
+ try {
288
+ recordFiled(ctx.root, ctx.filed.issue, ctx.filed.title);
289
+ }
290
+ catch (err) {
291
+ ctx.warn(`feedback filing #${ctx.filed.issue} not recorded: ${errorMessage(err)}`);
292
+ }
293
+ }
236
294
  return { result: "queued", summary: finalSummary, commit, highFriction };
237
295
  }
@@ -1,26 +1,25 @@
1
- // vocabulary: plain
2
1
  import { BUILD_CHECK_TIMEOUT_MS } from "../build/build-check-detect.js";
3
- import { warnEvent, TUMWATER_SOURCE } from "../events/events.js";
2
+ import { warnEventBestEffort, TUMWATER_SOURCE } from "../events/events.js";
4
3
  import { removeQuiet } from "../files/files.js";
5
4
  import { mergeStatePath } from "../paths.js";
6
5
  import { runOnAbort } from "../process/process.js";
7
- /** The orchestrator's tick-timing and scheduling seams (src/orchestrator/orchestrator.ts
6
+ /** The orchestrator's run-timing and scheduling seams (src/orchestrator/orchestrator.ts
8
7
  * keeps the poll loop itself — the control flow that calls these). Each is exported as a
9
- * unit-test seam (test/orchestrator-seams.test.ts): timing a semaphore-gated tick, an
8
+ * unit-test seam (test/orchestrator-seams.test.ts): timing a semaphore-gated run, an
10
9
  * abort-interruptible sleep, and the restart hand-off's bounded wait on the in-flight merge.
11
10
  * Split out of orchestrator.ts — which had grown into both the poll loop and the timing helpers
12
11
  * it schedules with — so the loop reads as control flow over these named steps. The team-health
13
12
  * polls (the team hold, the error-storm warning, the failure-spread alarm) are their own family
14
13
  * in src/team/team-polls.ts. */
15
- /** How many completed role-tick samples the p75 needs before it is trusted as the drain window.
14
+ /** How many completed role-run samples the p75 needs before it is trusted as the drain window.
16
15
  * Below this the orchestrator reports no p75 and poll keeps the cold-start constant. */
17
16
  const DRAIN_P75_MIN_SAMPLES = 10;
18
- /** The p75 of completed role-tick durations (ms), or null when there are too few samples to
19
- * trust. The orchestrator's half of the adaptive drain window (BUGS.md 2026-09-18): a tick that
17
+ /** The p75 of completed role-run durations (ms), or null when there are too few samples to
18
+ * trust. The orchestrator's half of the adaptive drain window (BUGS.md 2026-09-18): a run that
20
19
  * finishes inside it is waited for, a longer one is aborted resumably. The samples come from
21
- * runTimedRoleTick above; the consumer is the redeploy policy's InFlightCounts.roleTickP75Ms.
20
+ * runTimedRoleRun above; the consumer is the redeploy policy's InFlightCounts.roleRunP75Ms.
22
21
  * Exported for its unit test; the p75 is the statistic the bug's expected fix names. */
23
- export function p75TickDurationMs(durations) {
22
+ export function p75RunDurationMs(durations) {
24
23
  if (durations.length < DRAIN_P75_MIN_SAMPLES)
25
24
  return null;
26
25
  const sorted = [...durations].sort((a, b) => a - b);
@@ -29,8 +28,8 @@ export function p75TickDurationMs(durations) {
29
28
  /** How long a restart's hand-off waits on an in-flight merge, per phase: one window for it to
30
29
  * finish on its own, then — having aborted it — one more for the step it was in to end
31
30
  * (awaitMergeForHandoff; BUGS.md 2026-09-23). By the time a restart reaches its `finally` the new
32
- * build is already in dist/ and every role tick is done or aborted, so the merge is all the team
33
- * is still running: each minute spent on it is a minute nothing else ticks. A merge's unbounded
31
+ * build is already in dist/ and every role run is done or aborted, so the merge is all the team
32
+ * is still running: each minute spent on it is a minute nothing else runs. A merge's unbounded
34
33
  * part is model work — its reviewer run (and, until 2026-09-24, a gate build-fix run: the
35
34
  * 2026-09-23 one held the slot for 4 h 35 m and that day's hand-off sat through 97 minutes of it).
36
35
  * Its deterministic part is bounded: at most one build check (BUILD_CHECK_TIMEOUT_MS) plus git
@@ -38,35 +37,35 @@ export function p75TickDurationMs(durations) {
38
37
  * last check and fast-forward — merge, and gives an aborted merge's current step (a check, which
39
38
  * no abort reaches) time to end, so the hand-off does not leave a check running in a merger
40
39
  * worktree the next generation is about to reset. Not the drain's window: that is the p75 of whole
41
- * role ticks (over an hour on this team) — the very lag this bounds. */
40
+ * role runs (over an hour on this team) — the very lag this bounds. */
42
41
  export const HANDOFF_MERGE_WINDOW_MS = BUILD_CHECK_TIMEOUT_MS + 60_000;
43
- /** Run one role tick under the concurrency semaphore and return how long the tick itself ran,
42
+ /** Run one role run under the concurrency semaphore and return how long the run itself ran,
44
43
  * in ms — null when it never started (the harness is already stopping, or `held`) or was cut
45
44
  * off by an abort. This is the restart drain's p75 sample (BUGS.md 2026-09-18). The clock starts
46
- * only once the permit is granted, so time a tick spends parked in the semaphore queue is not
47
- * counted as work: the drain waits on ticks that are already running, and folding queue wait
45
+ * only once the permit is granted, so time a run spends parked in the semaphore queue is not
46
+ * counted as work: the drain waits on runs that are already running, and folding queue wait
48
47
  * into the window would overstate how long they have left (the `run_start`..`run_end` span
49
- * the window was sized against excludes it too). Aborted ticks return null so their short
50
- * cut-off lengths cannot drag the window down. So do ticks that ended with nothing to show
48
+ * the window was sized against excludes it too). Aborted runs return null so their short
49
+ * cut-off lengths cannot drag the window down. So do runs that ended with nothing to show
51
50
  * (`no_change`, `skipped`): a `main moved` re-check runs the model and still finishes in
52
51
  * seconds, and sampling it equally let a team's window collapse to the re-check scale and
53
52
  * cut real work off minutes into its run (BUGS.md 2026-09-30) — only a work-bearing outcome
54
53
  * says how long the work the drain waits on takes. `now` is a test seam.
55
54
  *
56
- * `held` is the team-wide hold on new ticks (today the restart drain's), re-checked at the one
57
- * moment a reserved tick actually begins: when its permit is granted. A tick scheduled before
55
+ * `held` is the team-wide hold on new runs (today the restart drain's), re-checked at the one
56
+ * moment a reserved run actually begins: when its permit is granted. A run scheduled before
58
57
  * the hold may have parked in the semaphore queue long before it; checking the hold only at
59
58
  * scheduling let every such waiter start a fresh pi run mid-drain as slots freed — the very
60
- * ticks the restart then aborted (BUGS.md 2026-09-23). A held tick releases its permit without
61
- * calling `tick`, so it writes no state and logs no run_start/run_end; the caller hands its
59
+ * runs the restart then aborted (BUGS.md 2026-09-23). A held run releases its permit without
60
+ * calling `run`, so it writes no state and logs no run_start/run_end; the caller hands its
62
61
  * reservation back. */
63
- export async function runTimedRoleTick(signal, acquire, release, tick, now = Date.now, held = () => false) {
62
+ export async function runTimedRoleRun(signal, acquire, release, run, now = Date.now, held = () => false) {
64
63
  await acquire();
65
64
  try {
66
65
  if (signal.aborted || held())
67
66
  return null;
68
67
  const startedAt = now();
69
- const outcome = await tick();
68
+ const outcome = await run();
70
69
  if (outcome.result === "aborted" || outcome.result === "user_aborted")
71
70
  return null;
72
71
  if (outcome.result === "no_change" || outcome.result === "skipped")
@@ -81,7 +80,7 @@ export async function runTimedRoleTick(signal, acquire, release, tick, now = Dat
81
80
  * abort) is prompt instead of waiting out the current poll cycle. The listener is removed
82
81
  * on either exit path so long-running orchestrators don't accumulate one per poll, and an
83
82
  * already-aborted signal returns synchronously through runOnAbort. Exported as a unit-test
84
- * seam, like runTimedRoleTick above. */
83
+ * seam, like runTimedRoleRun above. */
85
84
  export function sleepInterruptible(ms, signal) {
86
85
  return new Promise((resolve) => {
87
86
  const onAbort = () => {
@@ -126,7 +125,7 @@ function settlesWithin(promise, ms, after) {
126
125
  * swap by 97 minutes, in silence. The wait is announced as it starts (one warning naming the
127
126
  * merge's roles), so the feed says why `restarting onto build …` is not yet followed by
128
127
  * `orchestrator stopped`. Past `windowMs` the merge is stopped the way the drain gives up on role
129
- * ticks: `abort` fires the harness's internal stop, which kills its pi runs at once and makes it
128
+ * runs: `abort` fires the harness's internal stop, which kills its pi runs at once and makes it
130
129
  * stop at its next step boundary (merge-core.ts, merge-batch.ts, and merge-stack.ts check the
131
130
  * signal before every gate, every approved merge, and each of a stack's check attempts), and a
132
131
  * warning names what was still awaited. An aborted merge records `aborted` like any shutdown
@@ -134,24 +133,25 @@ function settlesWithin(promise, ms, after) {
134
133
  * new build. One still running a window after the abort (a step wedged past its own bound) is left
135
134
  * behind: its 4/5 marker is cleared here, since this process exits the moment the hand-off returns,
136
135
  * and its entries and pins survive exactly as a crash leaves them — the next generation's first
137
- * drain re-merges them. Exported as a unit-test seam, like runTimedRoleTick. */
136
+ * drain re-merges them. Its announcements are best-effort: an events feed that cannot be written
137
+ * does not shorten the wait. Exported as a unit-test seam, like runTimedRoleRun. */
138
138
  export async function awaitMergeForHandoff(root, merge, windowMs, abort, after = realAfterDelay) {
139
139
  const roles = merge.roles.join(", ");
140
140
  const deadline = `${windowMs / 1000}s`;
141
- warnEvent(root, TUMWATER_SOURCE, `restart waiting on the merge in progress for ${roles} (deadline ${deadline})`);
141
+ warnEventBestEffort(root, TUMWATER_SOURCE, `restart waiting on the merge in progress for ${roles} (deadline ${deadline})`);
142
142
  if (await settlesWithin(merge.promise, windowMs, after))
143
143
  return "finished";
144
144
  abort();
145
- warnEvent(root, TUMWATER_SOURCE, `restart: the merge for ${roles} outlived its ${deadline} deadline — aborted; its pinned commits survive for the next restart`);
145
+ warnEventBestEffort(root, TUMWATER_SOURCE, `restart: the merge for ${roles} outlived its ${deadline} deadline — aborted; its pinned commits survive for the next restart`);
146
146
  if (await settlesWithin(merge.promise, windowMs, after))
147
147
  return "aborted";
148
148
  removeQuiet(mergeStatePath(root));
149
- warnEvent(root, TUMWATER_SOURCE, `restart: the aborted merge for ${roles} was still running ${deadline} later — restarting without it; its queue entries and pinned commits survive for the next restart`);
149
+ warnEventBestEffort(root, TUMWATER_SOURCE, `restart: the aborted merge for ${roles} was still running ${deadline} later — restarting without it; its queue entries and pinned commits survive for the next restart`);
150
150
  return "abandoned";
151
151
  }
152
152
  /**
153
153
  * Drain the orchestrator's in-flight work at shutdown: wait out the reserved role and director
154
- * ticks, and await the merge pipeline's tasks beside them — and how depends on what comes
154
+ * runs, and await the merge pipeline's tasks beside them — and how depends on what comes
155
155
  * next. On an operator stop the harness signal has already aborted the merge — its pi runs die
156
156
  * and it stops at its next step boundary, ending "aborted" with its ref kept and its entry
157
157
  * dropped for next-start recovery — and it is waited out: returning early would remove
@@ -159,15 +159,16 @@ export async function awaitMergeForHandoff(root, merge, windowMs, abort, after =
159
159
  * concurrent merger (a second Ctrl+C still forces the exit). On a restart the caller exits for
160
160
  * the next generation the moment this returns, and a merge is not bounded by one reviewer run
161
161
  * — a merge's check follows the verifications' — so the wait is a bounded hand-off that
162
- * announces itself (awaitMergeForHandoff; BUGS.md 2026-09-23). "The merge" is every merge task
163
- * at once — each verification holding a permit, and the merge — waited on (and, past the hand-off's
164
- * window, aborted) together under the roles of them all. The approved changes waiting for the
165
- * merge have no task, and a verification still parked for its permit has started nothing (a
162
+ * announces itself (awaitMergeForHandoff; BUGS.md 2026-09-23). The announcements are best-effort:
163
+ * an events feed that cannot be written does not shorten the wait. "The merge" is every merge task
164
+ * at once — each verification holding a permit, and the merge — waited on (and, past the
165
+ * hand-off's window, aborted) together under the roles of them all. The approved changes waiting
166
+ * for the merge have no task, and a verification still parked for its permit has started nothing (a
166
167
  * shutdown settles it at once; a restart's closed start gate at its grant): all of them keep their
167
168
  * entries and pins for the next start.
168
169
  */
169
170
  export async function drainInFlightWork(root, roleInFlight, directorInFlight, inFlight, restart, handoffWindowMs, abort, after = realAfterDelay) {
170
- const ticks = Promise.allSettled([...roleInFlight, ...directorInFlight]);
171
+ const runs = Promise.allSettled([...roleInFlight, ...directorInFlight]);
171
172
  const merge = inFlight.length === 0
172
173
  ? null
173
174
  : {
@@ -179,18 +180,19 @@ export async function drainInFlightWork(root, roleInFlight, directorInFlight, in
179
180
  // wires the harness signal to its controller, and a conflict-resolution run watches the
180
181
  // harness signal alone (runMergePi). Nothing else it reaches can start work here — the
181
182
  // director has finished, permit holders were aborted at the restart, and a parked waiter
182
- // meets the closed start gate (tickStartHeld) whenever it is granted a permit.
183
+ // meets the closed start gate (runStartHeld) whenever it is granted a permit.
183
184
  const outcome = await awaitMergeForHandoff(root, merge, handoffWindowMs, abort, after);
184
185
  // An abandoned merge still holds its permit, so a waiter parked behind it would never be
185
- // granted one and never settle. The ticks have had both windows beside the hand-off, so
186
+ // granted one and never settle. The runs have had both windows beside the hand-off, so
186
187
  // whatever is still reserved then started nothing (or is wedged like the merge): the
187
188
  // hand-off goes ahead without it too.
188
189
  if (outcome !== "abandoned")
189
- await ticks;
190
+ await runs;
190
191
  }
191
192
  else {
192
- if (merge)
193
- warnEvent(root, TUMWATER_SOURCE, `shutdown waiting on the merge in progress for ${merge.roles.join(", ")}`);
194
- await Promise.allSettled([ticks, merge?.promise]);
193
+ if (merge) {
194
+ warnEventBestEffort(root, TUMWATER_SOURCE, `shutdown waiting on the merge in progress for ${merge.roles.join(", ")}`);
195
+ }
196
+ await Promise.allSettled([runs, merge?.promise]);
195
197
  }
196
198
  }