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,12 +1,25 @@
1
- // vocabulary: plain
2
- /** The dashboard's Pending view, browser-side: every loop's unmerged change as /api/diff
1
+ /** The dashboard's Pending view, browser-side: every role's unmerged change as /api/diff
3
2
  * serves it (the same document `tumwater diff` prints from the shared change collectors). The
4
3
  * roster shows each role's branch, state, commits ahead of main with their subjects, and its
5
- * uncommitted-file count; a row opens that loop's drawer, whose Pending change section fetches
4
+ * uncommitted-file count; a row opens that role's drawer, whose Pending change section fetches
6
5
  * the full patch (`/api/diff?role=<id>`). It refetches when the view opens and when its
7
- * Refresh button is pressed — never on a timer of its own. Spliced into gui-client.ts's
8
- * script, reaching its helpers (esc, getJson, icon, errorPanel) through that concatenation. */
6
+ * Refresh button is pressed — never on a timer of its own. It also paints the overlapping-files
7
+ * card from `/api/diff?overlaps=1`, with Role and Folder pickers that narrow it client-side,
8
+ * and the Merge queue card from the status payload, with a Role picker that keeps only the
9
+ * chosen role's waiting merges under their whole-queue positions (the tumwater queue --role
10
+ * twin). Spliced into gui-client.ts's script, reaching its helpers (esc, getJson, icon,
11
+ * errorPanel) through that concatenation. */
9
12
  export const GUI_CLIENT_PENDING_JS = String.raw ` let pendingTeam = null;
13
+ let pendingOverlaps = null;
14
+ let pendingOverlapOpen = null;
15
+ let pendingOverlapRole = "";
16
+ let pendingOverlapDir = "";
17
+ let pendingQueueRole = "";
18
+ let pendingCancelSha = "";
19
+ // The Merge queue card's per-row cancel, armed by a first click and fired by a second within
20
+ // 5 s — the same confirmSlot two-click factory the drawer's note-clear/entry-cancel use, so a
21
+ // stray click cannot remove an entry. Repaints the card (an armed row reads Confirm cancel).
22
+ const queueCancelSlot = confirmSlot(() => renderPendingQueue(lastStatus));
10
23
  // pending-table:start
11
24
  // One role's state cell: the collector's three states, with "ready" split into pending vs
12
25
  // idle work. Never colors a degraded state as an active one.
@@ -30,7 +43,7 @@ export const GUI_CLIENT_PENDING_JS = String.raw ` let pendingTeam = null;
30
43
  return "<span class='num' title='" + esc(files.join(", ")) + "'>" + esc(plural(files.length, "file")) + "</span>";
31
44
  }
32
45
  // The Pending roster: one clickable row per known role (the collector's config order), each
33
- // opening that loop's drawer. Pure — the view paints what this returns.
46
+ // opening that role's drawer. Pure — the view paints what this returns.
34
47
  function pendingTableHtml(team) {
35
48
  if (!team || !Array.isArray(team.roles) || team.roles.length === 0)
36
49
  return "<div class='empty'><strong>No roles yet</strong>Roles appear here once the team is configured.</div>";
@@ -44,37 +57,142 @@ export const GUI_CLIENT_PENDING_JS = String.raw ` let pendingTeam = null;
44
57
  "<td>" + pendingAheadHtml(r) + "</td>" +
45
58
  "<td class='num'>" + pendingDirtyHtml(r) + "</td></tr>",
46
59
  ).join("");
47
- return "<div class='table-wrap'><table class='table pending'><thead><tr><th class='c-loop'>Role</th><th>Branch</th>" + // vocabulary-ok: CSS class the stylesheet matches
60
+ return "<div class='table-wrap'><table class='table pending'><thead><tr><th class='c-role'>Role</th><th>Branch</th>" +
48
61
  "<th>State</th><th>Ahead of " + esc(team.mainBranch) + "</th><th class='c-dirty num'>Uncommitted</th></tr></thead><tbody>" + rows + "</tbody></table></div>";
49
62
  }
63
+ // One queued merge's line: the 1-based whole-queue position, role, run, short sha, age, an
64
+ // optional merge label, and summary. Shared by the Merge queue card and the role drawer so
65
+ // the two cannot drift; a malformed entry renders stand-ins rather than undefined/NaN.
66
+ function queueEntryLineHtml(e, position, now, label, cancel) {
67
+ const sec = Number.isFinite(e.enqueuedAt) ? Math.max(0, Math.round((now - e.enqueuedAt) / 1000)) : 0;
68
+ const run = Number.isFinite(e.run) ? e.run : "?";
69
+ const note = Number.isFinite(e.revisionRound) ? " · revision " + e.revisionRound + (Number.isFinite(e.priorObjections) && e.priorObjections > 0 ? " · " + plural(e.priorObjections, "prior objection") : "") : "";
70
+ const retry = typeof e.retry === "string" ? " · re-queued after " + e.retry + (Number.isFinite(e.notBefore) && e.notBefore > now ? " · eligible in " + humanSeconds(Math.max(0, Math.round((e.notBefore - now) / 1000))) : "") : "";
71
+ const line = "<div class='mono clamp1' style='padding:3px 0'>" + position + ". " + esc(e.role ? roleLabel(e.role) : "?") +
72
+ " run #" + esc(run) + " · " + esc(shortSha(e.sha)) + " · " + esc(humanSeconds(sec)) +
73
+ " ago" + (label && typeof label === "string" ? " · [" + esc(label) + "]" : "") +
74
+ retry +
75
+ note +
76
+ " · " + esc(e.summary || "—") + "</div>";
77
+ // A two-click cancel: armed by the first click, fired by the second within 5 s (the
78
+ // confirmSlot guard), so a stray click cannot remove an entry. A malformed entry carries
79
+ // no sha, so it renders no button.
80
+ if (cancel && typeof cancel.sha === "string") {
81
+ return line + "<button type='button' class='btn btn-sm' data-cancel='" + esc(cancel.sha) + "'>" + (cancel.armed ? "Confirm cancel" : "Cancel") + "</button>";
82
+ }
83
+ return line;
84
+ }
85
+ // The role drawer's Merge queue section: only this role's waiting merges, each under its
86
+ // whole-queue position (labels run parallel to the whole queue). "" when it has none.
87
+ function drawerQueueHtml(mergeQueue, role, now, labels) {
88
+ const entries = mergeQueue && Array.isArray(mergeQueue.entries) ? mergeQueue.entries : [];
89
+ let kept = 0;
90
+ const lines = entries.map((e, i) => {
91
+ if (!e || e.role !== role) return "";
92
+ kept++;
93
+ return queueEntryLineHtml(e, i + 1, now, Array.isArray(labels) ? labels[i] : undefined);
94
+ }).join("");
95
+ if (!kept) return "";
96
+ return "<section><div class='sec-head'><h3>Merge queue</h3><span class='muted' style='font-size:13px'>" +
97
+ esc(kept + " of " + entries.length + " waiting") + "</span></div><div style='padding:4px 0'>" + lines + "</div></section>";
98
+ }
50
99
  // The Merge queue card's body: every queued merge in execution order (oldest first), one
51
- // line each mirroring tumwater queue's renderMergeQueue — 1-based position, loop, tick,
100
+ // line each mirroring tumwater queue's renderMergeQueue — 1-based position, role, run,
52
101
  // short sha, age, summary — so the page and the CLI cannot drift. Painted from the status
53
102
  // payload the page already polls; depth counts the unreadable queue files the status reader
54
103
  // skips, so a positive depth with no readable list states the count instead of inventing rows.
55
104
  // Pure: the age comes from the passed now, and a malformed entry renders a stand-in rather
56
- // than undefined/NaN. Null/absent/zero-depth reads as empty.
57
- function pendingQueueHtml(mergeQueue, now, labels) {
105
+ // than undefined/NaN. Null/absent/zero-depth reads as empty. Its Role picker keeps only the
106
+ // chosen role's waiting merges, each under its position in the whole queue (the
107
+ // tumwater queue --role twin); "All roles" (the default) shows every line.
108
+ function pendingQueueHtml(mergeQueue, now, labels, role = "", cancelArmedSha = "") {
58
109
  const depth = mergeQueue && Number.isFinite(mergeQueue.depth) ? mergeQueue.depth : 0;
59
110
  const entries = mergeQueue && Array.isArray(mergeQueue.entries) ? mergeQueue.entries : [];
111
+ const roles = [...new Set(entries.map((e) => e && e.role).filter((r) => typeof r === "string"))].sort();
112
+ const active = role && roles.includes(role) ? role : "";
113
+ let head = "<div class='card-head'><h2>Merge queue</h2></div>";
60
114
  let body;
61
115
  if (depth === 0) {
62
116
  body = "<div class='empty'>Merge queue empty</div>";
63
117
  } else if (entries.length === 0) {
64
118
  body = "<div class='empty'>merge queue: " + esc(depth) + " waiting</div>";
65
119
  } else {
66
- body = "<div style='padding:10px 16px 12px'>" + entries.map((e, i) => {
67
- const sec = Number.isFinite(e.enqueuedAt) ? Math.max(0, Math.round((now - e.enqueuedAt) / 1000)) : 0;
68
- const run = Number.isFinite(e.run) ? e.run : "?";
69
- const note = Number.isFinite(e.revisionRound) ? " · revision " + e.revisionRound + (Number.isFinite(e.priorObjections) && e.priorObjections > 0 ? " · " + plural(e.priorObjections, "prior objection") : "") : "";
70
- return "<div class='mono clamp1' style='padding:3px 0'>" + (i + 1) + ". " + esc(e.role ? roleLabel(e.role) : "?") +
71
- " run #" + esc(run) + " · " + esc(shortSha(e.sha)) + " · " + esc(humanSeconds(sec)) +
72
- " ago" + (Array.isArray(labels) && labels[i] && typeof labels[i] === "string" ? " · [" + esc(labels[i]) + "]" : "") +
73
- note +
74
- " · " + esc(e.summary || "—") + "</div>";
75
- }).join("") + "</div>";
120
+ if (roles.length) {
121
+ head = "<div class='card-head'><h2>Merge queue</h2><label><span class='sr-only'>Role</span><select class='field' id='pendingqueuerole'><option value=''>All roles</option>" +
122
+ roles.map((id) => "<option value='" + esc(id) + "'" + (id === active ? " selected" : "") + ">" + esc(roleLabel(id)) + "</option>").join("") + "</select></label></div>";
123
+ }
124
+ body = "<div style='padding:10px 16px 12px'>" + entries.map((e, i) => ({ e, i })).filter(({ e }) => !active || (e && e.role === active)).map(({ e, i }) =>
125
+ queueEntryLineHtml(e, i + 1, now, Array.isArray(labels) ? labels[i] : undefined, { sha: e && e.sha, armed: e && e.sha === cancelArmedSha })).join("") + "</div>";
126
+ }
127
+ return head + body;
128
+ }
129
+ // The Overlapping files card: one "path: role, role" line per file two or more pending changes
130
+ // touch (tumwater diff --overlaps). Pure; a malformed document reads as no overlaps. Its Role
131
+ // picker keeps only the lines the chosen role holds (diff --overlaps --role), each line still
132
+ // naming every holder and keeping its index in the unfiltered list. Its Folder picker keeps only
133
+ // the lines at or under the chosen directory (diff --overlaps --path).
134
+ // The expanded detail under one overlap line: each holding role's path-scoped change, or its
135
+ // loading / error row. Pure.
136
+ function overlapDetailHtml(rows) {
137
+ return (Array.isArray(rows) ? rows : []).map((r) => {
138
+ let body;
139
+ if (r.state === "loading") body = "<p class='muted'>Loading…</p>";
140
+ else if (r.state === "error") body = "<p class='muted'>" + esc(r.message || "Pending change unavailable") + "</p>";
141
+ else body = drawerChangeHtml(r.view);
142
+ return "<h3 class='mono'>" + esc(roleLabel(r.role)) + "</h3>" + body;
143
+ }).join("");
144
+ }
145
+ // The sorted, de-duplicated role ids any overlap names; empty for a malformed document.
146
+ function pendingOverlapHolders(view) {
147
+ if (!view || typeof view !== "object" || !Array.isArray(view.overlaps)) return [];
148
+ const ids = new Set();
149
+ for (const o of view.overlaps) {
150
+ if (o && Array.isArray(o.roles)) for (const id of o.roles) if (typeof id === "string") ids.add(id);
151
+ }
152
+ return [...ids].sort();
153
+ }
154
+ // The sorted, de-duplicated directories holding any overlap path; empty for a malformed
155
+ // document. A trailing-slash path (an untracked directory) also adds itself.
156
+ function pendingOverlapDirs(view) {
157
+ if (!view || typeof view !== "object" || !Array.isArray(view.overlaps)) return [];
158
+ const dirs = new Set();
159
+ for (const o of view.overlaps) {
160
+ if (!o || typeof o.path !== "string") continue;
161
+ const dir = o.path.endsWith("/");
162
+ const parts = (dir ? o.path.slice(0, -1) : o.path).split("/");
163
+ const n = dir ? parts.length : parts.length - 1;
164
+ for (let k = 1; k <= n; k++) dirs.add(parts.slice(0, k).join("/"));
165
+ }
166
+ return [...dirs].sort();
167
+ }
168
+ function pendingOverlapsHtml(view, open, role, dir) {
169
+ const ok = view && typeof view === "object";
170
+ let body;
171
+ let head = "<div class='card-head'><h2>Overlapping files</h2></div>";
172
+ if (ok && view.state === "no-base") {
173
+ body = "<div class='empty'>Main branch " + esc(view.mainBranch) + " does not exist</div>";
174
+ } else if (!ok || !Array.isArray(view.overlaps) || view.overlaps.length === 0) {
175
+ body = "<div class='empty'>No file is touched by two pending changes</div>";
176
+ } else {
177
+ const holders = pendingOverlapHolders(view);
178
+ const active = role && holders.includes(role) ? role : "";
179
+ const dirs = pendingOverlapDirs(view);
180
+ const activeDir = dir && dirs.includes(dir) ? dir : "";
181
+ head = "<div class='card-head'><h2>Overlapping files</h2><label><span class='sr-only'>Role</span><select class='field' id='pendingoverlaprole'><option value=''>All roles</option>" +
182
+ holders.map((id) => "<option value='" + esc(id) + "'" + (id === active ? " selected" : "") + ">" + esc(roleLabel(id)) + "</option>").join("") + "</select></label>" +
183
+ (dirs.length ? "<label><span class='sr-only'>Folder</span><select class='field' id='pendingoverlapdir'><option value=''>All folders</option>" +
184
+ dirs.map((d) => "<option value='" + esc(d) + "'" + (d === activeDir ? " selected" : "") + ">" + esc(d + "/") + "</option>").join("") + "</select></label>" : "") + "</div>";
185
+ const lines = view.overlaps.map((o, i) => {
186
+ const roles = o && Array.isArray(o.roles) ? o.roles : [];
187
+ if (active && !roles.includes(active)) return "";
188
+ const path = o && typeof o.path === "string" ? o.path : "?";
189
+ if (activeDir && !path.startsWith(activeDir + "/")) return "";
190
+ const line = "<button type='button' class='li-open mono clamp1' data-overlap='" + i + "'>" + esc(path) + ": " + esc(roles.map(roleLabel).join(", ")) + "</button>";
191
+ return open && open.path === path ? line + "<div class='overlap-detail'>" + overlapDetailHtml(open.rows) + "</div>" : line;
192
+ }).join("");
193
+ body = lines ? "<div style='padding:10px 16px 12px'>" + lines + "</div>" : "<div class='empty'>No overlapping file matches the filters</div>";
76
194
  }
77
- return "<div class='card-head'><h2>Merge queue</h2></div>" + body;
195
+ return head + body;
78
196
  }
79
197
  // pending-table:end
80
198
  // Paint the Merge queue card from the polled status payload, at paint-time age; no fetch of
@@ -82,19 +200,105 @@ export const GUI_CLIENT_PENDING_JS = String.raw ` let pendingTeam = null;
82
200
  function renderPendingQueue(status) {
83
201
  const card = $("pendingqueue");
84
202
  if (!card) return;
85
- card.innerHTML = pendingQueueHtml(status && status.mergeQueue, Date.now(), status && status.mergeQueueLabels);
203
+ const entries = status && status.mergeQueue && Array.isArray(status.mergeQueue.entries) ? status.mergeQueue.entries : [];
204
+ const roles = new Set(entries.map((e) => e && e.role).filter((r) => typeof r === "string"));
205
+ if (!roles.has(pendingQueueRole)) pendingQueueRole = "";
206
+ card.innerHTML = pendingQueueHtml(status && status.mergeQueue, Date.now(), status && status.mergeQueueLabels, pendingQueueRole, queueCancelSlot.armed(pendingCancelSha) ? pendingCancelSha : "");
207
+ }
208
+ function paintPendingOverlaps() {
209
+ const card = $("pendingoverlaps");
210
+ if (card) card.innerHTML = pendingOverlapsHtml(pendingOverlaps, pendingOverlapOpen, pendingOverlapRole, pendingOverlapDir);
211
+ }
212
+ // Toggle one overlap line's detail: fetch each holding role's change scoped to the path. A
213
+ // reply is dropped once another line (or a Refresh) replaced the open detail.
214
+ async function togglePendingOverlap(index) {
215
+ const o = pendingOverlaps && Array.isArray(pendingOverlaps.overlaps) ? pendingOverlaps.overlaps[index] : null;
216
+ if (!o || typeof o.path !== "string") return;
217
+ const path = o.path;
218
+ if (pendingOverlapOpen && pendingOverlapOpen.path === path) {
219
+ pendingOverlapOpen = null;
220
+ paintPendingOverlaps();
221
+ return;
222
+ }
223
+ const roles = Array.isArray(o.roles) ? o.roles : [];
224
+ const open = { path, rows: roles.map((role) => ({ role, state: "loading" })) };
225
+ pendingOverlapOpen = open;
226
+ paintPendingOverlaps();
227
+ await Promise.all(roles.map(async (role, k) => {
228
+ let row;
229
+ try {
230
+ const view = await getJson("/api/diff?role=" + encodeURIComponent(role) + "&path=" + encodeURIComponent(path));
231
+ row = { role, state: "ok", view };
232
+ } catch (err) {
233
+ row = { role, state: "error", message: err && err.message ? err.message : "Pending change unavailable" };
234
+ }
235
+ if (pendingOverlapOpen !== open) return;
236
+ open.rows[k] = row;
237
+ paintPendingOverlaps();
238
+ }));
86
239
  }
87
240
  function buildPendingView() {
88
241
  if (!buildOnce("pending", "<div class='view-head'><div><h1>Pending</h1><p>Every role's unmerged change — its branch, commits ahead of main, and uncommitted files. Open a row for the full diff.</p></div>" +
89
242
  "<div class='toolbar'><button type='button' class='btn btn-sm' id='pendingrefresh'>" + icon("refresh") + "Refresh</button></div></div>" +
90
243
  "<div class='card' id='pendingqueue'><div class='empty'>Loading…</div></div>" +
244
+ "<div class='card' id='pendingoverlaps'><div class='empty'>Loading…</div></div>" +
91
245
  "<div class='card' id='pendingbody'><div class='empty'>Loading…</div></div>")) return;
92
246
  $("pendingrefresh").addEventListener("click", () => fetchPending());
247
+ // A Merge queue row's Cancel: the first click on an unarmed sha arms it (repainting the
248
+ // button as Confirm cancel), a second on the armed sha within 5 s posts /api/queue-cancel
249
+ // then clears the arm and refetches so the removed row disappears at once.
250
+ $("pendingqueue").addEventListener("click", async (e) => {
251
+ const b = e.target.closest("[data-cancel]");
252
+ if (!b) return;
253
+ const sha = b.getAttribute("data-cancel");
254
+ if (!sha) return;
255
+ if (queueCancelSlot.armed(sha)) {
256
+ await postAction("/api/queue-cancel", { sha }, (d) => d && d.status === "cancelled" ? "cancelled the merge of " + shortSha(d.commit) : "that merge already left the queue");
257
+ pendingCancelSha = "";
258
+ queueCancelSlot.disarm();
259
+ fetchPending();
260
+ } else {
261
+ pendingCancelSha = sha;
262
+ queueCancelSlot.arm(sha);
263
+ }
264
+ });
265
+ $("pendingqueue").addEventListener("change", (e) => {
266
+ if (e.target.id === "pendingqueuerole") {
267
+ pendingQueueRole = e.target.value;
268
+ renderPendingQueue(lastStatus);
269
+ }
270
+ });
271
+ $("pendingoverlaps").addEventListener("click", (e) => {
272
+ const b = e.target.closest("[data-overlap]");
273
+ if (b) togglePendingOverlap(Number(b.dataset.overlap));
274
+ });
275
+ $("pendingoverlaps").addEventListener("change", (e) => {
276
+ if (e.target.id === "pendingoverlapdir") pendingOverlapDir = e.target.value;
277
+ else if (e.target.id === "pendingoverlaprole") pendingOverlapRole = e.target.value;
278
+ else return;
279
+ pendingOverlapOpen = null;
280
+ paintPendingOverlaps();
281
+ });
93
282
  }
94
283
  // Load the team-wide change roster. Called when the view opens and by the Refresh button;
95
- // no timer of its own, and the roster only changes when a loop merges or edits its worktree.
284
+ // no timer of its own, and the roster only changes when a role merges or edits its worktree.
285
+ // Load the overlapping-files card on its own, so neither read can blank the other's card.
286
+ async function fetchPendingOverlaps() {
287
+ pendingOverlapOpen = null;
288
+ try {
289
+ pendingOverlaps = await getJson("/api/diff?overlaps=1");
290
+ } catch (err) {
291
+ const card = $("pendingoverlaps");
292
+ if (card) card.innerHTML = errorPanel("Overlaps unavailable", err);
293
+ return;
294
+ }
295
+ if (!pendingOverlapHolders(pendingOverlaps).includes(pendingOverlapRole)) pendingOverlapRole = "";
296
+ if (!pendingOverlapDirs(pendingOverlaps).includes(pendingOverlapDir)) pendingOverlapDir = "";
297
+ paintPendingOverlaps();
298
+ }
96
299
  async function fetchPending() {
97
300
  buildPendingView();
301
+ fetchPendingOverlaps();
98
302
  try {
99
303
  pendingTeam = await getJson("/api/diff");
100
304
  } catch (err) {