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.
- package/README.md +27 -13
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-args.js +8 -9
- package/dist/src/agent/agent-backend.js +8 -6
- package/dist/src/agent/backend-traits.js +42 -25
- package/dist/src/agent/claude/claude-account.js +71 -204
- package/dist/src/agent/claude/claude-args.js +9 -6
- package/dist/src/agent/claude/claude-backend.js +10 -10
- package/dist/src/agent/claude/claude-env.js +1 -1
- package/dist/src/agent/claude/claude-stream.js +4 -3
- package/dist/src/agent/claude/claude-write-guard.js +49 -0
- package/dist/src/agent/claude/write-guard-hook.js +13 -0
- package/dist/src/agent/codex/codex-account.js +119 -0
- package/dist/src/agent/codex/codex-args.js +181 -0
- package/dist/src/agent/codex/codex-backend.js +279 -0
- package/dist/src/agent/codex/codex-catalog.js +97 -0
- package/dist/src/agent/codex/codex-env.js +18 -0
- package/dist/src/agent/codex/codex-hook-trust.js +246 -0
- package/dist/src/agent/codex/codex-patch-paths.js +36 -0
- package/dist/src/agent/codex/codex-rollout.js +179 -0
- package/dist/src/agent/codex/codex-stream.js +402 -0
- package/dist/src/agent/codex/codex-write-guard.js +86 -0
- package/dist/src/agent/codex/write-guard-hook.js +12 -0
- package/dist/src/agent/image-input.js +29 -24
- package/dist/src/agent/notes-shim.js +11 -12
- package/dist/src/agent/opencode/opencode-args.js +0 -1
- package/dist/src/agent/opencode/opencode-backend.js +12 -9
- package/dist/src/agent/opencode/opencode-catalog.js +55 -29
- package/dist/src/agent/opencode/opencode-config.js +19 -14
- package/dist/src/agent/opencode/opencode-env.js +0 -1
- package/dist/src/agent/opencode/opencode-session-id.js +1 -2
- package/dist/src/agent/opencode/opencode-stream.js +1 -1
- package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
- package/dist/src/agent/role-notes-write.js +5 -6
- package/dist/src/agent/run-agent.js +465 -448
- package/dist/src/agent/run-settle.js +214 -0
- package/dist/src/agent/session-ledger.js +86 -16
- package/dist/src/agent/subscription-account.js +220 -0
- package/dist/src/agent/write-guard.js +148 -0
- package/dist/src/backlog/backlog-archive.js +246 -55
- package/dist/src/backlog/backlog-cli-view.js +280 -0
- package/dist/src/backlog/backlog-cli.js +85 -274
- package/dist/src/backlog/backlog-commit.js +4 -5
- package/dist/src/backlog/backlog-eligibility.js +175 -242
- package/dist/src/backlog/backlog-index.js +149 -0
- package/dist/src/backlog/backlog-md.js +54 -6
- package/dist/src/backlog/backlog-parts.js +254 -0
- package/dist/src/backlog/backlog-release.js +117 -0
- package/dist/src/backlog/backlog-render.js +27 -0
- package/dist/src/backlog/backlog-structure.js +99 -156
- package/dist/src/backlog/backlog-write.js +40 -127
- package/dist/src/backlog/backlog.js +21 -6
- package/dist/src/backlog/question-answer.js +21 -15
- package/dist/src/baseline/main-baseline.js +89 -20
- package/dist/src/baseline/main-culprit.js +94 -0
- package/dist/src/baseline/main-guardrails.js +209 -0
- package/dist/src/baseline/main-red.js +139 -32
- package/dist/src/baseline/main-revert.js +133 -0
- package/dist/src/baseline/main-watch.js +184 -0
- package/dist/src/brief/brief-edit.js +9 -11
- package/dist/src/brief/brief.js +31 -51
- package/dist/src/budget/budget.js +30 -20
- package/dist/src/budget/fallback-breaker.js +26 -27
- package/dist/src/build/build-check-detect.js +23 -15
- package/dist/src/build/build-check-events.js +25 -6
- package/dist/src/build/build-check-report.js +49 -9
- package/dist/src/build/build-check-scoped.js +18 -11
- package/dist/src/build/build-check.js +2 -1
- package/dist/src/build/build-info.js +2 -3
- package/dist/src/build/build-stage.js +9 -14
- package/dist/src/build/dep-install.js +1 -2
- package/dist/src/change/change-data.js +123 -31
- package/dist/src/change/change-render.js +46 -5
- package/dist/src/cli/brief-command.js +3 -4
- package/dist/src/cli/cli-args.js +118 -33
- package/dist/src/cli/cli-command-args.js +46 -18
- package/dist/src/cli/cli-editor.js +2 -1
- package/dist/src/cli/cli-flag-specs.js +150 -45
- package/dist/src/cli/cli-init.js +51 -0
- package/dist/src/cli/cli-marker-commands.js +4 -4
- package/dist/src/cli/cli-query-commands.js +308 -590
- package/dist/src/cli/cli-repo-gate.js +11 -1
- package/dist/src/cli/cli-role-command.js +420 -0
- package/dist/src/cli/cli-start-summary.js +160 -0
- package/dist/src/cli/{cli-run.js → cli-start.js} +165 -237
- package/dist/src/cli/completion.js +149 -22
- package/dist/src/cli/config-commands.js +41 -18
- package/dist/src/cli/follow-view.js +75 -0
- package/dist/src/cli/help.js +189 -73
- package/dist/src/cli/log-commands.js +148 -82
- package/dist/src/cli/question-commands.js +205 -10
- package/dist/src/cli.js +106 -43
- package/dist/src/collections.js +26 -10
- package/dist/src/concurrency/check-permit.js +20 -3
- package/dist/src/concurrency/lock.js +17 -13
- package/dist/src/concurrency/provider-lease.js +44 -8
- package/dist/src/concurrency/semaphore.js +102 -26
- package/dist/src/config/config-aliases.js +0 -1
- package/dist/src/config/config-editable-keys.js +13 -1
- package/dist/src/config/config-fallbacks.js +93 -56
- package/dist/src/config/config-field-checks.js +40 -10
- package/dist/src/config/config-keys.js +30 -8
- package/dist/src/config/config-live.js +63 -24
- package/dist/src/config/config-request.js +137 -0
- package/dist/src/config/config-schema.js +31 -24
- package/dist/src/config/config-selector-checks.js +123 -0
- package/dist/src/config/config-validation-agents.js +80 -0
- package/dist/src/config/config-validation-model.js +116 -0
- package/dist/src/config/config-validation.js +141 -199
- package/dist/src/config/config-views.js +127 -53
- package/dist/src/config/config-write-core.js +135 -0
- package/dist/src/config/config-write.js +131 -264
- package/dist/src/config/config.js +48 -38
- package/dist/src/config/model-selector.js +34 -34
- package/dist/src/doctor/doctor-backlog-bugs.js +287 -0
- package/dist/src/doctor/doctor-backlog-plans.js +263 -0
- package/dist/src/doctor/doctor-backlog.js +100 -496
- package/dist/src/doctor/doctor-check-names.js +81 -0
- package/dist/src/doctor/doctor-checks.js +52 -579
- package/dist/src/doctor/doctor-claude-checks.js +145 -0
- package/dist/src/doctor/doctor-codex-checks.js +279 -0
- package/dist/src/doctor/doctor-contention-checks.js +380 -0
- package/dist/src/doctor/doctor-holds.js +82 -66
- package/dist/src/doctor/doctor-install.js +2 -1
- package/dist/src/doctor/doctor-model-checks.js +236 -402
- package/dist/src/doctor/doctor-notify.js +3 -4
- package/dist/src/doctor/doctor-opencode-checks.js +162 -0
- package/dist/src/doctor/doctor-orphans.js +182 -63
- package/dist/src/doctor/doctor-project-checks.js +303 -0
- package/dist/src/doctor/doctor.js +121 -77
- package/dist/src/doctor/image-probe.js +21 -8
- package/dist/src/doctor/outcome.js +3 -2
- package/dist/src/doctor/probe.js +7 -11
- package/dist/src/events/event-format.js +181 -86
- package/dist/src/events/event-labels.js +12 -5
- package/dist/src/events/event-legacy.js +39 -15
- package/dist/src/events/event-read.js +39 -39
- package/dist/src/events/event-tally.js +16 -0
- package/dist/src/events/event-window.js +60 -24
- package/dist/src/events/events.js +30 -13
- package/dist/src/events/notify.js +7 -7
- package/dist/src/failure/failure-cluster.js +12 -12
- package/dist/src/failure/failure-data.js +56 -56
- package/dist/src/failure/failure-render.js +31 -23
- package/dist/src/failure/failure-state-change.js +17 -12
- package/dist/src/failure/time-spend.js +99 -35
- package/dist/src/files/files.js +111 -35
- package/dist/src/files/json-files.js +3 -3
- package/dist/src/files/json-object.js +28 -7
- package/dist/src/files/stat-cache.js +2 -2
- package/dist/src/files/tail.js +1 -1
- package/dist/src/gates/bootstrap-gates.js +4 -5
- package/dist/src/gates/budget-gates.js +177 -84
- package/dist/src/gates/disk-gate.js +1 -1
- package/dist/src/gates/gate-polls.js +108 -58
- package/dist/src/gates/maintenance-quota.js +10 -13
- package/dist/src/gates/pause-gates.js +28 -10
- package/dist/src/gates/readiness.js +7 -3
- package/dist/src/gates/role-cap-gates.js +8 -8
- package/dist/src/gates/startup-gate.js +94 -32
- package/dist/src/gates/streak-gate.js +54 -30
- package/dist/src/git/commit-message.js +77 -46
- package/dist/src/git/git-diff.js +16 -10
- package/dist/src/git/git-run.js +79 -38
- package/dist/src/git/git.js +36 -34
- package/dist/src/git/slot-salvage.js +253 -0
- package/dist/src/git/slots-state.js +6 -6
- package/dist/src/git/worktree-pool.js +132 -38
- package/dist/src/git/worktree-use.js +4 -11
- package/dist/src/git/worktree.js +94 -37
- package/dist/src/gui/gui-admin-endpoints.js +96 -0
- package/dist/src/gui/gui-args.js +79 -8
- package/dist/src/gui/gui-backlog-commands.js +42 -18
- package/dist/src/gui/gui-backlog-endpoints.js +155 -0
- package/dist/src/gui/gui-command.js +8 -13
- package/dist/src/gui/gui-config-commands.js +94 -72
- package/dist/src/gui/gui-endpoint-commands.js +4 -3
- package/dist/src/gui/gui-endpoint-common.js +4 -4
- package/dist/src/gui/gui-endpoints.js +11 -380
- package/dist/src/gui/gui-history-endpoints.js +301 -0
- package/dist/src/gui/gui-operator-commands.js +108 -13
- package/dist/src/gui/gui-prompt-commands.js +11 -14
- package/dist/src/gui/gui-report-endpoints.js +90 -0
- package/dist/src/gui/gui-server.js +11 -4
- package/dist/src/gui/gui-team-commands.js +7 -7
- package/dist/src/gui/http-body.js +1 -1
- package/dist/src/history/history-data.js +122 -234
- package/dist/src/history/history-queued.js +147 -0
- package/dist/src/history/history-render.js +42 -22
- package/dist/src/history/history.js +193 -49
- package/dist/src/inbox/inbox-attachments.js +29 -30
- package/dist/src/inbox/inbox-cancel.js +10 -11
- package/dist/src/inbox/inbox-edit.js +9 -9
- package/dist/src/inbox/inbox-submit.js +18 -18
- package/dist/src/inbox/inbox-top.js +25 -9
- package/dist/src/inbox/inbox.js +42 -43
- package/dist/src/inbox/pending-prompt.js +26 -26
- package/dist/src/inbox/prompt-commands.js +62 -49
- package/dist/src/inbox/prompt-not-before.js +2 -2
- package/dist/src/init/init-templates.js +1 -2
- package/dist/src/init/init.js +28 -23
- package/dist/src/merge/backlog-conflicts.js +215 -35
- package/dist/src/merge/merge-batch.js +14 -8
- package/dist/src/merge/merge-cancel.js +53 -0
- package/dist/src/merge/merge-check-failures.js +44 -22
- package/dist/src/merge/merge-core.js +81 -32
- package/dist/src/merge/merge-diff.js +5 -4
- package/dist/src/merge/merge-drain.js +150 -11
- package/dist/src/merge/merge-git.js +177 -38
- package/dist/src/merge/merge-pipeline.js +15 -3
- package/dist/src/merge/merge-post-review.js +90 -0
- package/dist/src/merge/merge-queue-render.js +49 -11
- package/dist/src/merge/merge-queue.js +31 -15
- package/dist/src/merge/merge-requeue.js +102 -0
- package/dist/src/merge/merge-slot.js +61 -16
- package/dist/src/merge/merge-stack.js +33 -11
- package/dist/src/merge/merge-to-main.js +86 -27
- package/dist/src/merge/merge-top.js +69 -0
- package/dist/src/merge/merge-verify.js +12 -10
- package/dist/src/notes-cli.js +30 -15
- package/dist/src/operator/operator-commands.js +192 -81
- package/dist/src/operator/operator-intent.js +69 -42
- package/dist/src/operator/operator-requests.js +35 -40
- package/dist/src/operator/retire.js +5 -6
- package/dist/src/operator/revert.js +142 -34
- package/dist/src/orchestrator/orchestrator-launch.js +60 -52
- package/dist/src/orchestrator/orchestrator-poll.js +320 -0
- package/dist/src/orchestrator/orchestrator-scheduling.js +68 -369
- package/dist/src/orchestrator/orchestrator.js +128 -369
- package/dist/src/orchestrator/retention.js +42 -8
- package/dist/src/orchestrator/scheduling-admit.js +116 -0
- package/dist/src/orchestrator/scheduling-scope.js +201 -0
- package/dist/src/orchestrator/scheduling-skip.js +185 -0
- package/dist/src/orchestrator/startup-migration-steps.js +8 -7
- package/dist/src/orchestrator/startup-migrations.js +11 -7
- package/dist/src/paths.js +116 -49
- package/dist/src/pi/command-shape.js +12 -1
- package/dist/src/pi/pi-args.js +6 -4
- package/dist/src/pi/pi-backend.js +6 -6
- package/dist/src/pi/pi-bin.js +54 -63
- package/dist/src/pi/pi-listed-models.js +53 -0
- package/dist/src/pi/pi-models.js +63 -30
- package/dist/src/pi/pi-run-result.js +1 -1
- package/dist/src/pi/pi-settings.js +1 -1
- package/dist/src/pi/pi-stream.js +6 -6
- package/dist/src/pi/pi-watchdogs.js +18 -19
- package/dist/src/pi-extension/bounded-output.js +5 -3
- package/dist/src/pi-extension/context-budget.js +2 -2
- package/dist/src/pi-extension/context-shake.js +1 -1
- package/dist/src/pi-extension/context-window.js +0 -1
- package/dist/src/pi-extension/full-output.js +3 -10
- package/dist/src/pi-extension/strip-thinking.js +0 -1
- package/dist/src/pi-extension/trim-system-prompt.js +3 -4
- package/dist/src/pi-extension/write-guard.js +37 -0
- package/dist/src/process/launch-services.js +1 -2
- package/dist/src/process/node-bin.js +16 -0
- package/dist/src/process/process-group.js +8 -18
- package/dist/src/process/process.js +41 -2
- package/dist/src/process/run-marker.js +18 -6
- package/dist/src/process/supervisor.js +22 -21
- package/dist/src/prompt/dialect.js +17 -2
- package/dist/src/prompt/gate-notes.js +203 -0
- package/dist/src/prompt/gate-prompts.js +121 -211
- package/dist/src/prompt/notes-backlog.js +181 -0
- package/dist/src/prompt/principles.js +1 -2
- package/dist/src/prompt/prompt-followup.js +19 -14
- package/dist/src/prompt/prompt.js +129 -204
- package/dist/src/redeploy/redeploy-policy.js +24 -19
- package/dist/src/redeploy/redeploy-probes.js +1 -1
- package/dist/src/redeploy/redeploy.js +2 -3
- package/dist/src/redeploy/redeployer.js +20 -20
- package/dist/src/redeploy/self-reload.js +2 -1
- package/dist/src/report/report-data.js +161 -77
- package/dist/src/report/report-fold.js +129 -52
- package/dist/src/report/report-main-red.js +55 -0
- package/dist/src/report/report-render.js +133 -64
- package/dist/src/report/report-types.js +1 -0
- package/dist/src/report/report.js +26 -9
- package/dist/src/request-timeouts.js +2 -2
- package/dist/src/review/known-flakes.js +100 -1
- package/dist/src/review/post-merge-review.js +106 -0
- package/dist/src/review/protected-edits.js +4 -5
- package/dist/src/review/review-followup.js +8 -8
- package/dist/src/review/review-precheck.js +66 -23
- package/dist/src/review/review-run.js +395 -0
- package/dist/src/review/review-verdict.js +2 -2
- package/dist/src/review/review.js +155 -360
- package/dist/src/review/secret-leaks.js +36 -16
- package/dist/src/review/suite-rerun.js +0 -1
- package/dist/src/roles/legacy-role-migration.js +80 -7
- package/dist/src/roles/role-catalog.js +69 -12
- package/dist/src/roles/role-guidance.js +55 -20
- package/dist/src/roles/role-labels.js +1 -2
- package/dist/src/roles/role-render.js +67 -50
- package/dist/src/roles/role-view.js +59 -47
- package/dist/src/roles/roles.js +31 -31
- package/dist/src/roles/runner-ids.js +97 -0
- package/dist/src/run/acceptance-greps.js +119 -0
- package/dist/src/run/feedback-filings.js +51 -0
- package/dist/src/{tick → run}/qa-coverage.js +9 -27
- package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
- package/dist/src/run/run-detail-data.js +179 -0
- package/dist/src/run/run-detail-render.js +98 -0
- package/dist/src/run/run-detail.js +168 -0
- package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +72 -60
- package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
- package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +108 -73
- package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
- package/dist/src/{tick/tick-stage.js → run/run-stage.js} +108 -50
- package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
- package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
- package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +91 -56
- package/dist/src/run/selfcheck-digest.js +40 -0
- package/dist/src/run/stage-backlog-checks.js +249 -0
- package/dist/src/run/stage-check.js +172 -0
- package/dist/src/run/stage-path-checks.js +730 -0
- package/dist/src/{loop → runner}/leftover.js +80 -25
- package/dist/src/{loop → runner}/model-fallback.js +12 -12
- package/dist/src/runner/revision.js +202 -0
- package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
- package/dist/src/runner/runner-outcome.js +107 -0
- package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
- package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +21 -15
- package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +218 -240
- package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
- package/dist/src/runner/runner-state.js +102 -0
- package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
- package/dist/src/scheduling/backoff.js +38 -38
- package/dist/src/scheduling/claims.js +35 -22
- package/dist/src/scheduling/once-round.js +22 -22
- package/dist/src/scheduling/quiet-hours.js +5 -6
- package/dist/src/scheduling/scheduling.js +74 -64
- package/dist/src/scheduling/work-merged-cache.js +2 -2
- package/dist/src/status/status-budget.js +55 -47
- package/dist/src/status/status-data.js +43 -16
- package/dist/src/status/status-inbox.js +2 -2
- package/dist/src/status/status-install.js +0 -1
- package/dist/src/status/status-polls.js +28 -28
- package/dist/src/status/status-roles.js +39 -13
- package/dist/src/team/error-storm.js +8 -8
- package/dist/src/team/failure-spread.js +2 -2
- package/dist/src/team/orchestrator-info.js +15 -9
- package/dist/src/team/reclaim.js +5 -6
- package/dist/src/team/team-hold.js +7 -7
- package/dist/src/team/team-polls.js +91 -50
- package/dist/src/team/team-state.js +8 -2
- package/dist/src/text/datetime.js +63 -17
- package/dist/src/text/format.js +48 -11
- package/dist/src/text/markdown.js +8 -5
- package/dist/src/text/phrases.js +57 -23
- package/dist/src/text/suggest.js +1 -1
- package/dist/src/text/text.js +67 -13
- package/dist/src/ui/badges.js +65 -37
- package/dist/src/ui/gui/gui-client-backlog.js +292 -0
- package/dist/src/ui/gui/gui-client-boot.js +32 -26
- package/dist/src/ui/gui/gui-client-composer.js +8 -9
- package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
- package/dist/src/ui/gui/gui-client-drawer.js +126 -285
- package/dist/src/ui/gui/gui-client-feed.js +271 -0
- package/dist/src/ui/gui/gui-client-history.js +434 -67
- package/dist/src/ui/gui/gui-client-labels.js +1 -2
- package/dist/src/ui/gui/gui-client-markdown.js +1 -2
- package/dist/src/ui/gui/gui-client-model.js +27 -36
- package/dist/src/ui/gui/gui-client-operator.js +55 -51
- package/dist/src/ui/gui/gui-client-pending.js +227 -23
- package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
- package/dist/src/ui/gui/gui-client-report.js +87 -427
- package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
- package/dist/src/ui/gui/gui-client-settings.js +56 -4
- package/dist/src/ui/gui/gui-client-team.js +25 -465
- package/dist/src/ui/gui/gui-client.js +27 -25
- package/dist/src/ui/gui/gui-icons.js +0 -2
- package/dist/src/ui/gui/gui-page.js +9 -7
- package/dist/src/ui/gui/gui-styles.js +34 -34
- package/dist/src/ui/progress-data.js +9 -11
- package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +31 -31
- package/dist/src/ui/status-model.js +133 -125
- package/dist/src/ui/status-payload.js +136 -49
- package/dist/src/ui/status-render.js +101 -82
- package/dist/src/ui/team-alerts.js +248 -57
- package/dist/src/ui/tone.js +5 -6
- package/dist/src/ui/transcript-run.js +58 -0
- package/dist/src/ui/transcript-tail.js +28 -20
- package/dist/src/ui/transcript.js +63 -23
- package/dist/src/ui/tui/tui-app.js +0 -1
- package/dist/src/ui/tui/tui-backlog.js +51 -14
- package/dist/src/ui/tui/tui-editor-answer.js +27 -31
- package/dist/src/ui/tui/tui-editor-attach.js +3 -5
- package/dist/src/ui/tui/tui-editor-backends.js +132 -0
- package/dist/src/ui/tui/tui-editor-brief.js +10 -12
- package/dist/src/ui/tui/tui-editor-budget.js +35 -31
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
- package/dist/src/ui/tui/tui-editor-concurrency.js +24 -0
- package/dist/src/ui/tui/tui-editor-config.js +86 -0
- package/dist/src/ui/tui/tui-editor-entry.js +36 -47
- package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
- package/dist/src/ui/tui/tui-editor-instances.js +37 -0
- package/dist/src/ui/tui/tui-editor-instructions.js +136 -0
- package/dist/src/ui/tui/tui-editor-interval.js +11 -9
- package/dist/src/ui/tui/tui-editor-model.js +29 -0
- package/dist/src/ui/tui/tui-editor-notes.js +3 -5
- package/dist/src/ui/tui/tui-editor-notify.js +25 -0
- package/dist/src/ui/tui/tui-editor-pause.js +48 -59
- package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
- package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
- package/dist/src/ui/tui/tui-editor-queued.js +5 -9
- package/dist/src/ui/tui/tui-editor-quiet.js +99 -35
- package/dist/src/ui/tui/tui-editor-retire.js +4 -6
- package/dist/src/ui/tui/tui-editor-role-key.js +76 -0
- package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
- package/dist/src/ui/tui/tui-editor-search.js +45 -33
- package/dist/src/ui/tui/tui-editor-session.js +60 -34
- package/dist/src/ui/tui/tui-editor-slots.js +23 -0
- package/dist/src/ui/tui/tui-editors.js +35 -2
- package/dist/src/ui/tui/tui-frame.js +56 -11
- package/dist/src/ui/tui/tui-history.js +8 -9
- package/dist/src/ui/tui/tui-input.js +13 -11
- package/dist/src/ui/tui/tui-keymap.js +0 -1
- package/dist/src/ui/tui/tui-keys.js +24 -9
- package/dist/src/ui/tui/tui-pane-activity.js +14 -11
- package/dist/src/ui/tui/tui-pane-backlog.js +171 -126
- package/dist/src/ui/tui/tui-pane-brief.js +0 -1
- package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
- package/dist/src/ui/tui/tui-pane-health.js +58 -8
- package/dist/src/ui/tui/tui-pane-history.js +402 -82
- package/dist/src/ui/tui/tui-pane-pending.js +308 -13
- package/dist/src/ui/tui/tui-pane-queued.js +71 -55
- package/dist/src/ui/tui/tui-pane-report.js +46 -14
- package/dist/src/ui/tui/tui-pane-session.js +25 -6
- package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
- package/dist/src/ui/tui/tui-pane.js +80 -23
- package/dist/src/ui/tui/tui-panes.js +100 -30
- package/dist/src/ui/tui/tui.js +14 -14
- package/dist/src/verdict/fix-claim.js +82 -32
- package/dist/src/verdict/no-change.js +4 -4
- package/dist/src/verdict/refusal.js +40 -16
- package/dist/src/verdict/reply-contract.js +42 -31
- package/dist/src/version.js +0 -1
- package/package.json +2 -1
- package/dist/src/loop/revision.js +0 -70
- package/dist/src/roles/loop-ids.js +0 -77
- package/dist/src/tick/stage-check.js +0 -419
- package/dist/src/tick/telemetry-digest.js +0 -25
- package/dist/src/tick/tick-detail-data.js +0 -108
- package/dist/src/tick/tick-detail.js +0 -163
- package/dist/src/ui/gui/gui-client-sound.js +0 -53
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
import { dailyCost } from "../budget/budget.js";
|
|
2
2
|
import { clipToWidth, displayWidth } from "../text/text-width.js";
|
|
3
3
|
import { compactTokens, usd } from "../text/format.js";
|
|
4
|
-
import {
|
|
4
|
+
import { formatTime, humanAgo, nextRunPhrase, pad2, secondsUntil } from "../text/datetime.js";
|
|
5
5
|
import { projectName } from "../project-name.js";
|
|
6
|
-
import { bootstrapBadge, buildBadge, budgetBadge,
|
|
7
|
-
import { idleClaimText, isActivePhase,
|
|
8
|
-
import {
|
|
6
|
+
import { bootstrapBadge, buildBadge, budgetBadge, subscriptionWindowTone, diskBadge, mergeBadge, mainCheckBadge, mainCheckVerdict, pauseBadge, roleWakeMarker, permitsBadge, checksBadge, quietBadge, runWindowBadge, wakeBadge, workspacesBadge } from "./badges.js";
|
|
7
|
+
import { idleClaimText, isActivePhase, roleRowCells, onErrorStreak, sortRolesByState } from "./status-model.js";
|
|
8
|
+
import { runProgress } from "./run-progress-model.js";
|
|
9
9
|
import { eventTypeLabel } from "../events/event-labels.js";
|
|
10
10
|
import { roleLabel } from "../roles/role-labels.js";
|
|
11
11
|
import { yieldMultiplierFor, yieldMultiplierSuffix } from "../scheduling/backoff.js";
|
|
12
12
|
import { phaseTone, resultTone } from "./tone.js";
|
|
13
13
|
import { teamAlerts } from "./team-alerts.js";
|
|
14
14
|
import { openQuestions } from "../backlog/backlog.js";
|
|
15
|
-
import {
|
|
15
|
+
import { operatorHeldEntries, refusedEntries, stalledPlanEntries } from "../backlog/backlog-eligibility.js";
|
|
16
|
+
import { planDependencyAlert, skippedMergeFiles, staleMerges, staleOpenBugs, stalePrompts, staleWaitingBugs } from "./status-payload.js";
|
|
16
17
|
/** The status RENDER layer: time/token cell formatters and the width-aware table shared by
|
|
17
18
|
* `tumwater status` and the TUI. The labels, badges, and metrics it draws come from the shared
|
|
18
19
|
* display model (status-model.ts); this module decides column widths and layout. Depends on
|
|
19
20
|
* status/status-data.ts one way — rendering reads the snapshot; it never collects team state
|
|
20
|
-
* itself (live
|
|
21
|
+
* itself (live run detail is display-only). */
|
|
21
22
|
function ago(ts) {
|
|
22
23
|
if (!ts)
|
|
23
24
|
return "-";
|
|
24
|
-
|
|
25
|
-
return `${humanSeconds(s)} ago`;
|
|
25
|
+
return humanAgo(ts, Date.now());
|
|
26
26
|
}
|
|
27
|
-
/** The table's `last
|
|
27
|
+
/** The table's `last run` cell: the absolute local time of the last run end alongside its
|
|
28
28
|
* relative age ("14:32:05 · 3m ago"). Zero-padded HH:MM:SS in local time, prefixed `MM-DD `
|
|
29
|
-
* once older than a day so multi-day runs stay unambiguous; "-" for
|
|
29
|
+
* once older than a day so multi-day runs stay unambiguous; "-" for roles that never ran.
|
|
30
30
|
* The GUI renders the same cell (absolute stamp plus relative age) from its own JS copy in
|
|
31
31
|
* gui-client.ts — formatting at each surface, per the fmtTokens precedent. */
|
|
32
|
-
export function
|
|
32
|
+
export function lastRunCell(ts) {
|
|
33
33
|
if (!ts)
|
|
34
34
|
return "-";
|
|
35
35
|
const d = new Date(ts);
|
|
@@ -38,42 +38,42 @@ export function lastTickCell(ts) {
|
|
|
38
38
|
s = `${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${s}`;
|
|
39
39
|
return `${s} · ${ago(ts)}`;
|
|
40
40
|
}
|
|
41
|
-
/** The table's `last merged` cell: when the
|
|
42
|
-
* absolute-time-plus-relative-age shape as the `last
|
|
43
|
-
* columns cannot drift on what "ago" means. A
|
|
44
|
-
*
|
|
41
|
+
/** The table's `last merged` cell: when the role last merged a change on main, in the SAME
|
|
42
|
+
* absolute-time-plus-relative-age shape as the `last run` cell (lastRunCell), so the two
|
|
43
|
+
* columns cannot drift on what "ago" means. A role that has never merged shows the same dash
|
|
44
|
+
* lastRunCell uses for a role that has never run. */
|
|
45
45
|
export function lastMergedCell(ts) {
|
|
46
|
-
return
|
|
46
|
+
return lastRunCell(ts);
|
|
47
47
|
}
|
|
48
|
-
/** The table's `next run` cell: when the
|
|
49
|
-
* operator's question — a
|
|
48
|
+
/** The table's `next run` cell: when the role may run again, or `-` when that is not the
|
|
49
|
+
* operator's question — a role in flight (an active rendered phase: working/reviewing/merge,
|
|
50
50
|
* or `s.running`) has no next run to speak of, and a stopped team renders no schedule at all
|
|
51
|
-
* (its `nextRunAt`s are stale leftovers, not plans). A due idle
|
|
51
|
+
* (its `nextRunAt`s are stale leftovers, not plans). A due idle role reads `now`; a future one
|
|
52
52
|
* reads its remaining time through humanSeconds (the same bucketing the sleeping phase label
|
|
53
53
|
* uses, so the two cannot drift), prefixed `backoff ` while `backoffSeconds > 0` — the wake
|
|
54
54
|
* row-action clears exactly that, so the prefix is what makes waking meaningful. The GUI
|
|
55
55
|
* renders the same rules from its own JS copy in gui-client.ts (fmtNextRun). The `phase` and
|
|
56
|
-
* `teamRunning` arguments come from the caller because neither fact lives on
|
|
56
|
+
* `teamRunning` arguments come from the caller because neither fact lives on RunnerState: the
|
|
57
57
|
* active-phase check needs the rendered label (a merge is visible only there), and the
|
|
58
|
-
* team's running flag lives on the snapshot, not the
|
|
58
|
+
* team's running flag lives on the snapshot, not the role. */
|
|
59
59
|
export function nextRunCell(s, phase, now, teamRunning) {
|
|
60
60
|
if (!teamRunning || s.running || isActivePhase(phase))
|
|
61
61
|
return "-";
|
|
62
62
|
const remain = secondsUntil(s.nextRunAt, now);
|
|
63
63
|
// The yield multiplier rides the cell as `×N` (yield-scaled clocks, PLANS.md): a scalable
|
|
64
|
-
// role whose recent
|
|
64
|
+
// role whose recent runs merged nothing keeps a longer effective gap than nextRunAt's
|
|
65
65
|
// countdown shows, and the suffix is what makes that visible. The multiplier gates the
|
|
66
66
|
// scheduled gap and the main-moved wake in isEligible, so a `now`-due cell at ×4 is a
|
|
67
67
|
// role waiting out its stretched gap, not a broken clock.
|
|
68
68
|
const suffix = yieldMultiplierSuffix(yieldMultiplierFor(s));
|
|
69
69
|
return nextRunPhrase(remain, s.backoffSeconds > 0, "now", false) + suffix;
|
|
70
70
|
}
|
|
71
|
-
/** The table's state cell: a
|
|
71
|
+
/** The table's state cell: a role's phase label (rolePhase, computed once per row by
|
|
72
72
|
* renderStatus so the same label drives the row order), with the current work item prepended
|
|
73
|
-
* while a
|
|
73
|
+
* while a run is in flight ("implement plan X · working 3m · turn 2"). Prepending — not
|
|
74
74
|
* appending — so the item survives ellipsis clipping on narrow terminals; the live detail
|
|
75
|
-
* after it is what gets clipped first. Idle
|
|
76
|
-
* finished
|
|
75
|
+
* after it is what gets clipped first. Idle roles are untouched: their log tail describes a
|
|
76
|
+
* finished run and must not leak its work item into the state cell. (The GUI shows the same
|
|
77
77
|
* item in its own `current` column instead, so its state cell stays clean.) */
|
|
78
78
|
function stateCell(root, s, phase, live) {
|
|
79
79
|
// While under review the log tail's "current work" is the reviewer's own output, not the
|
|
@@ -88,18 +88,19 @@ function stateCell(root, s, phase, live) {
|
|
|
88
88
|
const idle = idleClaimText(s, phase);
|
|
89
89
|
return idle ? `${idle} · ${phase}` : phase;
|
|
90
90
|
}
|
|
91
|
-
const p =
|
|
91
|
+
const p = runProgress(root, s, live);
|
|
92
92
|
const work = p?.currentWork;
|
|
93
93
|
return work ? `${work} · ${phase}` : phase;
|
|
94
94
|
}
|
|
95
95
|
/** Columns allowed to shrink when the table is wider than the terminal, widest offender
|
|
96
|
-
* first: `last result` (holds the
|
|
97
|
-
* `last
|
|
98
|
-
* lines clip; its minimum is a bare HH:MM:SS. `last merged` sits right of `last
|
|
99
|
-
* not flexible: it is a short sibling time cell whose width follows
|
|
96
|
+
* first: `last result` (holds the run summary), then `state` (live working detail), then
|
|
97
|
+
* `last run` — it shrinks last so on a narrow terminal it loses " · 3m ago" before whole
|
|
98
|
+
* lines clip; its minimum is a bare HH:MM:SS. `last merged` sits right of `last run` and is
|
|
99
|
+
* not flexible: it is a short sibling time cell whose width follows lastRunCell's, and the
|
|
100
100
|
* whole-line clip that already protects the header also bounds it. Indices are positional in
|
|
101
101
|
* the `cols` array below — renumber when columns change. (`today`, like `cost`, is a short
|
|
102
|
-
* fixed-width cell: never flexible.)
|
|
102
|
+
* fixed-width cell: never flexible.)
|
|
103
|
+
* The name column (index 0) gives way after all of these, down to the longest bare role label. */
|
|
103
104
|
const FLEXIBLE_COLUMNS = [
|
|
104
105
|
{ index: 10, minWidth: 12 },
|
|
105
106
|
{ index: 1, minWidth: 12 },
|
|
@@ -173,8 +174,8 @@ function badgeSpans(badge, tone) {
|
|
|
173
174
|
/** The daily budget badge's tone: yellow from 85% of the cap, red once it is spent; the
|
|
174
175
|
* subscription window's own severity (amber near its limit, red when a rejected window holds
|
|
175
176
|
* the seams) wins when it stands beside the daily spend. */
|
|
176
|
-
function budgetTone(budget,
|
|
177
|
-
const window =
|
|
177
|
+
function budgetTone(budget, windows) {
|
|
178
|
+
const window = subscriptionWindowTone(windows);
|
|
178
179
|
if (window !== undefined)
|
|
179
180
|
return window;
|
|
180
181
|
if (budget.free || !(budget.capUsd > 0))
|
|
@@ -183,14 +184,14 @@ function budgetTone(budget, claudeWindow) {
|
|
|
183
184
|
return share >= 1 ? "red" : share >= 0.85 ? "yellow" : undefined;
|
|
184
185
|
}
|
|
185
186
|
/** Render the status table shared by `tumwater status` and the TUI as lines of toned spans,
|
|
186
|
-
* plus the per-
|
|
187
|
+
* plus the per-role facts it computed (see StatusRoleRow). When `maxWidth` is given, wide cells
|
|
187
188
|
* are clipped so no line exceeds it (terminal rows never wrap). */
|
|
188
|
-
export function renderStatusSpans(root, snap, maxWidth) {
|
|
189
|
+
export function renderStatusSpans(root, snap, maxWidth, scoped = false) {
|
|
189
190
|
const name = projectName(root);
|
|
190
191
|
const lines = [];
|
|
191
192
|
// One clock read per render, shared by the timed-pause badge's countdown and every row's
|
|
192
|
-
// next-run cell — a per-site Date.now() could tick over
|
|
193
|
-
// itself.
|
|
193
|
+
// next-run cell — a per-site Date.now() could tick over // retired-name-ok
|
|
194
|
+
// between them and disagree with itself.
|
|
194
195
|
const now = Date.now();
|
|
195
196
|
// The questions badge (like the inbox one) appears only when something needs an answer.
|
|
196
197
|
// The budget badge is standing information for a money-spending system in EVERY cap state
|
|
@@ -212,7 +213,7 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
212
213
|
...badgeSpans(mergeBadge(snap.mergeQueue), "blue"),
|
|
213
214
|
...(snap.inbox ? [{ text: ` · inbox: ${snap.inbox}` }] : []),
|
|
214
215
|
...(snap.questions ? badgeSpans(` · questions: ${snap.questions}`, "magenta") : []),
|
|
215
|
-
...badgeSpans(budgetBadge(snap.budget, snap.
|
|
216
|
+
...badgeSpans(budgetBadge(snap.budget, snap.subscriptionWindows, snap.subscriptionOnly, now, snap.chain), budgetTone(snap.budget, snap.subscriptionWindows)),
|
|
216
217
|
// The disk floor (plans/disk-floor.md, part 4/4): `disk 8.2 GB free — holding new work`
|
|
217
218
|
// while the hold is on, or the bare reading below the reclaim threshold. Empty in every
|
|
218
219
|
// comfortable or unmeasured state, so an unaffected header stays byte-identical.
|
|
@@ -221,6 +222,10 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
221
222
|
// `slots 5/8 · omlx 3/3` only while the per-provider cap map is set (permitsBadge already
|
|
222
223
|
// omits the providers on a map-less team, so the header stays byte-identical).
|
|
223
224
|
...badgeSpans(permitsBadge(snap.permits), "blue"),
|
|
225
|
+
// Checks running/waiting for a check permit; empty while the check pool is idle.
|
|
226
|
+
...badgeSpans(checksBadge(snap.checkPermits), snap.checkPermits && snap.checkPermits.waiting > 0 ? "yellow" : "blue"),
|
|
227
|
+
// The workspace pool, only while it is full or a run waits for a slot.
|
|
228
|
+
...badgeSpans(workspacesBadge(snap.slotPool), snap.slotPool && snap.slotPool.waiting > 0 ? "yellow" : "blue"),
|
|
224
229
|
// New-project bootstrap (plans/work-ratio.md, part 2/2): the progress reading while the
|
|
225
230
|
// hold is active. Empty in every normal project, so the header stays byte-identical.
|
|
226
231
|
...badgeSpans(bootstrapBadge(snap.bootstrap), "blue"),
|
|
@@ -238,31 +243,31 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
238
243
|
...badgeSpans(quietBadge(snap.quietHours, snap.inQuietHours), snap.inQuietHours ? "yellow" : undefined),
|
|
239
244
|
]);
|
|
240
245
|
lines.push([]);
|
|
241
|
-
// `today` is the
|
|
242
|
-
// or missing, so
|
|
246
|
+
// `today` is the role's daily budget window (dailyCost): $0.00 while its stamp is stale
|
|
247
|
+
// or missing, so roles that never ran — or last ran yesterday — read zero without a
|
|
243
248
|
// save. It renders whether or not the cap is enabled: spend observability does not depend
|
|
244
249
|
// on it.
|
|
245
250
|
// `next run` is a short fixed-width cell like `today`, never flexible: the columns that
|
|
246
251
|
// give way when the table is wider than the terminal are `last result`, then `state`, then
|
|
247
|
-
// `last
|
|
252
|
+
// `last run`, in FLEXIBLE_COLUMNS' order.
|
|
248
253
|
const cols = ["role", "state", "runs", "commits", "output", "peak ctx", "cost", "today", "last run", "last merged", "last result", "next run"];
|
|
249
|
-
// One live tail read per running
|
|
254
|
+
// One live tail read per running role per frame, threaded through every cell that shows
|
|
250
255
|
// in-flight detail (metrics, state, current work) — each helper used to re-read the log on
|
|
251
|
-
// its own, up to three stats + reads per
|
|
252
|
-
// row (in
|
|
253
|
-
// label drives the state cell and the shared row order (
|
|
254
|
-
// Per-role prompts 2/2 — a
|
|
256
|
+
// its own, up to three stats + reads per role per second. The phase is computed once per
|
|
257
|
+
// row (in roleRowCells, shared with the GUI payload) and carried on the row: the same
|
|
258
|
+
// label drives the state cell and the shared row order (sortRolesByState).
|
|
259
|
+
// Per-role prompts 2/2 — a role with queued prompts carries a small `p:N` marker on its
|
|
255
260
|
// state cell (appended, so the work-item prefix survives clipping ahead of it), telling the
|
|
256
|
-
// operator steering one
|
|
257
|
-
// naming the
|
|
261
|
+
// operator steering one role that something is waiting for it. A pending scheduled wake
|
|
262
|
+
// naming the role adds a blue ` · wake in …` marker after it.
|
|
258
263
|
const roleQueued = (role) => {
|
|
259
264
|
const n = snap.roleInbox[role] ?? 0;
|
|
260
265
|
return n > 0 ? ` p:${n}` : "";
|
|
261
266
|
};
|
|
262
|
-
const withMetrics = snap.
|
|
263
|
-
//
|
|
267
|
+
const withMetrics = snap.roles.map((s) => {
|
|
268
|
+
// roleRowCells single-homes the per-role tail read + metrics + phase derivation the GUI
|
|
264
269
|
// payload (status-payload.ts) repeats, so the two dashboards cannot drift apart.
|
|
265
|
-
const cells =
|
|
270
|
+
const cells = roleRowCells(snap, root, s);
|
|
266
271
|
return {
|
|
267
272
|
s,
|
|
268
273
|
m: { generated: cells.generated, peakCtx: cells.peakCtx },
|
|
@@ -272,26 +277,32 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
272
277
|
lastRunEndedAt: s.lastRunEndedAt,
|
|
273
278
|
};
|
|
274
279
|
});
|
|
275
|
-
// User-defined
|
|
276
|
-
// dashboards' at-a-glance marker. The name column (index 0) is not in FLEXIBLE_COLUMNS
|
|
277
|
-
// its width derives from row content
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
+
// User-defined roles (snapshot's `custom` flag) get an asterisk beside their name — the
|
|
281
|
+
// dashboards' at-a-glance marker. The name column (index 0) is not in FLEXIBLE_COLUMNS;
|
|
282
|
+
// its width derives from row content and it shrinks only after those,
|
|
283
|
+
// so the extra character widens it.
|
|
284
|
+
// Rows are ordered by the shared display rule (status-model's roleRank, then last run most
|
|
285
|
+
// recent first) so the TUI/status table groups roles the way the GUI table does.
|
|
280
286
|
// Each cell is spans: the state cell takes its phase's tone (the p:N marker stays plain), the
|
|
281
287
|
// last result tones its outcome word, a backoff countdown reads yellow.
|
|
282
288
|
const cell = (text, tone) => [{ text, ...(tone ? { tone } : {}) }];
|
|
283
|
-
|
|
289
|
+
// A model every role shares prints once in a footnote instead of on each name cell.
|
|
290
|
+
const modelInner = (s) => s.modelTier
|
|
291
|
+
? `${s.modelTier}${s.model ? ` · ${s.model}` : ""}`
|
|
292
|
+
: s.model
|
|
293
|
+
? `${s.model}${s.modelFromPi ? ", pi default" : ""}`
|
|
294
|
+
: "";
|
|
295
|
+
const inners = snap.roles.map(modelInner);
|
|
296
|
+
const sharedModel = snap.roles.length >= 2 && inners[0] && inners.every((m) => m === inners[0]) ? inners[0] : undefined;
|
|
297
|
+
const rows = sortRolesByState(withMetrics).map(({ s, m, live, phase }) => {
|
|
284
298
|
// The name cell appends the selector the role resolves to; with a top-level tier map
|
|
285
299
|
// declared it additionally names the seam tier (model-tiers.md part 7a,
|
|
286
300
|
// "Observability"). No model resolved and no tier keeps the name byte-identical to
|
|
287
301
|
// today's.
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
: "";
|
|
293
|
-
// Model failure fallback, part 2/2: while the loop runs off-model its name cell says so,
|
|
294
|
-
// so the operator can see which loops the fallback pair is carrying without reading logs.
|
|
302
|
+
const inner = modelInner(s);
|
|
303
|
+
const modelSuffix = inner && !sharedModel ? ` (${inner})` : "";
|
|
304
|
+
// Model failure fallback, part 2/2: while the role runs off-model its name cell says so,
|
|
305
|
+
// so the operator can see which roles the fallback pair is carrying without reading logs.
|
|
295
306
|
const fallbackSuffix = s.fallback ? " (over-cap model)" : "";
|
|
296
307
|
const name = (s.custom ? `${roleLabel(s.role)}*` : roleLabel(s.role)) + modelSuffix + fallbackSuffix;
|
|
297
308
|
const state = stateCell(root, s, phase, live);
|
|
@@ -300,12 +311,12 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
300
311
|
// survives clipping), and clipping often leaves only it — the color still tells the state.
|
|
301
312
|
const stateSpans = [{ text: state, ...(tone ? { tone } : {}) }];
|
|
302
313
|
const next = nextRunCell(s, phase, now, snap.running);
|
|
303
|
-
const wakeMark =
|
|
314
|
+
const wakeMark = roleWakeMarker(snap.scheduledWakes, s.role, s.instanceOf, now);
|
|
304
315
|
const resultT = resultTone(s.lastResult);
|
|
305
316
|
return [
|
|
306
317
|
cell(name),
|
|
307
318
|
// Merge queue 4/5 — the merge role's phase label (the marker-driven record, filtered to
|
|
308
|
-
// this role by
|
|
319
|
+
// this role by rolePhase's `merge` argument above) rides this same phase string.
|
|
309
320
|
[...stateSpans, { text: roleQueued(s.role) }, ...(wakeMark ? [{ text: wakeMark, tone: "blue" }] : [])],
|
|
310
321
|
cell(String(s.runs)),
|
|
311
322
|
cell(String(s.commits)),
|
|
@@ -313,7 +324,7 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
313
324
|
cell(compactTokens(m.peakCtx)),
|
|
314
325
|
cell(usd(s.totalCostUsd)),
|
|
315
326
|
cell(usd(dailyCost(s))),
|
|
316
|
-
cell(
|
|
327
|
+
cell(lastRunCell(s.lastRunEndedAt)),
|
|
317
328
|
cell(lastMergedCell(s.lastMergedAt)),
|
|
318
329
|
s.lastResult
|
|
319
330
|
? [{ text: eventTypeLabel(s.lastResult), ...(resultT ? { tone: resultT } : {}) }, { text: s.lastSummary ? ` — ${s.lastSummary}` : "" }]
|
|
@@ -328,15 +339,16 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
328
339
|
"",
|
|
329
340
|
compactTokens(withMetrics.reduce((sum, { m }) => sum + m.generated, 0)),
|
|
330
341
|
compactTokens(Math.max(0, ...withMetrics.map(({ m }) => m.peakCtx))),
|
|
331
|
-
usd(snap.
|
|
342
|
+
usd(snap.roles.reduce((sum, s) => sum + s.totalCostUsd, 0)),
|
|
332
343
|
// The team's today-spend, from the snapshot's budget block — the same number the header
|
|
333
344
|
// badge reads, so table and badge cannot drift. While the orchestrator runs that figure is
|
|
334
|
-
// the scheduler's live sum over its in-memory states, which can sit ABOVE the per-
|
|
335
|
-
// `today` cells above (each
|
|
345
|
+
// the scheduler's live sum over its in-memory states, which can sit ABOVE the per-role
|
|
346
|
+
// `today` cells above (each role's persisted copy, written only at its saves): the gap is
|
|
336
347
|
// exactly the in-flight charge the budget gate has already counted, and displaying it is
|
|
337
348
|
// the point (BUGS.md 2026-09-30). Not running, the snapshot derives the block from these
|
|
338
|
-
// same
|
|
339
|
-
|
|
349
|
+
// same roles, so cells and total agree by construction.
|
|
350
|
+
// A role-scoped table adds up the rows shown instead: the team figure would not match them.
|
|
351
|
+
usd(scoped ? snap.roles.reduce((sum, s) => sum + dailyCost(s), 0) : snap.budget.spentUsd),
|
|
340
352
|
"",
|
|
341
353
|
"",
|
|
342
354
|
"",
|
|
@@ -357,6 +369,11 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
357
369
|
widths[index] = current - reduction;
|
|
358
370
|
overflow -= reduction;
|
|
359
371
|
}
|
|
372
|
+
// The name column gives way last, down to the longest bare role label.
|
|
373
|
+
if (overflow > 0) {
|
|
374
|
+
const floor = Math.max(displayWidth(cols[0] ?? ""), ...snap.roles.map((s) => displayWidth(s.custom ? `${roleLabel(s.role)}*` : roleLabel(s.role))));
|
|
375
|
+
widths[0] = Math.max(floor, (widths[0] ?? 0) - overflow);
|
|
376
|
+
}
|
|
360
377
|
}
|
|
361
378
|
// Each cell clipped and padded to its column (padding stays untoned), the columns joined by
|
|
362
379
|
// the gap, and the line's trailing padding trimmed — the plain text is exactly the aligned
|
|
@@ -390,14 +407,14 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
390
407
|
}
|
|
391
408
|
return out;
|
|
392
409
|
};
|
|
393
|
-
const
|
|
410
|
+
const roles = withMetrics.map(({ s, phase, live }) => ({
|
|
394
411
|
role: s.role,
|
|
395
412
|
phase,
|
|
396
413
|
errorStreak: onErrorStreak(s),
|
|
397
414
|
inFlight: isActivePhase(phase),
|
|
398
415
|
...(live?.quietMs !== undefined ? { stuckMs: live.quietMs } : {}),
|
|
399
416
|
...(s.lastError ? { lastError: s.lastError } : {}),
|
|
400
|
-
// The
|
|
417
|
+
// The role's image-input surface (plans/image-input.md "Surfaces", part 5/5): always
|
|
401
418
|
// present, so the TUI's images attention line keys on it like the dashboard's.
|
|
402
419
|
imageInput: s.imageInput,
|
|
403
420
|
imageReroute: s.imageRerouteLabel,
|
|
@@ -409,19 +426,21 @@ export function renderStatusSpans(root, snap, maxWidth) {
|
|
|
409
426
|
lines.push(fmt(r));
|
|
410
427
|
lines.push(separator);
|
|
411
428
|
lines.push(fmt(totalsRow, "bold"));
|
|
412
|
-
// One footnote under the table when any custom
|
|
413
|
-
// taking a column. Absent (byte-identical table) on a team with no user-defined
|
|
414
|
-
if (
|
|
429
|
+
// One footnote under the table when any custom role exists — explains the asterisk without
|
|
430
|
+
// taking a column. Absent (byte-identical table) on a team with no user-defined roles.
|
|
431
|
+
if (sharedModel)
|
|
432
|
+
lines.push([{ text: `model (every role): ${sharedModel}`, tone: "dim" }]);
|
|
433
|
+
if (snap.roles.some((l) => l.custom))
|
|
415
434
|
lines.push([{ text: "* custom role", tone: "dim" }]);
|
|
416
435
|
// The header line (and any residual overflow past the columns' minimums) is clipped too,
|
|
417
436
|
// so no status line ever wraps in a terminal of `maxWidth` columns.
|
|
418
|
-
return { lines: maxWidth === undefined ? lines : lines.map((l) => clipSpans(l, maxWidth)),
|
|
437
|
+
return { lines: maxWidth === undefined ? lines : lines.map((l) => clipSpans(l, maxWidth)), roles };
|
|
419
438
|
}
|
|
420
439
|
/** Render the status table shared by `tumwater status` and the TUI as plain text (see
|
|
421
440
|
* renderStatusSpans). When `maxWidth` is given, wide cells are clipped so no line exceeds it
|
|
422
441
|
* (terminal rows never wrap). */
|
|
423
|
-
export function renderStatus(root, snap, maxWidth) {
|
|
424
|
-
const { lines,
|
|
442
|
+
export function renderStatus(root, snap, maxWidth, scoped = false) {
|
|
443
|
+
const { lines, roles } = renderStatusSpans(root, snap, maxWidth, scoped);
|
|
425
444
|
// The team-attention banners (team-alerts.ts) drop under the header's blank line here, on
|
|
426
445
|
// the CLI-only plain-text surface — the same spelled-out banners the dashboard and TUI draw,
|
|
427
446
|
// from the one shared source, so the CLI/GUI/TUI cannot drift on wording. One QUESTIONS.md
|
|
@@ -431,7 +450,7 @@ export function renderStatus(root, snap, maxWidth) {
|
|
|
431
450
|
// Composed here, NOT inside renderStatusSpans: the TUI re-slices that function's shared
|
|
432
451
|
// `lines` (lines[0], its own attention block, then lines.slice(1)), so a banner embedded
|
|
433
452
|
// there would render twice in the TUI.
|
|
434
|
-
const alerts = teamAlerts(snap, openQuestions(root),
|
|
453
|
+
const alerts = teamAlerts(snap, openQuestions(root), roles, Date.now(), refusedEntries(root), operatorHeldEntries(root), staleOpenBugs(root), stalePrompts(root), staleMerges(root), planDependencyAlert(root), staleWaitingBugs(root), skippedMergeFiles(root), stalledPlanEntries(root)).filter((a) => a.key !== "stopped");
|
|
435
454
|
const banner = alertLines(alerts, maxWidth ?? Number.MAX_SAFE_INTEGER, alerts.length).map(plainText);
|
|
436
455
|
const text = lines.map(plainText);
|
|
437
456
|
return [...text.slice(0, 2), ...banner, ...text.slice(2)].join("\n");
|