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
@@ -0,0 +1,263 @@
1
+ import { andMore, readDoc, readDocChecked, FIX_CLAIMS_CHECKED, FIX_CLAIM_HEADING_MAX, REFUSAL_NOTE_MAX } from "./doctor-backlog.js";
2
+ import { VALIDATION_GAP_TAGS } from "../roles/role-guidance.js";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { truncate } from "../text/text.js";
6
+ import { donePlans, plannedPlanEntries } from "../backlog/backlog.js";
7
+ import { entryHold, heldCount } from "../backlog/backlog-eligibility.js";
8
+ import { planDependencyProblems } from "../backlog/backlog-parts.js";
9
+ import { BACKLOG_FILES, validationGapTag } from "../backlog/backlog-md.js";
10
+ import { duplicateHeadings, strandedPlanEntriesFor } from "../backlog/backlog-structure.js";
11
+ import { liveFinishedHistory } from "../backlog/backlog-archive.js";
12
+ import { fixSymbols, fixedEntriesAt, missingSymbolNames, sourceHaystack, unbackedSymbols } from "../verdict/fix-claim.js";
13
+ import { plural } from "../text/phrases.js";
14
+ import { okOutcome, warnOutcome } from "./outcome.js";
15
+ /** The doctor's PLANS.md-structure and fix-claim checks, split out of doctor-backlog.ts: the
16
+ * checks that re-verify BUGS.md's newest Fixed records against the tree (checkFixClaims,
17
+ * checkValidationGaps) and the PLANS.md / backlog-file integrity checks that read sections and
18
+ * clauses rather than a single document's entries (checkStrandedPlans, checkLiveFinished,
19
+ * checkBacklogHeadings, checkStalledPlans, checkPlanDependencies). They import the shared helpers
20
+ * (readDoc and its checked wrapper readDocChecked, andMore) from doctor-backlog.ts and the
21
+ * heading-trim bounds,
22
+ * and all of them warn instead of failing: damaged documentation is operator signal, not a
23
+ * broken environment. doctor.ts composes them beside the open-item and queue checks; the shared
24
+ * CheckOutcome shape is imported from outcome.ts. */
25
+ /** Fix claims — the standalone half of the merge gate's false-fix check (src/verdict/fix-claim.ts):
26
+ * that gate fires only when an md-only diff moves a BUGS.md entry to Fixed, so a phantom fix that
27
+ * reached main any other way (merged before the gate existed, or through a path it never sees) was
28
+ * visible only to a human reading raw history. This re-verifies the newest Fixed records against
29
+ * the tree at `root` — the primary checkout IS main's tree — with the gate's own parsing and
30
+ * existence rules. Deliberately looser than the gate: a record warns only when EVERY symbol its Fix
31
+ * paragraph names is absent (one live symbol passes — the phantom signature, not a half-stale
32
+ * narrative), and a record naming no symbols is skipped, since pure-documentation fixes are
33
+ * legitimate. A warn, never a fail (the checkFallbackModel precedent): a suspicious record is
34
+ * operator signal, not a broken environment. */
35
+ export function checkFixClaims(root) {
36
+ const doc = readDocChecked(path.join(root, "BUGS.md"), "BUGS.md", "no BUGS.md — nothing to verify");
37
+ if (typeof doc !== "string")
38
+ return doc;
39
+ // One parse feeds every record's title and body: bugEntryBody re-parsed the whole document
40
+ // per heading, and BUGS.md grows without bound, so the newest-10 scan paid 1 + 10 full-document
41
+ // walks where one suffices.
42
+ const records = fixedEntriesAt(root, doc).slice(0, FIX_CLAIMS_CHECKED);
43
+ // The haystack walks src/, test/ and scripts/: build it only once a record names something.
44
+ let haystack;
45
+ const phantoms = [];
46
+ for (const { title: heading, body } of records) {
47
+ const symbols = fixSymbols(body);
48
+ if (symbols.length === 0)
49
+ continue;
50
+ haystack ??= sourceHaystack(root);
51
+ const missing = unbackedSymbols(root, symbols, haystack);
52
+ if (missing.length === symbols.length)
53
+ phantoms.push({ heading, missing });
54
+ }
55
+ const [first, ...rest] = phantoms;
56
+ if (!first)
57
+ return okOutcome(`newest ${records.length} Fixed record(s) name code that exists on this tree`);
58
+ // falseFixReason's message shape: the heading, then up to 3 missing names.
59
+ const { heading, missing } = first;
60
+ const names = missingSymbolNames(missing);
61
+ // Every other suspect is still named, shortened: a doctor check is one line.
62
+ const more = andMore(rest, (p) => `"${truncate(p.heading, FIX_CLAIM_HEADING_MAX)}"`, "record(s)");
63
+ return warnOutcome(`BUGS.md records "${heading}" as Fixed, but none of the symbols its Fix paragraph names ` +
64
+ `exist on this tree: ${names}${more} — land the fix or keep the bug Open / refresh a stale record`);
65
+ }
66
+ /** The fixed-record validation-gap twin of checkFixClaims, standing in for plans/repair-traces.md's
67
+ * invariant 2 ("the vocabulary is closed and lives in one place"): the bugfix guidance tells every
68
+ * run to write exactly one `**Validation gap:**` line with a known tag, and `none` rather than no
69
+ * line at all so a tally's denominator stays honest. Nothing checks that a Fixed entry actually
70
+ * carries such a line or that its tag is one of the closed vocabulary — a typo like `no-reprod`
71
+ * lands as its own key on `tumwater report`'s `Validation gaps` line and matches no
72
+ * closed-vocabulary consumer that enumerates the six non-`none` tags, and an entry with no line at
73
+ * all is invisible to every one of them. Re-reads the newest Fixed records with the same
74
+ * FIX_CLAIMS_CHECKED bound (pre-convention history would otherwise warn forever) and warns on a
75
+ * missing or unrecognized tag. An entry declaring `unclear-invariant` should also have pinned the
76
+ * reconstructed guarantee in docs/invariants.md in the same commit — a fix that names no such
77
+ * section warns with that nudge (informational: code-pinned or prose-that-skips-the-filename fixes
78
+ * may spuriously warn). A warn, never a fail, like its sibling: a suspect record is
79
+ * operator signal, not a broken environment. */
80
+ export function checkValidationGaps(root) {
81
+ const doc = readDocChecked(path.join(root, "BUGS.md"), "BUGS.md", "no BUGS.md — nothing to verify");
82
+ if (typeof doc !== "string")
83
+ return doc;
84
+ const records = fixedEntriesAt(root, doc).slice(0, FIX_CLAIMS_CHECKED);
85
+ const suspects = [];
86
+ const unpinned = [];
87
+ for (const { title, body } of records) {
88
+ const tag = validationGapTag(body);
89
+ if (tag === null)
90
+ suspects.push({ title, problem: "missing" });
91
+ else if (!VALIDATION_GAP_TAGS.includes(tag))
92
+ suspects.push({ title, problem: "unrecognized", tag });
93
+ else if (tag === "unclear-invariant" && !body.toLowerCase().includes("docs/invariants.md"))
94
+ unpinned.push({ title });
95
+ }
96
+ const [first, ...rest] = suspects;
97
+ if (!first) {
98
+ if (unpinned.length > 0) {
99
+ const up = unpinned[0];
100
+ const more = andMore(unpinned.slice(1), (s) => `"${truncate(s.title, FIX_CLAIM_HEADING_MAX)}"`, "record(s)");
101
+ return warnOutcome(`BUGS.md record "${up.title}" is an \`unclear-invariant\` fix but names no ` +
102
+ `docs/invariants.md section — record the reconstructed guarantee there (a "Pinned by" line) in the ` +
103
+ `same commit so the next bug in that area does not reconstruct it again${more}`);
104
+ }
105
+ return okOutcome(`newest ${records.length} Fixed record(s) carry a known Validation gap tag`);
106
+ }
107
+ const more = andMore(rest, (s) => `"${truncate(s.title, FIX_CLAIM_HEADING_MAX)}"`, "record(s)");
108
+ const what = first.problem === "missing"
109
+ ? `BUGS.md record "${first.title}" has no **Validation gap:** line`
110
+ : `BUGS.md record "${first.title}" names unrecognized Validation gap tag "${first.tag}"`;
111
+ const valid = VALIDATION_GAP_TAGS.join(", ");
112
+ return warnOutcome(`${what}${more} — every Fixed entry writes exactly one of: ${valid}; ` +
113
+ `write \`none\` rather than omitting the line (the bugfix role records it; see plans/repair-traces.md)`);
114
+ }
115
+ /** Stranded plans — plan headings filed under the wrong PLANS.md section (the stranded-plan
116
+ * detector, src/backlog/backlog-structure.ts): a `(planned …)` heading sitting under `## Done` is
117
+ * invisible to every Planned reader, and a done-dated heading still under `## Planned` invites
118
+ * a second implementation. The clean role repairs these when it happens to run; this makes
119
+ * the state visible to an operator without waiting for that run. Reads the tree at `root` —
120
+ * the primary checkout IS main's tree — like checkFixClaims. A warn, never a fail: a misplaced
121
+ * heading is operator signal, not a broken environment. */
122
+ export function checkStrandedPlans(root) {
123
+ const doc = readDocChecked(path.join(root, "PLANS.md"), "PLANS.md", "no PLANS.md — nothing to verify");
124
+ if (typeof doc !== "string")
125
+ return doc;
126
+ // PLANS-DONE.md too: finished plans are archived there (plans/backlog-archive.md), and a plan
127
+ // filed under its ## Done is as invisible as one under PLANS.md's. Each entry names its file.
128
+ const stranded = strandedPlanEntriesFor(root);
129
+ const clean = "no plan headings filed under the wrong PLANS.md section";
130
+ if (stranded.length === 0)
131
+ return okOutcome(clean);
132
+ const [first, ...rest] = stranded;
133
+ if (!first)
134
+ return okOutcome(clean);
135
+ const more = andMore(rest, (e) => `"${truncate(e.title, FIX_CLAIM_HEADING_MAX)}"`);
136
+ const file = first.file ?? "PLANS.md";
137
+ return warnOutcome(`${file} has a plan stranded under ## ${first.section}: "${first.title}"${more} — ` +
138
+ `move it under ## ${first.section === "Done" ? "Planned" : "Done"}${file === "PLANS.md" ? "" : " in PLANS.md"} ` +
139
+ "(the clean role repairs these)");
140
+ }
141
+ /** Finished history still in the live backlog files (plans/backlog-archive.md): entries under
142
+ * PLANS.md's `## Done` or BUGS.md's `## Fixed` that have not moved to PLANS-DONE.md or
143
+ * BUGS-FIXED.md. Every run that reads the live file pays for them. A starting team archives them
144
+ * unless that is refused (a dirty file, a checkout off the base branch), and a hand commit can put
145
+ * new ones there between starts, so this names how many there are and the fix. A warn, never a
146
+ * fail: the team works either way, only more expensively. */
147
+ export function checkLiveFinished(root) {
148
+ const live = liveFinishedHistory(root);
149
+ if (live.length === 0)
150
+ return okOutcome("PLANS.md and BUGS.md hold no finished entries — they are archived");
151
+ const parts = live.map((l) => `${l.file} ## ${l.section} holds ${plural(l.entries, "finished entry", "finished entries")} (${Math.round(l.bytes / 1024)} KB)`);
152
+ return warnOutcome(`${parts.join("; ")} — every run that reads the file pays for them; ` +
153
+ "`tumwater archive` moves them to the archive files and commits them (a starting team does too)");
154
+ }
155
+ /** Duplicated `## ` headings already on main (the duplicate-heading half of the backlog-structure
156
+ * check, src/backlog/backlog-structure.ts): a `## Done` a past merge or conflict resolution added
157
+ * twice leaves every later entry after the first section invisible to the section readers, and
158
+ * nothing repairs it on its own. Reads the tree at `root` — the primary checkout IS main's tree —
159
+ * like checkFixClaims. A warn, never a fail: existing damage is operator signal, not a broken
160
+ * environment, and a merge that adds another copy of the heading trips the gate's same rule (a) —
161
+ * src/backlog/backlog-structure.ts fires only when a count EXCEEDS the merge-base's, so an edit
162
+ * that leaves an existing duplicate alone passes and the duplicate itself needs a deliberate
163
+ * removal edit. */
164
+ export function checkBacklogHeadings(root) {
165
+ const duplicates = [];
166
+ for (const file of BACKLOG_FILES) {
167
+ const p = path.join(root, file);
168
+ if (!fs.existsSync(p))
169
+ continue; // Absent file (early repo): contributes nothing.
170
+ const read = readDoc(p);
171
+ if ("error" in read) {
172
+ duplicates.push(`${file} (unreadable)`);
173
+ continue;
174
+ }
175
+ const { doc } = read;
176
+ for (const title of duplicateHeadings(doc))
177
+ duplicates.push(`${file} "## ${title}"`);
178
+ }
179
+ if (duplicates.length === 0)
180
+ return okOutcome("no duplicated ## section headings in the backlog files");
181
+ const [first, ...rest] = duplicates;
182
+ const more = andMore(rest, (d) => d);
183
+ return warnOutcome(`${first}${more} appears more than once — readers take the first section of a name, so ` +
184
+ `later entries are invisible; keep one heading per name (the gate blocks only a merge ` +
185
+ `that adds another copy — remove the duplicate in a deliberate edit)`);
186
+ }
187
+ /** Stalled plans — planned plans held by a **Needs review …** or **Needs replan …** note,
188
+ * parked until the plan role splits or replans them. No role takes a held entry (the claim
189
+ * gate refuses one) and nothing clears the note on its own, so the state is visible only as
190
+ * the `[needs review]`/`[needs replan]` marks in the backlog listings; this names them so an
191
+ * operator gets a single-line health signal from `tumwater doctor`. Reads the tree at `root`
192
+ * — the primary checkout IS main's tree — like checkRefusedEntries, which is also where
193
+ * refusal notes (a different hold) are surfaced. entryHold (src/backlog/backlog-eligibility.ts)
194
+ * already classifies the notes, so this check adds no parsing: only the planned entries whose
195
+ * hold is exactly needs-review, needs-replan, or held-for-operator are collected. A warn, never
196
+ * a fail — the checkRefusedEntries precedent: a parked plan is operator signal, not a broken
197
+ * environment; the plan role resolves the first two itself, and a held-for-operator plan is
198
+ * the operator's to release (`tumwater plan --release`). */
199
+ export function checkStalledPlans(root) {
200
+ const planned = plannedPlanEntries(root);
201
+ const stalled = planned.filter((entry) => {
202
+ const hold = entryHold(entry, planned);
203
+ return hold === "needs-review" || hold === "needs-replan" || hold === "held-for-operator"; // vocabulary-ok: the held-for-operator hold's key
204
+ });
205
+ const [first, ...rest] = stalled;
206
+ if (!first)
207
+ return okOutcome("no planned plan is held by a Needs-review, Needs-replan, or held-for-operator note"); // vocabulary-ok: names the held-for-operator hold
208
+ const more = andMore(rest, (e) => `"${truncate(e.title, FIX_CLAIM_HEADING_MAX)}"`);
209
+ const hold = entryHold(first, planned);
210
+ const note = (first.body.split(/\n/).find((line) => line.trimStart().startsWith("**Needs review ") || line.trimStart().startsWith("**Needs replan ")) ?? "").replace(/^\s*\*\*/, "").replace("**", "");
211
+ if (hold === "held-for-operator") { // vocabulary-ok: the held-for-operator hold's key
212
+ return warnOutcome(`PLANS.md plan "${truncate(first.title, FIX_CLAIM_HEADING_MAX)}" is held for the operator ${heldCount(first.body)} times${more} — release it with tumwater plan --release`); // vocabulary-ok: a held-for-operator plan is the operator's to release
213
+ }
214
+ return warnOutcome(`PLANS.md plan "${truncate(first.title, FIX_CLAIM_HEADING_MAX)}" is held by a ${hold === "needs-review" ? "Needs review" : "Needs replan"} note ` +
215
+ `(${truncate(note, REFUSAL_NOTE_MAX)})${more} — the plan role resolves it by splitting or replanning`);
216
+ }
217
+ /** Plan dependencies — mistakes in the planned plans' `requires … landed` clauses that the claim
218
+ * allocator (entryHoldAgainst) cannot see: a ref naming no heading (the hold is void, so the
219
+ * dependent is claimable now), a clause that parses to no refs (it holds nothing), and a
220
+ * dependency cycle (every part on it stays blocked forever). Reads the tree at `root` through
221
+ * planDependencyProblems. A warn, never a fail, like checkStalledPlans: the plan role writes the
222
+ * clauses by hand and fixes them itself. The most serious kind is named — cycle, then dangling,
223
+ * then unparsed — with a count of the other kinds. */
224
+ export function planDependencyProblem(root) {
225
+ const { cycle, dangling, unparsed, misworded } = planDependencyProblems(plannedPlanEntries(root), donePlans(root));
226
+ const [firstDangling, ...restDangling] = dangling;
227
+ const [firstUnparsed, ...restUnparsed] = unparsed;
228
+ const [firstMisworded, ...restMisworded] = misworded;
229
+ const quote = (t) => `"${truncate(t, FIX_CLAIM_HEADING_MAX)}"`;
230
+ const others = (parts) => parts.some(([n]) => n > 0) ? ` (also ${parts.map(([n, noun]) => `${n} ${noun}`).join(", ")})` : "";
231
+ const danglingMsg = (d) => `PLANS.md plan ${quote(d.title)} requires ${d.ref}, which no PLANS.md heading carries — the hold is void, ` +
232
+ "so the plan is claimable now; fix the series name or part number";
233
+ const unparsedMsg = (t) => `PLANS.md plan ${quote(t)} has a \`requires … landed\` clause that names no part the allocator can read — ` + // vocabulary-ok: PLANS.md heading syntax
234
+ "it holds nothing; write each ref as `<Series> i/n` or give the heading a `<Series>, part i/n:` prefix";
235
+ const miswordedMsg = (w) => `PLANS.md plan ${quote(w.title)} names ${w.ref} as a prerequisite without a \`requires … landed\` clause — ` + // vocabulary-ok: PLANS.md heading syntax
236
+ "the claim allocator ignores that wording and hands the part out now; write it as `requires part i/n landed`"; // vocabulary-ok: PLANS.md heading syntax
237
+ if (cycle !== null) {
238
+ return { kind: "cycle", message: `plan dependency cycle: ${cycle.join(" → ")} — every part on it stays blocked forever; ` +
239
+ "edit one heading's `requires … landed` clause to break it" + // vocabulary-ok: PLANS.md heading syntax
240
+ others([[dangling.length, "dangling ref(s)"], [unparsed.length, "unparsed clause(s)"], [misworded.length, "misworded dependency(ies)"]]) };
241
+ }
242
+ if (firstDangling) {
243
+ return { kind: "dangling", message: danglingMsg(firstDangling) +
244
+ andMore(restDangling, (d) => `${quote(d.title)} → ${d.ref}`, "ref(s)") +
245
+ others([[unparsed.length, "unparsed clause(s)"], [misworded.length, "misworded dependency(ies)"]]) };
246
+ }
247
+ if (firstUnparsed) {
248
+ return { kind: "unparsed", message: unparsedMsg(firstUnparsed) + andMore(restUnparsed, quote, "clause(s)") +
249
+ others([[misworded.length, "misworded dependency(ies)"]]) };
250
+ }
251
+ if (firstMisworded) {
252
+ return { kind: "misworded", message: miswordedMsg(firstMisworded) +
253
+ andMore(restMisworded, (w) => `${quote(w.title)} → ${w.ref}`, "dependency(ies)") };
254
+ }
255
+ return null;
256
+ }
257
+ /** The doctor's `plan dependencies` row: warn on the first problem planDependencyProblem finds
258
+ * (sharing its problem set, severity order, and wording), ok when all four rules pass. A warn,
259
+ * never a fail: a broken clause is operator signal, not a broken environment. */
260
+ export function checkPlanDependencies(root) {
261
+ const p = planDependencyProblem(root);
262
+ return p ? warnOutcome(p.message) : okOutcome("every planned plan's requires clause names a known part, with no cycle or misworded dependency");
263
+ }