tumwater 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/README.md +27 -13
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +8 -9
  4. package/dist/src/agent/agent-backend.js +8 -6
  5. package/dist/src/agent/backend-traits.js +55 -25
  6. package/dist/src/agent/claude/claude-account.js +71 -204
  7. package/dist/src/agent/claude/claude-args.js +9 -6
  8. package/dist/src/agent/claude/claude-backend.js +10 -10
  9. package/dist/src/agent/claude/claude-env.js +1 -1
  10. package/dist/src/agent/claude/claude-stream.js +6 -4
  11. package/dist/src/agent/claude/claude-write-guard.js +49 -0
  12. package/dist/src/agent/claude/write-guard-hook.js +13 -0
  13. package/dist/src/agent/codex/codex-account.js +119 -0
  14. package/dist/src/agent/codex/codex-args.js +181 -0
  15. package/dist/src/agent/codex/codex-backend.js +279 -0
  16. package/dist/src/agent/codex/codex-catalog.js +97 -0
  17. package/dist/src/agent/codex/codex-env.js +18 -0
  18. package/dist/src/agent/codex/codex-hook-trust.js +246 -0
  19. package/dist/src/agent/codex/codex-patch-paths.js +36 -0
  20. package/dist/src/agent/codex/codex-rollout.js +179 -0
  21. package/dist/src/agent/codex/codex-stream.js +404 -0
  22. package/dist/src/agent/codex/codex-write-guard.js +86 -0
  23. package/dist/src/agent/codex/write-guard-hook.js +12 -0
  24. package/dist/src/agent/image-input.js +29 -24
  25. package/dist/src/agent/notes-shim.js +11 -12
  26. package/dist/src/agent/opencode/opencode-args.js +0 -1
  27. package/dist/src/agent/opencode/opencode-backend.js +12 -9
  28. package/dist/src/agent/opencode/opencode-catalog.js +55 -29
  29. package/dist/src/agent/opencode/opencode-config.js +19 -14
  30. package/dist/src/agent/opencode/opencode-env.js +0 -1
  31. package/dist/src/agent/opencode/opencode-session-id.js +1 -2
  32. package/dist/src/agent/opencode/opencode-stream.js +1 -1
  33. package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
  34. package/dist/src/agent/role-notes-write.js +5 -6
  35. package/dist/src/agent/run-agent.js +465 -448
  36. package/dist/src/agent/run-settle.js +214 -0
  37. package/dist/src/agent/session-ledger.js +86 -16
  38. package/dist/src/agent/subscription-account.js +220 -0
  39. package/dist/src/agent/write-guard.js +148 -0
  40. package/dist/src/backlog/backlog-archive.js +281 -55
  41. package/dist/src/backlog/backlog-cli-view.js +255 -0
  42. package/dist/src/backlog/backlog-cli.js +91 -335
  43. package/dist/src/backlog/backlog-commit.js +20 -13
  44. package/dist/src/backlog/backlog-eligibility.js +193 -242
  45. package/dist/src/backlog/backlog-index.js +149 -0
  46. package/dist/src/backlog/backlog-md.js +54 -6
  47. package/dist/src/backlog/backlog-parts.js +264 -0
  48. package/dist/src/backlog/backlog-release.js +134 -0
  49. package/dist/src/backlog/backlog-render.js +27 -0
  50. package/dist/src/backlog/backlog-structure.js +99 -156
  51. package/dist/src/backlog/backlog-write.js +40 -127
  52. package/dist/src/backlog/backlog.js +21 -6
  53. package/dist/src/backlog/question-answer.js +21 -15
  54. package/dist/src/baseline/main-baseline.js +89 -20
  55. package/dist/src/baseline/main-guardrails.js +136 -0
  56. package/dist/src/baseline/main-red.js +153 -36
  57. package/dist/src/baseline/main-watch.js +232 -0
  58. package/dist/src/brief/brief-edit.js +12 -12
  59. package/dist/src/brief/brief.js +31 -51
  60. package/dist/src/budget/budget-fallback-view.js +288 -0
  61. package/dist/src/budget/budget-wiring.js +158 -0
  62. package/dist/src/budget/budget.js +31 -20
  63. package/dist/src/budget/fallback-breaker.js +26 -27
  64. package/dist/src/build/build-check-detect.js +23 -15
  65. package/dist/src/build/build-check-events.js +25 -6
  66. package/dist/src/build/build-check-report.js +49 -9
  67. package/dist/src/build/build-check-scoped.js +18 -11
  68. package/dist/src/build/build-check.js +2 -1
  69. package/dist/src/build/build-info.js +2 -3
  70. package/dist/src/build/build-stage.js +9 -14
  71. package/dist/src/build/dep-install.js +65 -3
  72. package/dist/src/change/change-data.js +123 -31
  73. package/dist/src/change/change-render.js +46 -5
  74. package/dist/src/cli/brief-command.js +3 -4
  75. package/dist/src/cli/cli-args.js +181 -33
  76. package/dist/src/cli/cli-command-args.js +46 -18
  77. package/dist/src/cli/cli-editor.js +2 -1
  78. package/dist/src/cli/cli-flag-specs.js +183 -45
  79. package/dist/src/cli/cli-init.js +51 -0
  80. package/dist/src/cli/cli-marker-commands.js +4 -4
  81. package/dist/src/cli/cli-query-commands.js +291 -598
  82. package/dist/src/cli/cli-repo-gate.js +11 -1
  83. package/dist/src/cli/cli-review-commands.js +113 -0
  84. package/dist/src/cli/cli-role-command.js +525 -0
  85. package/dist/src/cli/cli-start-summary.js +167 -0
  86. package/dist/src/cli/{cli-run.js → cli-start.js} +193 -237
  87. package/dist/src/cli/completion.js +169 -22
  88. package/dist/src/cli/config-commands.js +58 -26
  89. package/dist/src/cli/follow-view.js +75 -0
  90. package/dist/src/cli/help.js +258 -82
  91. package/dist/src/cli/log-commands.js +164 -85
  92. package/dist/src/cli/question-commands.js +211 -10
  93. package/dist/src/cli.js +173 -45
  94. package/dist/src/collections.js +56 -13
  95. package/dist/src/concurrency/check-permit.js +20 -3
  96. package/dist/src/concurrency/lock.js +17 -13
  97. package/dist/src/concurrency/provider-lease.js +44 -8
  98. package/dist/src/concurrency/semaphore.js +102 -26
  99. package/dist/src/config/config-aliases.js +0 -1
  100. package/dist/src/config/config-editable-keys.js +16 -1
  101. package/dist/src/config/config-fallbacks.js +94 -56
  102. package/dist/src/config/config-field-checks.js +40 -10
  103. package/dist/src/config/config-keys.js +30 -8
  104. package/dist/src/config/config-live.js +63 -24
  105. package/dist/src/config/config-request.js +137 -0
  106. package/dist/src/config/config-schema.js +31 -24
  107. package/dist/src/config/config-selector-checks.js +123 -0
  108. package/dist/src/config/config-validation-agents.js +80 -0
  109. package/dist/src/config/config-validation-model.js +116 -0
  110. package/dist/src/config/config-validation.js +169 -222
  111. package/dist/src/config/config-views.js +136 -54
  112. package/dist/src/config/config-write-core.js +135 -0
  113. package/dist/src/config/config-write.js +164 -265
  114. package/dist/src/config/config.js +53 -38
  115. package/dist/src/config/model-selector.js +34 -34
  116. package/dist/src/doctor/doctor-backlog-bugs.js +330 -0
  117. package/dist/src/doctor/doctor-backlog-plans.js +398 -0
  118. package/dist/src/doctor/doctor-backlog.js +249 -512
  119. package/dist/src/doctor/doctor-check-names.js +85 -0
  120. package/dist/src/doctor/doctor-checks.js +61 -593
  121. package/dist/src/doctor/doctor-claude-checks.js +142 -0
  122. package/dist/src/doctor/doctor-codex-checks.js +276 -0
  123. package/dist/src/doctor/doctor-contention-checks.js +506 -0
  124. package/dist/src/doctor/doctor-fallback-checks.js +130 -0
  125. package/dist/src/doctor/doctor-holds.js +80 -66
  126. package/dist/src/doctor/doctor-install.js +2 -1
  127. package/dist/src/doctor/doctor-model-checks.js +216 -537
  128. package/dist/src/doctor/doctor-notify.js +9 -5
  129. package/dist/src/doctor/doctor-opencode-checks.js +164 -0
  130. package/dist/src/doctor/doctor-orphans.js +182 -63
  131. package/dist/src/doctor/doctor-pi-checks.js +62 -0
  132. package/dist/src/doctor/doctor-project-checks.js +397 -0
  133. package/dist/src/doctor/doctor.js +126 -77
  134. package/dist/src/doctor/image-probe.js +21 -8
  135. package/dist/src/doctor/outcome.js +3 -2
  136. package/dist/src/doctor/probe.js +21 -12
  137. package/dist/src/events/event-format.js +185 -89
  138. package/dist/src/events/event-labels.js +11 -5
  139. package/dist/src/events/event-legacy.js +39 -15
  140. package/dist/src/events/event-read.js +52 -39
  141. package/dist/src/events/event-tally.js +16 -0
  142. package/dist/src/events/event-window.js +80 -26
  143. package/dist/src/events/events.js +30 -13
  144. package/dist/src/events/notify.js +7 -7
  145. package/dist/src/failure/failure-cluster.js +12 -12
  146. package/dist/src/failure/failure-data.js +65 -60
  147. package/dist/src/failure/failure-render.js +29 -21
  148. package/dist/src/failure/failure-state-change.js +17 -12
  149. package/dist/src/failure/time-spend.js +35 -35
  150. package/dist/src/files/file-queue.js +17 -0
  151. package/dist/src/files/files.js +111 -35
  152. package/dist/src/files/json-files.js +3 -3
  153. package/dist/src/files/json-object.js +28 -7
  154. package/dist/src/files/stat-cache.js +2 -2
  155. package/dist/src/files/tail.js +1 -1
  156. package/dist/src/gates/bootstrap-gates.js +4 -5
  157. package/dist/src/gates/budget-gates.js +88 -225
  158. package/dist/src/gates/disk-gate.js +1 -1
  159. package/dist/src/gates/gate-polls.js +101 -143
  160. package/dist/src/gates/maintenance-quota.js +37 -30
  161. package/dist/src/gates/pause-gates.js +28 -10
  162. package/dist/src/gates/readiness.js +7 -3
  163. package/dist/src/gates/role-cap-gates.js +8 -8
  164. package/dist/src/gates/startup-gate.js +94 -32
  165. package/dist/src/gates/streak-gate.js +54 -30
  166. package/dist/src/git/commit-message.js +89 -46
  167. package/dist/src/git/git-diff.js +16 -10
  168. package/dist/src/git/git-run.js +79 -38
  169. package/dist/src/git/git.js +54 -34
  170. package/dist/src/git/slot-salvage.js +253 -0
  171. package/dist/src/git/slots-state.js +6 -6
  172. package/dist/src/git/worktree-pool.js +132 -38
  173. package/dist/src/git/worktree-use.js +4 -11
  174. package/dist/src/git/worktree.js +94 -37
  175. package/dist/src/gui/gui-admin-endpoints.js +96 -0
  176. package/dist/src/gui/gui-args.js +79 -8
  177. package/dist/src/gui/gui-backlog-commands.js +43 -19
  178. package/dist/src/gui/gui-backlog-endpoints.js +156 -0
  179. package/dist/src/gui/gui-command.js +26 -13
  180. package/dist/src/gui/gui-config-commands.js +168 -80
  181. package/dist/src/gui/gui-endpoint-commands.js +5 -4
  182. package/dist/src/gui/gui-endpoint-common.js +4 -4
  183. package/dist/src/gui/gui-endpoints.js +11 -380
  184. package/dist/src/gui/gui-history-endpoints.js +301 -0
  185. package/dist/src/gui/gui-operator-commands.js +235 -13
  186. package/dist/src/gui/gui-prompt-commands.js +26 -20
  187. package/dist/src/gui/gui-report-endpoints.js +97 -0
  188. package/dist/src/gui/gui-server.js +26 -5
  189. package/dist/src/gui/gui-team-commands.js +7 -7
  190. package/dist/src/gui/http-body.js +1 -1
  191. package/dist/src/history/history-data.js +122 -234
  192. package/dist/src/history/history-queued.js +155 -0
  193. package/dist/src/history/history-render.js +42 -22
  194. package/dist/src/history/history.js +190 -49
  195. package/dist/src/inbox/inbox-attachments.js +29 -30
  196. package/dist/src/inbox/inbox-cancel.js +10 -11
  197. package/dist/src/inbox/inbox-edit.js +9 -9
  198. package/dist/src/inbox/inbox-submit.js +18 -18
  199. package/dist/src/inbox/inbox-top.js +28 -17
  200. package/dist/src/inbox/inbox.js +42 -43
  201. package/dist/src/inbox/pending-prompt.js +26 -26
  202. package/dist/src/inbox/prompt-commands.js +62 -49
  203. package/dist/src/inbox/prompt-not-before.js +2 -2
  204. package/dist/src/init/init-templates.js +1 -2
  205. package/dist/src/init/init.js +28 -23
  206. package/dist/src/merge/backlog-conflicts.js +215 -35
  207. package/dist/src/merge/merge-batch.js +14 -8
  208. package/dist/src/merge/merge-cancel.js +53 -0
  209. package/dist/src/merge/merge-check-failures.js +44 -22
  210. package/dist/src/merge/merge-core.js +58 -32
  211. package/dist/src/merge/merge-diff.js +5 -4
  212. package/dist/src/merge/merge-drain.js +45 -19
  213. package/dist/src/merge/merge-git.js +177 -38
  214. package/dist/src/merge/merge-pipeline.js +15 -3
  215. package/dist/src/merge/merge-post-review.js +214 -0
  216. package/dist/src/merge/merge-queue-render.js +48 -11
  217. package/dist/src/merge/merge-queue.js +52 -15
  218. package/dist/src/merge/merge-requeue.js +103 -0
  219. package/dist/src/merge/merge-slot.js +61 -16
  220. package/dist/src/merge/merge-stack.js +27 -11
  221. package/dist/src/merge/merge-to-main.js +77 -26
  222. package/dist/src/merge/merge-top.js +83 -0
  223. package/dist/src/merge/merge-verify.js +75 -18
  224. package/dist/src/notes-cli.js +35 -15
  225. package/dist/src/operator/operator-commands.js +192 -81
  226. package/dist/src/operator/operator-intent.js +69 -42
  227. package/dist/src/operator/operator-requests.js +35 -40
  228. package/dist/src/operator/retire.js +5 -6
  229. package/dist/src/operator/revert.js +142 -34
  230. package/dist/src/orchestrator/orchestrator-launch.js +60 -52
  231. package/dist/src/orchestrator/orchestrator-poll.js +352 -0
  232. package/dist/src/orchestrator/orchestrator-scheduling.js +71 -370
  233. package/dist/src/orchestrator/orchestrator.js +129 -369
  234. package/dist/src/orchestrator/retention.js +42 -8
  235. package/dist/src/orchestrator/scheduling-admit.js +116 -0
  236. package/dist/src/orchestrator/scheduling-scope.js +245 -0
  237. package/dist/src/orchestrator/scheduling-skip.js +195 -0
  238. package/dist/src/orchestrator/startup-migration-steps.js +8 -7
  239. package/dist/src/orchestrator/startup-migrations.js +11 -7
  240. package/dist/src/paths.js +126 -49
  241. package/dist/src/pi/command-shape.js +12 -1
  242. package/dist/src/pi/pi-args.js +6 -4
  243. package/dist/src/pi/pi-backend.js +6 -6
  244. package/dist/src/pi/pi-bin.js +54 -63
  245. package/dist/src/pi/pi-listed-models.js +53 -0
  246. package/dist/src/pi/pi-models.js +63 -30
  247. package/dist/src/pi/pi-run-result.js +1 -1
  248. package/dist/src/pi/pi-settings.js +1 -1
  249. package/dist/src/pi/pi-stream.js +6 -6
  250. package/dist/src/pi/pi-watchdogs.js +18 -19
  251. package/dist/src/pi-extension/bounded-output.js +5 -3
  252. package/dist/src/pi-extension/context-budget.js +2 -2
  253. package/dist/src/pi-extension/context-shake.js +1 -1
  254. package/dist/src/pi-extension/context-window.js +0 -1
  255. package/dist/src/pi-extension/full-output.js +3 -10
  256. package/dist/src/pi-extension/strip-thinking.js +0 -1
  257. package/dist/src/pi-extension/trim-system-prompt.js +3 -4
  258. package/dist/src/pi-extension/write-guard.js +37 -0
  259. package/dist/src/process/launch-services.js +1 -2
  260. package/dist/src/process/node-bin.js +16 -0
  261. package/dist/src/process/process-group.js +8 -18
  262. package/dist/src/process/process.js +41 -2
  263. package/dist/src/process/run-marker.js +26 -6
  264. package/dist/src/process/supervisor.js +22 -21
  265. package/dist/src/prompt/dialect.js +17 -2
  266. package/dist/src/prompt/gate-notes.js +203 -0
  267. package/dist/src/prompt/gate-prompts.js +150 -217
  268. package/dist/src/prompt/notes-backlog.js +184 -0
  269. package/dist/src/prompt/principles.js +1 -2
  270. package/dist/src/prompt/prompt-followup.js +19 -14
  271. package/dist/src/prompt/prompt.js +131 -204
  272. package/dist/src/redeploy/redeploy-policy.js +24 -19
  273. package/dist/src/redeploy/redeploy-probes.js +1 -1
  274. package/dist/src/redeploy/redeploy.js +2 -3
  275. package/dist/src/redeploy/redeployer.js +20 -20
  276. package/dist/src/redeploy/self-reload.js +2 -1
  277. package/dist/src/report/report-data.js +188 -124
  278. package/dist/src/report/report-fold.js +130 -54
  279. package/dist/src/report/report-main-red.js +55 -0
  280. package/dist/src/report/report-render.js +136 -65
  281. package/dist/src/report/report-types.js +1 -0
  282. package/dist/src/report/report.js +23 -9
  283. package/dist/src/request-timeouts.js +2 -2
  284. package/dist/src/review/diff-rules.js +53 -0
  285. package/dist/src/review/known-flakes.js +112 -1
  286. package/dist/src/review/post-merge-review-accept.js +51 -0
  287. package/dist/src/review/post-merge-review-render.js +112 -0
  288. package/dist/src/review/post-merge-review.js +239 -0
  289. package/dist/src/review/protected-edits.js +24 -10
  290. package/dist/src/review/quarantine-render.js +65 -0
  291. package/dist/src/review/review-followup.js +8 -8
  292. package/dist/src/review/review-precheck.js +66 -23
  293. package/dist/src/review/review-run.js +395 -0
  294. package/dist/src/review/review-verdict.js +2 -2
  295. package/dist/src/review/review.js +180 -401
  296. package/dist/src/review/secret-leaks.js +36 -16
  297. package/dist/src/review/suite-rerun.js +0 -1
  298. package/dist/src/roles/legacy-role-migration.js +80 -7
  299. package/dist/src/roles/role-catalog.js +127 -12
  300. package/dist/src/roles/role-guidance.js +65 -20
  301. package/dist/src/roles/role-labels.js +7 -5
  302. package/dist/src/roles/role-render.js +108 -56
  303. package/dist/src/roles/role-view.js +95 -56
  304. package/dist/src/roles/roles.js +33 -33
  305. package/dist/src/roles/runner-ids.js +97 -0
  306. package/dist/src/run/acceptance-greps.js +119 -0
  307. package/dist/src/run/ci-parity.js +121 -0
  308. package/dist/src/run/feedback-filings.js +51 -0
  309. package/dist/src/run/qa-coverage.js +125 -0
  310. package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
  311. package/dist/src/run/run-detail-data.js +175 -0
  312. package/dist/src/run/run-detail-render.js +91 -0
  313. package/dist/src/run/run-detail.js +168 -0
  314. package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +73 -60
  315. package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
  316. package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +122 -73
  317. package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
  318. package/dist/src/{tick/tick-stage.js → run/run-stage.js} +170 -83
  319. package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
  320. package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
  321. package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +105 -57
  322. package/dist/src/run/selfcheck-digest.js +40 -0
  323. package/dist/src/run/stage-backlog-checks.js +249 -0
  324. package/dist/src/run/stage-check.js +178 -0
  325. package/dist/src/run/stage-checks-shared.js +65 -0
  326. package/dist/src/run/stage-name-checks.js +204 -0
  327. package/dist/src/run/stage-path-checks.js +231 -0
  328. package/dist/src/run/stage-prose-checks.js +261 -0
  329. package/dist/src/runner/leftover.js +216 -0
  330. package/dist/src/{loop → runner}/model-fallback.js +12 -12
  331. package/dist/src/runner/revision.js +191 -0
  332. package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
  333. package/dist/src/runner/runner-outcome.js +109 -0
  334. package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
  335. package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +39 -18
  336. package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +236 -245
  337. package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
  338. package/dist/src/runner/runner-state.js +103 -0
  339. package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
  340. package/dist/src/scheduling/backoff.js +38 -38
  341. package/dist/src/scheduling/claim-entries.js +49 -0
  342. package/dist/src/scheduling/claims.js +35 -22
  343. package/dist/src/scheduling/once-round.js +22 -22
  344. package/dist/src/scheduling/quiet-hours.js +5 -6
  345. package/dist/src/scheduling/scheduling.js +74 -64
  346. package/dist/src/scheduling/work-merged-cache.js +2 -2
  347. package/dist/src/status/status-budget.js +55 -47
  348. package/dist/src/status/status-data.js +49 -17
  349. package/dist/src/status/status-inbox.js +2 -2
  350. package/dist/src/status/status-install.js +0 -1
  351. package/dist/src/status/status-polls.js +43 -28
  352. package/dist/src/status/status-roles.js +50 -13
  353. package/dist/src/team/error-storm.js +8 -8
  354. package/dist/src/team/failure-spread.js +2 -2
  355. package/dist/src/team/orchestrator-info.js +15 -9
  356. package/dist/src/team/reclaim.js +7 -8
  357. package/dist/src/team/team-hold.js +7 -7
  358. package/dist/src/team/team-polls.js +91 -50
  359. package/dist/src/team/team-state.js +8 -2
  360. package/dist/src/text/datetime.js +65 -18
  361. package/dist/src/text/format.js +48 -11
  362. package/dist/src/text/markdown.js +8 -5
  363. package/dist/src/text/phrases.js +60 -23
  364. package/dist/src/text/suggest.js +1 -1
  365. package/dist/src/text/text.js +78 -13
  366. package/dist/src/ui/badges.js +99 -38
  367. package/dist/src/ui/gui/gui-client-backlog.js +314 -0
  368. package/dist/src/ui/gui/gui-client-boot.js +89 -61
  369. package/dist/src/ui/gui/gui-client-composer.js +8 -9
  370. package/dist/src/ui/gui/gui-client-drawer-config.js +254 -0
  371. package/dist/src/ui/gui/gui-client-drawer.js +155 -289
  372. package/dist/src/ui/gui/gui-client-feed.js +271 -0
  373. package/dist/src/ui/gui/gui-client-history.js +437 -68
  374. package/dist/src/ui/gui/gui-client-labels.js +6 -3
  375. package/dist/src/ui/gui/gui-client-markdown.js +1 -2
  376. package/dist/src/ui/gui/gui-client-model.js +27 -36
  377. package/dist/src/ui/gui/gui-client-operator.js +60 -52
  378. package/dist/src/ui/gui/gui-client-pending.js +405 -24
  379. package/dist/src/ui/gui/gui-client-report-chart.js +492 -0
  380. package/dist/src/ui/gui/gui-client-report.js +105 -430
  381. package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
  382. package/dist/src/ui/gui/gui-client-settings.js +163 -5
  383. package/dist/src/ui/gui/gui-client-team.js +36 -465
  384. package/dist/src/ui/gui/gui-client.js +38 -26
  385. package/dist/src/ui/gui/gui-icons.js +0 -2
  386. package/dist/src/ui/gui/gui-page.js +10 -7
  387. package/dist/src/ui/gui/gui-styles.js +38 -34
  388. package/dist/src/ui/progress-data.js +9 -11
  389. package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +33 -32
  390. package/dist/src/ui/status-model.js +133 -125
  391. package/dist/src/ui/status-payload.js +364 -49
  392. package/dist/src/ui/status-render.js +109 -82
  393. package/dist/src/ui/team-alerts.js +728 -62
  394. package/dist/src/ui/tone.js +12 -6
  395. package/dist/src/ui/transcript-run.js +58 -0
  396. package/dist/src/ui/transcript-tail.js +28 -20
  397. package/dist/src/ui/transcript.js +63 -23
  398. package/dist/src/ui/tui/tui-app.js +0 -1
  399. package/dist/src/ui/tui/tui-backlog.js +53 -14
  400. package/dist/src/ui/tui/tui-editor-answer.js +27 -31
  401. package/dist/src/ui/tui/tui-editor-attach.js +4 -12
  402. package/dist/src/ui/tui/tui-editor-backends.js +127 -0
  403. package/dist/src/ui/tui/tui-editor-brief.js +14 -36
  404. package/dist/src/ui/tui/tui-editor-budget.js +36 -39
  405. package/dist/src/ui/tui/tui-editor-check.js +29 -0
  406. package/dist/src/ui/tui/tui-editor-checks.js +28 -0
  407. package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
  408. package/dist/src/ui/tui/tui-editor-concurrency.js +25 -0
  409. package/dist/src/ui/tui/tui-editor-config.js +99 -0
  410. package/dist/src/ui/tui/tui-editor-entry.js +39 -64
  411. package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
  412. package/dist/src/ui/tui/tui-editor-instances.js +46 -0
  413. package/dist/src/ui/tui/tui-editor-instructions.js +117 -0
  414. package/dist/src/ui/tui/tui-editor-interval.js +29 -16
  415. package/dist/src/ui/tui/tui-editor-model.js +29 -0
  416. package/dist/src/ui/tui/tui-editor-notes.js +9 -29
  417. package/dist/src/ui/tui/tui-editor-notify.js +25 -0
  418. package/dist/src/ui/tui/tui-editor-pause.js +48 -59
  419. package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
  420. package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
  421. package/dist/src/ui/tui/tui-editor-queued.js +5 -9
  422. package/dist/src/ui/tui/tui-editor-quiet.js +95 -41
  423. package/dist/src/ui/tui/tui-editor-retire.js +5 -13
  424. package/dist/src/ui/tui/tui-editor-role-enabled.js +126 -0
  425. package/dist/src/ui/tui/tui-editor-role-key.js +71 -0
  426. package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
  427. package/dist/src/ui/tui/tui-editor-search.js +45 -33
  428. package/dist/src/ui/tui/tui-editor-session.js +101 -34
  429. package/dist/src/ui/tui/tui-editor-slots.js +24 -0
  430. package/dist/src/ui/tui/tui-editors.js +44 -2
  431. package/dist/src/ui/tui/tui-frame.js +68 -11
  432. package/dist/src/ui/tui/tui-history.js +8 -9
  433. package/dist/src/ui/tui/tui-input.js +13 -11
  434. package/dist/src/ui/tui/tui-keymap.js +0 -1
  435. package/dist/src/ui/tui/tui-keys.js +33 -9
  436. package/dist/src/ui/tui/tui-pane-activity.js +35 -19
  437. package/dist/src/ui/tui/tui-pane-backlog.js +198 -126
  438. package/dist/src/ui/tui/tui-pane-brief.js +0 -1
  439. package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
  440. package/dist/src/ui/tui/tui-pane-health.js +168 -8
  441. package/dist/src/ui/tui/tui-pane-history.js +446 -82
  442. package/dist/src/ui/tui/tui-pane-pending.js +539 -15
  443. package/dist/src/ui/tui/tui-pane-queued.js +78 -57
  444. package/dist/src/ui/tui/tui-pane-report.js +54 -16
  445. package/dist/src/ui/tui/tui-pane-session.js +34 -6
  446. package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
  447. package/dist/src/ui/tui/tui-pane.js +91 -28
  448. package/dist/src/ui/tui/tui-panes.js +153 -34
  449. package/dist/src/ui/tui/tui.js +18 -15
  450. package/dist/src/verdict/fix-claim.js +129 -33
  451. package/dist/src/verdict/no-change.js +4 -4
  452. package/dist/src/verdict/refusal.js +40 -16
  453. package/dist/src/verdict/reply-contract.js +77 -31
  454. package/dist/src/version.js +0 -1
  455. package/package.json +2 -1
  456. package/dist/src/loop/leftover.js +0 -130
  457. package/dist/src/loop/revision.js +0 -70
  458. package/dist/src/roles/loop-ids.js +0 -77
  459. package/dist/src/tick/qa-coverage.js +0 -108
  460. package/dist/src/tick/stage-check.js +0 -419
  461. package/dist/src/tick/telemetry-digest.js +0 -25
  462. package/dist/src/tick/tick-detail-data.js +0 -108
  463. package/dist/src/tick/tick-detail.js +0 -163
  464. package/dist/src/ui/gui/gui-client-sound.js +0 -53
@@ -1,419 +0,0 @@
1
- // vocabulary: plain
2
- /** The merge gate's deterministic checks, run BEFORE a changed tick commits.
3
- *
4
- * The gate runs `backlogStructureReason` and `falseFixReason` at merge time, by which point
5
- * the author's session is over: a finding costs a queue slot, a verification, and a rejection that
6
- * discards the work (13 phantom-fix rejections between 2026-09-23 and 2026-10-02). Both checks
7
- * read the worktree and the change's merge-base, so the same questions can be asked at staging
8
- * time, while the authoring session can still be continued for one fix-up turn. This module
9
- * runs them over the tick's uncommitted edits; the gate still has the final say.
10
- *
11
- * Scope mirrors the gate exactly: `backlogStructureReason` runs on every changed tick, while
12
- * `falseFixReason` runs only for an exempt (doc-only) diff — the condition under which the gate
13
- * consults it.
14
- *
15
- * Part 2/2 appends git-level, language-neutral findings (PLANS.md "Pre-queue self-check, part
16
- * 2/2") for path drift, the organize loop's main rejection cause: references left behind by a
17
- * rename or delete, added lines naming paths that do not exist in the resulting tree, and text
18
- * files whose final newline the change removed. These stage the change (`git add -A`) to read
19
- * the index with git only, then restore the index to HEAD before returning: staging is a read
20
- * aid for the checks, not a change to the tick's state. A git hiccup yields no finding rather
21
- * than failing the tick. Unlike the backlog checks above, the gate does not run these: they are
22
- * heuristics for what a reviewer tends to reject, so neither path check looks inside record
23
- * prose (isRecordProse), where an old or not-yet-created path is correct as written.
24
- *
25
- * `planDependencyFinding` is staging-only: it is not part of the review gate or the in-lock
26
- * merge re-check, because `tumwater doctor`'s plan-dependency warning is the backstop for
27
- * anything that gets past the fix-up turn. `waitingNoteFinding` is staging-only and bugfix-only
28
- * for the same reason: it nudges a bugfix run that left a Progress note without the hold. */
29
- import path from "node:path";
30
- import { changedFiles } from "../git/git-diff.js";
31
- import { conflictMarkerReason } from "../merge/merge-git.js";
32
- import { isExemptDiff } from "../review/exemptions.js";
33
- import { backlogStructureReason } from "../backlog/backlog-structure.js";
34
- import { protectedEditReason } from "../review/protected-edits.js";
35
- import { secretLeakReason } from "../review/secret-leaks.js";
36
- import { falseFixReason } from "../verdict/fix-claim.js";
37
- import { changeBaseRev, fileContentAt } from "../git/git.js";
38
- import { readTextOrNull } from "../files/files.js";
39
- import { BACKLOG_FILES, finishedArchiveOf, parseEntryDetails } from "../backlog/backlog-md.js";
40
- import { entryKey, planDependencyProblems, refusalNote, waitingNote } from "../backlog/backlog-eligibility.js";
41
- import { baseRoleOf } from "../roles/loop-ids.js";
42
- import { WAITING_FOR_FAILURE_NOTE } from "../roles/role-guidance.js";
43
- import { gitTry } from "../git/git-run.js";
44
- import { moreSuffix } from "../text/phrases.js";
45
- /** Where finished plans are archived (plans/backlog-archive.md). */
46
- const PLANS_ARCHIVE = finishedArchiveOf("PLANS.md").archive;
47
- /** How many `file:line` hits a single stale-reference finding lists before it stops. */
48
- const MAX_STALE_HITS = 10;
49
- /** How many renamed/deleted paths each stale-reference finding reports before stopping — the
50
- * cap that keeps a 40-file rename to a bounded finding list. */
51
- const MAX_STALE_PATHS = 10;
52
- /** How many nonexistent added paths one finding names before it stops. */
53
- const MAX_MISSING_PATHS = 10;
54
- /** How many files one lost-final-newline finding names before it stops. */
55
- const MAX_NEWLINE_FILES = 10;
56
- /** The path a unified diff's `+++ b/<path>` header names. Strips the four-character `+++ `
57
- * marker and git's `b/` prefix. A deleted file's new side is `+++ /dev/null`, which carries no
58
- * `b/` prefix and is returned as `/dev/null`. */
59
- function diffPlusPath(line) {
60
- return line.slice(4).replace(/^b\//, "");
61
- }
62
- /** True for record prose: a backlog file (BACKLOG_FILES: PLANS.md, BUGS.md, QUESTIONS.md and the
63
- * finished-entry archives) or a markdown design doc under `plans/`. They record history and
64
- * describe planned work, so a path there is correct as written even when it is absent from the
65
- * tree: a finished entry names a file as it was, and a plan names the file it will create or
66
- * both sides of the move it describes. Asking a rename to sweep them rewrote "old → new" into
67
- * "new → new" and history into the present; that failure took five rejections on
68
- * one rename on 2026-10-18 and -19. */
69
- function isRecordProse(file) {
70
- const base = file.slice(file.lastIndexOf("/") + 1);
71
- return BACKLOG_FILES.has(base) || (file.startsWith("plans/") && file.endsWith(".md"));
72
- }
73
- /** The capped list a finding line appends: at most `max` entries joined by ", ", then
74
- * `moreSuffix` for the rest. Shared by the missing-path and lost-final-newline findings so both
75
- * cap and join identically. */
76
- function cappedList(items, max) {
77
- const shown = items.slice(0, max);
78
- return `${shown.join(", ")}${moreSuffix(items.length - shown.length)}`;
79
- }
80
- /** The finding for the heading `requires` clause problems that `head` (the edited PLANS.md)
81
- * adds over `base` (its text at the merge-base), or undefined when it adds none; `archivedTitles`
82
- * (PLANS-DONE.md's headings) count as existing headings on both sides. Only new
83
- * problems count: one already on main is not this run's fault and stays doctor's to report, so
84
- * the author is never asked to fix another run's heading. The most serious new problem is named
85
- * — cycle, then dangling, unparsed, then misworded — with a count of the rest. */
86
- export function planDependencyFinding(head, base, archivedTitles = []) {
87
- const problems = (text) => planDependencyProblems(parseEntryDetails(text, "Planned"), [...parseEntryDetails(text, "Done").map((e) => e.title), ...archivedTitles]);
88
- const was = problems(base);
89
- const now = problems(head);
90
- const newCycle = now.cycle !== null && was.cycle === null ? now.cycle : null;
91
- const newDangling = now.dangling.filter((d) => !was.dangling.some((b) => b.title === d.title && b.ref === d.ref));
92
- const newUnparsed = now.unparsed.filter((t) => !was.unparsed.includes(t));
93
- const newMisworded = now.misworded.filter((m) => !was.misworded.some((b) => b.title === m.title && b.ref === m.ref));
94
- const total = (newCycle !== null ? 1 : 0) + newDangling.length + newUnparsed.length + newMisworded.length;
95
- if (total === 0)
96
- return undefined;
97
- let first;
98
- if (newCycle !== null) {
99
- first = `PLANS.md's requires clauses now form a cycle: ${newCycle.join(" → ")} — every part on it stays blocked forever; ` + // vocabulary-ok: PLANS.md heading syntax
100
- "edit one heading's `requires … landed` clause to break it"; // vocabulary-ok: PLANS.md heading syntax
101
- }
102
- else if (newDangling.length > 0) {
103
- const d = newDangling[0];
104
- first = `PLANS.md plan "${d.title}" requires ${d.ref}, which no PLANS.md heading carries — the claim allocator ` +
105
- "ignores the clause and hands the plan out now; fix the series name or part number to match the prerequisite's heading";
106
- }
107
- else if (newUnparsed.length > 0) {
108
- first = `PLANS.md plan "${newUnparsed[0]}" has a \`requires … landed\` clause that names no part the claim allocator can read — ` + // vocabulary-ok: PLANS.md heading syntax
109
- "it holds nothing; write each ref as `<Series> i/n` or `part i/n` after a `<Series>, part i/n:` heading prefix";
110
- }
111
- else {
112
- const w = newMisworded[0];
113
- first = `PLANS.md plan "${w.title}" names ${w.ref} as a prerequisite without a \`requires … landed\` clause — ` + // vocabulary-ok: PLANS.md heading syntax
114
- "the claim allocator ignores that wording and hands the part out now; write it as `requires part i/n landed` — fix it before this change commits"; // vocabulary-ok: PLANS.md heading syntax
115
- }
116
- return `${first}${moreSuffix(total - 1)}`;
117
- }
118
- /** The number of `- **Progress …` lines in an entry body. */
119
- function progressCount(body) {
120
- return body.split("\n").filter((l) => /^-\s+\*\*Progress\b/.test(l.trim())).length;
121
- }
122
- /** The finding for a BUGS.md bug that `head` (the edited file) shows with more Progress notes
123
- * than `base` (its text at the merge-base) while it stays under `## Open` with neither a Waiting
124
- * note nor a Refused note — bugfix's step 2 asks a run that cannot reproduce a bug to write the
125
- * Waiting note, and runs that skip it leave the bug open to re-investigation. A newly filed bug
126
- * is not a re-investigation. Names the first such bug in file order, with a count of the rest. */
127
- export function waitingNoteFinding(head, base) {
128
- const was = new Map(parseEntryDetails(base, "Open").map((e) => [entryKey(e.title), e.body]));
129
- const counting = parseEntryDetails(head, "Open").filter((e) => {
130
- const before = was.get(entryKey(e.title));
131
- return (before !== undefined &&
132
- progressCount(e.body) > progressCount(before) &&
133
- waitingNote(e.body) === null &&
134
- refusalNote(e.body) === null);
135
- });
136
- if (counting.length === 0)
137
- return undefined;
138
- return (`BUGS.md bug "${counting[0].title}" gained a Progress note but stays open with no Waiting for the next ` +
139
- `failure note — if this run could not reproduce it, add ${WAITING_FOR_FAILURE_NOTE} as its own line under ` +
140
- `the heading so later runs wait for new evidence (bugfix's step 2); if it did reproduce, leave the entry ` +
141
- `as it is${moreSuffix(counting.length - 1)}`);
142
- }
143
- /** The findings likely to get this uncommitted change rejected at merge, or an empty list when
144
- * it is clean: the merge gate's own deterministic checks plus the staging-only heuristics (plan
145
- * dependencies, the bugfix Waiting note, path drift). Checks the worktree at `wt` against
146
- * `mainBranch`'s merge-base; `exemptPaths` are the config's review-exemption globs that scope
147
- * the false-fix check.
148
- *
149
- * Never throws: a check that fails yields no finding, so a git hiccup at staging time defers to
150
- * the gate instead of failing the tick. */
151
- export async function stageCheckFindings(wt, mainBranch, exemptPaths, role) {
152
- const files = await changedFiles(wt).catch(() => null);
153
- if (files === null || files.length === 0)
154
- return [];
155
- const findings = [];
156
- const structure = await backlogStructureReason(wt, mainBranch, files).catch(() => undefined);
157
- if (structure)
158
- findings.push(structure);
159
- if (files.includes("PLANS.md")) {
160
- const head = readTextOrNull(path.join(wt, "PLANS.md")) ?? "";
161
- const base = await fileContentAt(wt, await changeBaseRev(wt, mainBranch), "PLANS.md").catch(() => undefined);
162
- // Finished plans are archived in PLANS-DONE.md (plans/backlog-archive.md): a requires clause
163
- // naming a merged part resolves there, on both sides of the comparison alike.
164
- const archived = parseEntryDetails(readTextOrNull(path.join(wt, PLANS_ARCHIVE)) ?? "", "Done").map((e) => e.title);
165
- const dependency = base === undefined ? undefined : planDependencyFinding(head, base, archived);
166
- if (dependency !== undefined)
167
- findings.push(dependency);
168
- }
169
- if (baseRoleOf(role) === "bugfix" && files.includes("BUGS.md")) {
170
- const head = readTextOrNull(path.join(wt, "BUGS.md")) ?? "";
171
- const base = await fileContentAt(wt, await changeBaseRev(wt, mainBranch), "BUGS.md").catch(() => undefined);
172
- const waiting = base === undefined ? undefined : waitingNoteFinding(head, base);
173
- if (waiting !== undefined)
174
- findings.push(waiting);
175
- }
176
- const protectedReason = await protectedEditReason(wt, mainBranch, files, role).catch(() => undefined);
177
- if (protectedReason)
178
- findings.push(protectedReason);
179
- const secretReason = await secretLeakReason(wt, mainBranch, files).catch(() => undefined);
180
- if (secretReason)
181
- findings.push(secretReason);
182
- if (isExemptDiff(files, exemptPaths)) {
183
- const falseFix = await falseFixReason(wt, mainBranch, files).catch(() => undefined);
184
- if (falseFix)
185
- findings.push(falseFix);
186
- }
187
- findings.push(...(await gitLevelFindings(wt, mainBranch).catch(() => [])));
188
- const markers = conflictMarkerReason(wt, files);
189
- if (markers !== undefined)
190
- findings.push(markers);
191
- return findings;
192
- }
193
- /** The staging finding for a tick that holds a claim but moved a different backlog entry
194
- * (plans/parallel-work-instances.md "Claims", part 4/7): the assigned entry is the instance's
195
- * one task, so moving another is the same class of mistake `backlogStructureReason` catches
196
- * and costs one fix-up turn before the change commits. undefined when nothing moved or the
197
- * claimed entry itself moved — a merged entry is the expected way to release a claim. */
198
- export function assignedMovedFinding(claimKey, moved) {
199
- if (moved.length === 0 || moved.some((m) => m.key === claimKey))
200
- return undefined;
201
- return `this run is assigned one backlog entry but moved a different one: "${moved[0].title}"`;
202
- }
203
- /** The staging finding for an unassigned tick that moved a backlog entry a sibling instance
204
- * holds — one the poll listed in the tick's `<held-entries>` note (LoopState.heldElsewhere).
205
- * The note is advice the model can ignore; this is the deterministic backstop at the same
206
- * point as assignedMovedFinding (BUGS.md 2026-10-12: 24 of 29 staged claims duplicated a
207
- * sibling's held entry, and each duplicate cost a gate check, a review, and a conflict
208
- * resolver run holding the merge slot). The fix-up turn is asked to drop the duplicate work.
209
- * A change that held nothing else then ends with no change to merge. undefined when no moved
210
- * entry is held. */
211
- export function heldMovedFinding(heldKeys, moved) {
212
- const dup = moved.find((m) => heldKeys.has(m.key));
213
- if (dup === undefined)
214
- return undefined;
215
- return (`this run moved a backlog entry another instance of this role is working on: "${dup.title}" — it is that ` +
216
- `instance's task (see <held-entries>), so revert every edit made for it, including the ` +
217
- `entry's move, and leave nothing behind if that was the whole change`);
218
- }
219
- /** Parse `git diff --cached -M --name-status` output: `M\tpath`, `D\tpath`, or
220
- * `R100\told\tnew`. Malformed lines are skipped. */
221
- function parseNameStatus(out) {
222
- const entries = [];
223
- for (const line of (out ?? "").split("\n")) {
224
- if (!line)
225
- continue;
226
- const parts = line.split("\t");
227
- const status = (parts[0] ?? "").charAt(0);
228
- if (!status)
229
- continue;
230
- if (status === "R" || status === "C") {
231
- const oldPath = parts[1];
232
- const path = parts[2];
233
- if (oldPath && path)
234
- entries.push({ status, path, oldPath });
235
- }
236
- else if (parts[1]) {
237
- entries.push({ status, path: parts[1] });
238
- }
239
- }
240
- return entries;
241
- }
242
- /** Stage the change and run the three git-level path checks over the resulting index, then
243
- * restore the index to HEAD so the staging is invisible to the tick's own change detection.
244
- * Each check is best-effort: a failed git command contributes nothing. */
245
- async function gitLevelFindings(wt, mainBranch) {
246
- await gitTry(wt, "add", "-A");
247
- try {
248
- const base = await changeBaseRev(wt, mainBranch);
249
- const entries = parseNameStatus(await gitTry(wt, "diff", "--cached", "-M", "--name-status", base));
250
- const findings = await staleReferenceFindings(wt, entries);
251
- const diff = (await gitTry(wt, "diff", "--cached", "-U0", base)) ?? "";
252
- findings.push(...(await missingPathFindings(wt, diff, entries)));
253
- findings.push(...newlineFindings(diff));
254
- return findings;
255
- }
256
- finally {
257
- // Restore the index to HEAD. Without this, the tick's change detection (changedFiles and
258
- // stageTickMerge's "nothing left to merge" guard) reads the stale index and reports a
259
- // fix-up that reverted the whole change as still changed, so an empty tree would reach
260
- // commitAll instead of the tick ending no_change.
261
- await gitTry(wt, "reset");
262
- }
263
- }
264
- /** (a) References left behind by a rename or delete: for each `D` path and each `R` origin,
265
- * `git grep --cached` the old path across the staged tree and report up to MAX_STALE_HITS
266
- * `file:line` hits outside record prose (isRecordProse). Renames report the path they moved to;
267
- * deletes report the deletion. */
268
- async function staleReferenceFindings(wt, entries) {
269
- const findings = [];
270
- let reported = 0;
271
- for (const e of entries) {
272
- if (e.status !== "D" && e.status !== "R")
273
- continue;
274
- if (reported >= MAX_STALE_PATHS)
275
- break;
276
- const old = e.oldPath ?? e.path;
277
- // `git grep` exits 1 with no output when nothing matches — the common, clean case — and
278
- // gitTry maps that to null exactly as it maps a real failure, both of which mean "no hits".
279
- const out = await gitTry(wt, "grep", "-n", "-F", "-I", "--cached", "-e", old);
280
- if (!out)
281
- continue;
282
- const hits = [];
283
- for (const line of out.split("\n")) {
284
- const m = /^([^:]+):(\d+):(.*)$/.exec(line);
285
- if (!m || isRecordProse(m[1]) || !hasStandalonePathOccurrence(m[3] ?? "", old))
286
- continue;
287
- hits.push(`${m[1]}:${m[2]}`);
288
- if (hits.length >= MAX_STALE_HITS)
289
- break;
290
- }
291
- if (hits.length === 0)
292
- continue;
293
- const moved = e.status === "R" ? `was renamed to ${e.path}` : "was deleted";
294
- findings.push(`${old} ${moved} but is still named at: ${hits.join(", ")}`);
295
- reported++;
296
- }
297
- return findings;
298
- }
299
- /** True when `needle` appears in `line` as a repo path of its own — not as one segment of a
300
- * longer path. `git grep -F` matches substrings, so a line naming a rename's destination
301
- * (`src/config.ts`) would otherwise read as a stale reference to an old root `config.ts`.
302
- * A preceding path character (or a following one that continues the token, as a `.` extension
303
- * or an alphanumeric does) disqualifies the occurrence; trailing sentence punctuation does not. */
304
- function hasStandalonePathOccurrence(line, needle) {
305
- const pathChar = /[A-Za-z0-9_./-]/;
306
- for (let i = line.indexOf(needle); i !== -1; i = line.indexOf(needle, i + 1)) {
307
- const before = i > 0 ? line[i - 1] : "";
308
- if (before && pathChar.test(before))
309
- continue;
310
- const after = line[i + needle.length] ?? "";
311
- if (/[A-Za-z0-9_/-]/.test(after))
312
- continue;
313
- if (after === "." && /[A-Za-z0-9]/.test(line[i + needle.length + 1] ?? ""))
314
- continue;
315
- return true;
316
- }
317
- return false;
318
- }
319
- /** Each distinct path-shaped token in `text`, in first-appearance order: at least one slash and
320
- * a final `.<ext>` segment. Scanned inside the text (not whitespace-split) so surrounding
321
- * backticks, brackets, and sentence punctuation fall away with the match. */
322
- export function pathTokens(text) {
323
- const tokenRe = /[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+\.[A-Za-z0-9]+/g;
324
- return [...new Set([...text.matchAll(tokenRe)].map((m) => m[0]))];
325
- }
326
- /** (b) Nonexistent paths: every path-shaped token on an added diff line whose first segment is
327
- * a tracked top-level directory but which is absent from the staged tree. Rename origins and
328
- * deleted paths are excluded — "moved from X" prose names them legitimately. */
329
- async function missingPathFindings(wt, diff, entries) {
330
- const ls = await gitTry(wt, "ls-files");
331
- const staged = new Set();
332
- const topDirs = new Set();
333
- for (const p of (ls ?? "").split("\n")) {
334
- if (!p)
335
- continue;
336
- staged.add(p);
337
- const slash = p.indexOf("/");
338
- if (slash > 0)
339
- topDirs.add(p.slice(0, slash));
340
- }
341
- const oldPaths = new Set();
342
- for (const e of entries) {
343
- if (e.oldPath)
344
- oldPaths.add(e.oldPath);
345
- if (e.status === "D")
346
- oldPaths.add(e.path);
347
- }
348
- const seen = new Set();
349
- const missing = [];
350
- let file = null;
351
- for (const line of diff.split("\n")) {
352
- if (line.startsWith("+++ ")) {
353
- file = diffPlusPath(line);
354
- continue;
355
- }
356
- if (!line.startsWith("+") || line.startsWith("+++"))
357
- continue;
358
- // A test file's fixtures routinely name paths from the software under test, which need
359
- // not exist here, and record prose names the paths a plan proposes to create; the
360
- // added-nonexistent-path heuristic targets real references, so it spares added lines in
361
- // test/ and in record prose (isRecordProse).
362
- if (file !== null && (file.startsWith("test/") || isRecordProse(file)))
363
- continue;
364
- for (const p of pathTokens(line.slice(1))) {
365
- const top = p.slice(0, p.indexOf("/"));
366
- if (!topDirs.has(top) || oldPaths.has(p) || staged.has(p) || seen.has(p))
367
- continue;
368
- seen.add(p);
369
- missing.push(p);
370
- }
371
- }
372
- if (missing.length === 0)
373
- return [];
374
- return [
375
- `added lines name paths that do not exist in the tree: ${cappedList(missing, MAX_MISSING_PATHS)}`,
376
- ];
377
- }
378
- /** (c) Lost final newlines: a text file whose new diff side carries git's
379
- * `` marker while its base side does not. Git emits the marker
380
- * immediately after the line it applies to, so a marker after a `+` line flags the new side and
381
- * one after a `-` line flags the base side; a new file (`+` only) counts. Binary files have no
382
- * line hunks and never appear. */
383
- function newlineFindings(diff) {
384
- const files = [];
385
- let path = null;
386
- let newLacks = false;
387
- let oldLacks = false;
388
- let prev = "";
389
- const flush = () => {
390
- if (path !== null && newLacks && !oldLacks)
391
- files.push(path);
392
- };
393
- for (const line of diff.split("\n")) {
394
- if (line.startsWith("diff --git ")) {
395
- flush();
396
- path = null;
397
- newLacks = false;
398
- oldLacks = false;
399
- continue;
400
- }
401
- if (line.startsWith("+++ ")) {
402
- path = diffPlusPath(line);
403
- continue;
404
- }
405
- if (line.startsWith("\")) {
406
- if (prev.startsWith("+"))
407
- newLacks = true;
408
- else if (prev.startsWith("-"))
409
- oldLacks = true;
410
- continue;
411
- }
412
- if (line.startsWith("+") || line.startsWith("-") || line.startsWith(" "))
413
- prev = line;
414
- }
415
- flush();
416
- if (files.length === 0)
417
- return [];
418
- return [`the change removes the final newline from: ${cappedList(files, MAX_NEWLINE_FILES)}`];
419
- }
@@ -1,25 +0,0 @@
1
- /** The `telemetry` role's tick-time evidence: the failure digest rendered over the role's own
2
- * trailing 24 h window. Split out of src/failure/failure-render.ts — which stays the pure
3
- * renderer — so every observer role's evidence builder has its own module the way
4
- * qa-coverage.ts and backlog-structure.ts do, and the tick lifecycle (tick-prompt.ts) injects
5
- * it from there instead of reaching into a report module. */
6
- import { collectFailureReportSince } from "../failure/failure-data.js";
7
- import { renderFailureMarkdown } from "../failure/failure-render.js";
8
- /** The `telemetry` role's own digest window: the trailing 24 h ending now, not the local
9
- * calendar day. A calendar day starts at midnight, so a run at 06:32 would never see the
10
- * previous evening. The CLI keeps the usage report's 14-day default; the role reads 24 h so a
11
- * cluster re-surfaces only while it is live. The collector's equal-length prior window is what
12
- * makes the delta line meaningful. */
13
- export const TELEMETRY_DIGEST_MS = 24 * 60 * 60 * 1000;
14
- /** The `telemetry` role's tick-time evidence: the failure digest rendered over its own
15
- * trailing 24 h window. A missing or corrupt log omits the block (undefined) and never fails the
16
- * tick — an observer must not break on bookkeeping (plans/telemetry-role.md). The window and
17
- * the swallow-errors policy live with the digest, not in the tick lifecycle that injects it. */
18
- export function telemetryDigest(root) {
19
- try {
20
- return renderFailureMarkdown(collectFailureReportSince(root, TELEMETRY_DIGEST_MS));
21
- }
22
- catch {
23
- return undefined;
24
- }
25
- }
@@ -1,108 +0,0 @@
1
- // vocabulary: plain
2
- /** Collection half of `tumwater tick <role> <n>`: one completed tick's full event block,
3
- * distilled from the event log into a TickDetail payload. The collector-in-core convention
4
- * beside history-data.ts and src/report/report-data.ts — a pure read over root, so core
5
- * modules can serve the same payload without reaching into ui/, and the CLI
6
- * (`tick-detail.ts`) and the GUI History drill-down (`src/gui/gui-endpoints.ts`) cannot
7
- * drift on what one tick's block contains.
8
- * Every datum rides the events the harness already writes, so this is a distillation of the
9
- * existing record, not a new one. The rendering half — the summary header, formatEvent, the
10
- * CLI command — lives in tick-detail.ts. */
11
- import { HISTORY_SCAN_MAX_EVENTS, bucketMergeEvents, resolveQueuedResult } from "../history/history-data.js";
12
- import { eventUsage, readEvents, tickSpanMs, tickStartMap } from "../events/event-read.js";
13
- import { outcomeText, usageText } from "../events/event-format.js";
14
- /** The event block of `role`'s tick `n`, or null when the scanned window holds no such tick.
15
- * The newest `run_start` the log retains for `${role}#${n}` wins (a counter reset can make an
16
- * old tick number recur; the operator asking by number means the most recent one), and the
17
- * block closes at the first `run_end` for that tick after the start — or, when that end is
18
- * missing (in flight, or its tail lost to rotation), at the loop's next `run_start`, so a
19
- * rotation-cut block never leaks the following tick's events into this one. When even the
20
- * start is gone but a matching `run_end` survives, the end-alone block still answers with
21
- * `startTs` null and a null duration (the unpaired rule); nothing at all in the scan is the
22
- * not-found case the caller renders as a not-found line. */
23
- export function readTickDetail(root, role, tick) {
24
- // The scan rides history-data.ts's shared one-ask ceiling (HISTORY_SCAN_MAX_EVENTS): one
25
- // tick's block is small, but it can sit arbitrarily far back in a long-lived team's log,
26
- // and readTailText reads bytes proportional to the limit's line count — so the cap is also
27
- // the largest byte read one command can cost, and every read-only view scans under the
28
- // same bound.
29
- const events = readEvents(root, HISTORY_SCAN_MAX_EVENTS).filter((e) => e.source === role);
30
- // Newest run_start for this loop+tick: the block's lower bound.
31
- let startIdx = -1;
32
- for (let i = events.length - 1; i >= 0; i--) {
33
- const e = events[i];
34
- if (e.type === "run_start" && e.run === tick) {
35
- startIdx = i;
36
- break;
37
- }
38
- }
39
- if (startIdx < 0) {
40
- // The start is gone (rotation) or the tick never ran. A surviving run_end for the same
41
- // number still answers — unpaired, undated at the front — else there is no such tick here.
42
- for (let i = events.length - 1; i >= 0; i--) {
43
- const e = events[i];
44
- if (e.type === "run_end" && e.run === tick)
45
- return detailFrom(role, tick, events, [e], e, null);
46
- }
47
- return null;
48
- }
49
- const start = events[startIdx];
50
- // The block's upper bound: the first run_end for this tick after the start ends it; a
51
- // later run_start (this tick's end lost to rotation) closes the block exclusive — the
52
- // events after it belong to the next tick, not this one. No end and no next start reads as
53
- // in flight: everything since the start belongs to the open tick.
54
- let end = null;
55
- let nextStart = null;
56
- for (let i = startIdx + 1; i < events.length; i++) {
57
- const e = events[i];
58
- if (e.type === "run_end" && e.run === tick) {
59
- end = e;
60
- break;
61
- }
62
- if (e.type === "run_start") {
63
- nextStart = e;
64
- break;
65
- }
66
- }
67
- // The block itself: the start event plus everything this loop logged up to the bound —
68
- // inclusive at a paired end, exclusive at a next tick's start.
69
- const block = [];
70
- for (let i = startIdx; i < events.length; i++) {
71
- const e = events[i];
72
- if (end !== null ? e.ts > end.ts : nextStart !== null && e.ts >= nextStart.ts)
73
- break;
74
- block.push(e);
75
- }
76
- return detailFrom(role, tick, events, block, end, start.ts);
77
- }
78
- /** Fill the payload around a collected block: usage from the run_end via eventUsage/usageText,
79
- * duration via tickSpanMs against the loop's own start map (the shared pairing helper, so this
80
- * view and the history rows cannot drift on what a span is), events oldest-first. */
81
- function detailFrom(role, tick, scoped, block, end, startTs) {
82
- const usageNums = end === null ? { tokens: 0, costUsd: 0 } : eventUsage(end);
83
- let result = end === null ? null : outcomeText(end);
84
- let merge = null;
85
- if (end !== null && result === "queued") {
86
- // A fresh claim cursor suits one tick: the walk starts at the newest pin at or before this
87
- // tick's end, which is its own pin, since later ticks' pins sit after it.
88
- const { mergeQueuedByLoop, outcomeByLoop } = bucketMergeEvents(scoped);
89
- const resolved = resolveQueuedResult(end, mergeQueuedByLoop, outcomeByLoop, new Map(), tickStartMap(scoped), null);
90
- if (resolved !== null) {
91
- result = resolved.result;
92
- merge = resolved.outcome;
93
- }
94
- }
95
- return {
96
- role,
97
- run: tick,
98
- startTs,
99
- endTs: end === null ? null : end.ts,
100
- durationMs: end === null ? null : tickSpanMs(end, tickStartMap(scoped)),
101
- result,
102
- tokens: usageNums.tokens,
103
- costUsd: usageNums.costUsd,
104
- usage: end === null ? "" : usageText(end),
105
- events: block,
106
- merge,
107
- };
108
- }