tumwater 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +55 -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 +6 -4
- 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 +404 -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 +281 -55
- package/dist/src/backlog/backlog-cli-view.js +255 -0
- package/dist/src/backlog/backlog-cli.js +91 -335
- package/dist/src/backlog/backlog-commit.js +20 -13
- package/dist/src/backlog/backlog-eligibility.js +193 -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 +264 -0
- package/dist/src/backlog/backlog-release.js +134 -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-guardrails.js +136 -0
- package/dist/src/baseline/main-red.js +153 -36
- package/dist/src/baseline/main-watch.js +232 -0
- package/dist/src/brief/brief-edit.js +12 -12
- package/dist/src/brief/brief.js +31 -51
- package/dist/src/budget/budget-fallback-view.js +288 -0
- package/dist/src/budget/budget-wiring.js +158 -0
- package/dist/src/budget/budget.js +31 -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 +65 -3
- 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 +181 -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 +183 -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 +291 -598
- package/dist/src/cli/cli-repo-gate.js +11 -1
- package/dist/src/cli/cli-review-commands.js +113 -0
- package/dist/src/cli/cli-role-command.js +525 -0
- package/dist/src/cli/cli-start-summary.js +167 -0
- package/dist/src/cli/{cli-run.js → cli-start.js} +193 -237
- package/dist/src/cli/completion.js +169 -22
- package/dist/src/cli/config-commands.js +58 -26
- package/dist/src/cli/follow-view.js +75 -0
- package/dist/src/cli/help.js +258 -82
- package/dist/src/cli/log-commands.js +164 -85
- package/dist/src/cli/question-commands.js +211 -10
- package/dist/src/cli.js +173 -45
- package/dist/src/collections.js +56 -13
- 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 +16 -1
- package/dist/src/config/config-fallbacks.js +94 -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 +169 -222
- package/dist/src/config/config-views.js +136 -54
- package/dist/src/config/config-write-core.js +135 -0
- package/dist/src/config/config-write.js +164 -265
- package/dist/src/config/config.js +53 -38
- package/dist/src/config/model-selector.js +34 -34
- package/dist/src/doctor/doctor-backlog-bugs.js +330 -0
- package/dist/src/doctor/doctor-backlog-plans.js +398 -0
- package/dist/src/doctor/doctor-backlog.js +249 -512
- package/dist/src/doctor/doctor-check-names.js +85 -0
- package/dist/src/doctor/doctor-checks.js +61 -593
- package/dist/src/doctor/doctor-claude-checks.js +142 -0
- package/dist/src/doctor/doctor-codex-checks.js +276 -0
- package/dist/src/doctor/doctor-contention-checks.js +506 -0
- package/dist/src/doctor/doctor-fallback-checks.js +130 -0
- package/dist/src/doctor/doctor-holds.js +80 -66
- package/dist/src/doctor/doctor-install.js +2 -1
- package/dist/src/doctor/doctor-model-checks.js +216 -537
- package/dist/src/doctor/doctor-notify.js +9 -5
- package/dist/src/doctor/doctor-opencode-checks.js +164 -0
- package/dist/src/doctor/doctor-orphans.js +182 -63
- package/dist/src/doctor/doctor-pi-checks.js +62 -0
- package/dist/src/doctor/doctor-project-checks.js +397 -0
- package/dist/src/doctor/doctor.js +126 -77
- package/dist/src/doctor/image-probe.js +21 -8
- package/dist/src/doctor/outcome.js +3 -2
- package/dist/src/doctor/probe.js +21 -12
- package/dist/src/events/event-format.js +185 -89
- package/dist/src/events/event-labels.js +11 -5
- package/dist/src/events/event-legacy.js +39 -15
- package/dist/src/events/event-read.js +52 -39
- package/dist/src/events/event-tally.js +16 -0
- package/dist/src/events/event-window.js +80 -26
- 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 +65 -60
- package/dist/src/failure/failure-render.js +29 -21
- package/dist/src/failure/failure-state-change.js +17 -12
- package/dist/src/failure/time-spend.js +35 -35
- package/dist/src/files/file-queue.js +17 -0
- 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 +88 -225
- package/dist/src/gates/disk-gate.js +1 -1
- package/dist/src/gates/gate-polls.js +101 -143
- package/dist/src/gates/maintenance-quota.js +37 -30
- 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 +89 -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 +54 -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 +43 -19
- package/dist/src/gui/gui-backlog-endpoints.js +156 -0
- package/dist/src/gui/gui-command.js +26 -13
- package/dist/src/gui/gui-config-commands.js +168 -80
- package/dist/src/gui/gui-endpoint-commands.js +5 -4
- 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 +235 -13
- package/dist/src/gui/gui-prompt-commands.js +26 -20
- package/dist/src/gui/gui-report-endpoints.js +97 -0
- package/dist/src/gui/gui-server.js +26 -5
- 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 +155 -0
- package/dist/src/history/history-render.js +42 -22
- package/dist/src/history/history.js +190 -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 +28 -17
- 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 +58 -32
- package/dist/src/merge/merge-diff.js +5 -4
- package/dist/src/merge/merge-drain.js +45 -19
- 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 +214 -0
- package/dist/src/merge/merge-queue-render.js +48 -11
- package/dist/src/merge/merge-queue.js +52 -15
- package/dist/src/merge/merge-requeue.js +103 -0
- package/dist/src/merge/merge-slot.js +61 -16
- package/dist/src/merge/merge-stack.js +27 -11
- package/dist/src/merge/merge-to-main.js +77 -26
- package/dist/src/merge/merge-top.js +83 -0
- package/dist/src/merge/merge-verify.js +75 -18
- package/dist/src/notes-cli.js +35 -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 +352 -0
- package/dist/src/orchestrator/orchestrator-scheduling.js +71 -370
- package/dist/src/orchestrator/orchestrator.js +129 -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 +245 -0
- package/dist/src/orchestrator/scheduling-skip.js +195 -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 +126 -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 +26 -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 +150 -217
- package/dist/src/prompt/notes-backlog.js +184 -0
- package/dist/src/prompt/principles.js +1 -2
- package/dist/src/prompt/prompt-followup.js +19 -14
- package/dist/src/prompt/prompt.js +131 -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 +188 -124
- package/dist/src/report/report-fold.js +130 -54
- package/dist/src/report/report-main-red.js +55 -0
- package/dist/src/report/report-render.js +136 -65
- package/dist/src/report/report-types.js +1 -0
- package/dist/src/report/report.js +23 -9
- package/dist/src/request-timeouts.js +2 -2
- package/dist/src/review/diff-rules.js +53 -0
- package/dist/src/review/known-flakes.js +112 -1
- package/dist/src/review/post-merge-review-accept.js +51 -0
- package/dist/src/review/post-merge-review-render.js +112 -0
- package/dist/src/review/post-merge-review.js +239 -0
- package/dist/src/review/protected-edits.js +24 -10
- package/dist/src/review/quarantine-render.js +65 -0
- 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 +180 -401
- 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 +127 -12
- package/dist/src/roles/role-guidance.js +65 -20
- package/dist/src/roles/role-labels.js +7 -5
- package/dist/src/roles/role-render.js +108 -56
- package/dist/src/roles/role-view.js +95 -56
- package/dist/src/roles/roles.js +33 -33
- package/dist/src/roles/runner-ids.js +97 -0
- package/dist/src/run/acceptance-greps.js +119 -0
- package/dist/src/run/ci-parity.js +121 -0
- package/dist/src/run/feedback-filings.js +51 -0
- package/dist/src/run/qa-coverage.js +125 -0
- package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
- package/dist/src/run/run-detail-data.js +175 -0
- package/dist/src/run/run-detail-render.js +91 -0
- package/dist/src/run/run-detail.js +168 -0
- package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +73 -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} +122 -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} +170 -83
- 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} +105 -57
- 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 +178 -0
- package/dist/src/run/stage-checks-shared.js +65 -0
- package/dist/src/run/stage-name-checks.js +204 -0
- package/dist/src/run/stage-path-checks.js +231 -0
- package/dist/src/run/stage-prose-checks.js +261 -0
- package/dist/src/runner/leftover.js +216 -0
- package/dist/src/{loop → runner}/model-fallback.js +12 -12
- package/dist/src/runner/revision.js +191 -0
- package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
- package/dist/src/runner/runner-outcome.js +109 -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} +39 -18
- package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +236 -245
- package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
- package/dist/src/runner/runner-state.js +103 -0
- package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
- package/dist/src/scheduling/backoff.js +38 -38
- package/dist/src/scheduling/claim-entries.js +49 -0
- 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 +49 -17
- 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 +43 -28
- package/dist/src/status/status-roles.js +50 -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 +7 -8
- 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 +65 -18
- package/dist/src/text/format.js +48 -11
- package/dist/src/text/markdown.js +8 -5
- package/dist/src/text/phrases.js +60 -23
- package/dist/src/text/suggest.js +1 -1
- package/dist/src/text/text.js +78 -13
- package/dist/src/ui/badges.js +99 -38
- package/dist/src/ui/gui/gui-client-backlog.js +314 -0
- package/dist/src/ui/gui/gui-client-boot.js +89 -61
- package/dist/src/ui/gui/gui-client-composer.js +8 -9
- package/dist/src/ui/gui/gui-client-drawer-config.js +254 -0
- package/dist/src/ui/gui/gui-client-drawer.js +155 -289
- package/dist/src/ui/gui/gui-client-feed.js +271 -0
- package/dist/src/ui/gui/gui-client-history.js +437 -68
- package/dist/src/ui/gui/gui-client-labels.js +6 -3
- 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 +60 -52
- package/dist/src/ui/gui/gui-client-pending.js +405 -24
- package/dist/src/ui/gui/gui-client-report-chart.js +492 -0
- package/dist/src/ui/gui/gui-client-report.js +105 -430
- package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
- package/dist/src/ui/gui/gui-client-settings.js +163 -5
- package/dist/src/ui/gui/gui-client-team.js +36 -465
- package/dist/src/ui/gui/gui-client.js +38 -26
- package/dist/src/ui/gui/gui-icons.js +0 -2
- package/dist/src/ui/gui/gui-page.js +10 -7
- package/dist/src/ui/gui/gui-styles.js +38 -34
- package/dist/src/ui/progress-data.js +9 -11
- package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +33 -32
- package/dist/src/ui/status-model.js +133 -125
- package/dist/src/ui/status-payload.js +364 -49
- package/dist/src/ui/status-render.js +109 -82
- package/dist/src/ui/team-alerts.js +728 -62
- package/dist/src/ui/tone.js +12 -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 +53 -14
- package/dist/src/ui/tui/tui-editor-answer.js +27 -31
- package/dist/src/ui/tui/tui-editor-attach.js +4 -12
- package/dist/src/ui/tui/tui-editor-backends.js +127 -0
- package/dist/src/ui/tui/tui-editor-brief.js +14 -36
- package/dist/src/ui/tui/tui-editor-budget.js +36 -39
- package/dist/src/ui/tui/tui-editor-check.js +29 -0
- package/dist/src/ui/tui/tui-editor-checks.js +28 -0
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
- package/dist/src/ui/tui/tui-editor-concurrency.js +25 -0
- package/dist/src/ui/tui/tui-editor-config.js +99 -0
- package/dist/src/ui/tui/tui-editor-entry.js +39 -64
- package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
- package/dist/src/ui/tui/tui-editor-instances.js +46 -0
- package/dist/src/ui/tui/tui-editor-instructions.js +117 -0
- package/dist/src/ui/tui/tui-editor-interval.js +29 -16
- package/dist/src/ui/tui/tui-editor-model.js +29 -0
- package/dist/src/ui/tui/tui-editor-notes.js +9 -29
- 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 +95 -41
- package/dist/src/ui/tui/tui-editor-retire.js +5 -13
- package/dist/src/ui/tui/tui-editor-role-enabled.js +126 -0
- package/dist/src/ui/tui/tui-editor-role-key.js +71 -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 +101 -34
- package/dist/src/ui/tui/tui-editor-slots.js +24 -0
- package/dist/src/ui/tui/tui-editors.js +44 -2
- package/dist/src/ui/tui/tui-frame.js +68 -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 +33 -9
- package/dist/src/ui/tui/tui-pane-activity.js +35 -19
- package/dist/src/ui/tui/tui-pane-backlog.js +198 -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 +168 -8
- package/dist/src/ui/tui/tui-pane-history.js +446 -82
- package/dist/src/ui/tui/tui-pane-pending.js +539 -15
- package/dist/src/ui/tui/tui-pane-queued.js +78 -57
- package/dist/src/ui/tui/tui-pane-report.js +54 -16
- package/dist/src/ui/tui/tui-pane-session.js +34 -6
- package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
- package/dist/src/ui/tui/tui-pane.js +91 -28
- package/dist/src/ui/tui/tui-panes.js +153 -34
- package/dist/src/ui/tui/tui.js +18 -15
- package/dist/src/verdict/fix-claim.js +129 -33
- package/dist/src/verdict/no-change.js +4 -4
- package/dist/src/verdict/refusal.js +40 -16
- package/dist/src/verdict/reply-contract.js +77 -31
- package/dist/src/version.js +0 -1
- package/package.json +2 -1
- package/dist/src/loop/leftover.js +0 -130
- package/dist/src/loop/revision.js +0 -70
- package/dist/src/roles/loop-ids.js +0 -77
- package/dist/src/tick/qa-coverage.js +0 -108
- 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,21 +1,23 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import crypto from "node:crypto";
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { cancelScheduledStop, isTeamPaused, scheduleStop } from "../team/team-state.js";
|
|
6
5
|
import { orchestratorAlive, readOrchestratorInfo } from "../team/orchestrator-info.js";
|
|
7
6
|
import { durationLabel } from "../cli/cli-args.js";
|
|
8
|
-
import { formatDate, formatTime, humanUntil } from "../text/datetime.js";
|
|
7
|
+
import { formatDate, formatTime, humanUntil, DAY_MS } from "../text/datetime.js";
|
|
9
8
|
import { submitRolePrompt } from "../inbox/inbox-submit.js";
|
|
10
9
|
import { questionAsker } from "../backlog/question-answer.js";
|
|
11
|
-
import { knownRoleIdsCached } from "../config/config.js";
|
|
10
|
+
import { knownRoleIdsCached, loadConfigCached } from "../config/config.js";
|
|
11
|
+
import { baseRoleOf } from "../roles/runner-ids.js";
|
|
12
12
|
import { DIRECTOR_ROLE } from "../roles/roles.js";
|
|
13
|
-
import {
|
|
13
|
+
import { saveRunnerState, zeroCounters } from "../runner/runner-state.js";
|
|
14
|
+
import { loadRunnerState } from "../runner/runner-state-load.js";
|
|
14
15
|
import { clearBackoff } from "../scheduling/backoff.js";
|
|
15
16
|
import { writeJsonAtomic } from "../files/json-files.js";
|
|
16
17
|
import { reclaimPass } from "../team/reclaim.js";
|
|
17
18
|
import { formatGB } from "../text/format.js";
|
|
18
19
|
import { plural } from "../text/phrases.js";
|
|
20
|
+
import { numberedList } from "../text/markdown.js";
|
|
19
21
|
import { cancelScheduledWakes, pendingScheduledWakes } from "./operator-requests.js";
|
|
20
22
|
import { abortRequestPath, reclaimRequestPath, resetRequestPath, restartRequestPath, scheduledWakeDir, wakeRequestPath, } from "../paths.js";
|
|
21
23
|
/** The marker-writing cores of the operator-intent protocol, shared by every surface that writes
|
|
@@ -60,13 +62,13 @@ export function requestResetCounters(root, roles) {
|
|
|
60
62
|
// the affected runners' in-memory copies and the runners save them, and writes the file for a
|
|
61
63
|
// requested role that has no runner (a disabled role). Rewriting the files here too would be
|
|
62
64
|
// a second writer racing those whole-file saves — an overwrite from a snapshot the
|
|
63
|
-
// orchestrator has since moved past (a just-ended
|
|
65
|
+
// orchestrator has since moved past (a just-ended run's result, a claim, a schedule), which
|
|
64
66
|
// the lost update discards. So with a live team only the marker is written; with none there
|
|
65
67
|
// is no concurrent writer and the direct write is what carries the reset to the next run.
|
|
66
68
|
const { live, when } = markerApplyNote(root);
|
|
67
69
|
if (!live)
|
|
68
70
|
for (const r of roles)
|
|
69
|
-
|
|
71
|
+
saveRunnerState(root, zeroCounters(loadRunnerState(root, r)));
|
|
70
72
|
// Atomic: a concurrent poll reading the marker mid-write would see a torn file, which
|
|
71
73
|
// readJsonFile folds to null — the corrupt-marker superset that wakes/resets EVERY runner.
|
|
72
74
|
// For a scheduled wake (`--in`) that superset drops notBeforeMs, firing the wake hours early.
|
|
@@ -78,32 +80,28 @@ export function requestResetCounters(root, roles) {
|
|
|
78
80
|
}
|
|
79
81
|
/** The empty-list wording both wake-list renderers share — `cancelWakes`'s no-op arm and
|
|
80
82
|
* `renderWakeList`'s empty list: one literal so the two surfaces cannot drift on what an
|
|
81
|
-
* operator sees when nothing is pending, plus a ` for <
|
|
83
|
+
* operator sees when nothing is pending, plus a ` for <roles>` suffix when the view was
|
|
82
84
|
* role-scoped. */
|
|
83
|
-
function noWakePending(
|
|
84
|
-
return `no scheduled wake pending${
|
|
85
|
+
function noWakePending(roles) {
|
|
86
|
+
return `no scheduled wake pending${roles ? ` for ${roles.join(", ")}` : ""}`;
|
|
85
87
|
}
|
|
86
88
|
/** Drop pending scheduled wakes (`tumwater wake --cancel`, the dashboard's POST /api/wake-cancel):
|
|
87
|
-
* every pending wake, or only
|
|
88
|
-
* verbatim and the GUI flashes, so the surfaces cannot drift on wording. */
|
|
89
|
-
export function cancelWakes(root,
|
|
90
|
-
const n = cancelScheduledWakes(root,
|
|
91
|
-
const forRole =
|
|
92
|
-
return n > 0 ? `cancelled ${plural(n, "scheduled wake")}${forRole}` : noWakePending(
|
|
89
|
+
* every pending wake, or only the named roles' share of them. Returns the confirmation the CLI
|
|
90
|
+
* prints verbatim and the GUI flashes, so the surfaces cannot drift on wording. */
|
|
91
|
+
export function cancelWakes(root, roles) {
|
|
92
|
+
const n = cancelScheduledWakes(root, roles);
|
|
93
|
+
const forRole = roles ? ` for ${roles.join(", ")}` : "";
|
|
94
|
+
return n > 0 ? `cancelled ${plural(n, "scheduled wake")}${forRole}` : noWakePending(roles);
|
|
93
95
|
}
|
|
94
|
-
/** The pending scheduled wakes (`tumwater wake --list`), earliest first; with `
|
|
95
|
-
* wakes naming
|
|
96
|
-
export function listWakes(root,
|
|
96
|
+
/** The pending scheduled wakes (`tumwater wake --list`), earliest first; with `roles`, only the
|
|
97
|
+
* wakes naming any of them. Entries keep their full roles list. Read-only. */
|
|
98
|
+
export function listWakes(root, roles, now) {
|
|
97
99
|
const wakes = pendingScheduledWakes(root, now);
|
|
98
|
-
return
|
|
100
|
+
return roles ? wakes.filter((w) => w.roles.some((r) => roles.includes(r))) : wakes;
|
|
99
101
|
}
|
|
100
102
|
/** `wake --list`'s text: one numbered line per wake, or the empty wording `cancelWakes` uses. */
|
|
101
|
-
export function renderWakeList(wakes, now,
|
|
102
|
-
|
|
103
|
-
return noWakePending(role);
|
|
104
|
-
return wakes
|
|
105
|
-
.map((w, i) => `${i + 1}. in ${humanUntil(w.atMs, now)} — ${w.roles.join(", ")}`)
|
|
106
|
-
.join("\n");
|
|
103
|
+
export function renderWakeList(wakes, now, roles) {
|
|
104
|
+
return numberedList(wakes.map((w) => `in ${humanUntil(w.atMs, now)} — ${w.roles.join(", ")}`), noWakePending(roles));
|
|
107
105
|
}
|
|
108
106
|
/** The marker-writing core of `wake [--in <duration>]`, shared with the GUI's POST /api/wake:
|
|
109
107
|
* clear the named roles' backoff and pull nextRunAt to now (touching ONLY the schedule —
|
|
@@ -114,9 +112,9 @@ export function renderWakeList(wakes, now, role) {
|
|
|
114
112
|
* team stopped at submit (or restarted before the deadline) does not wake early;
|
|
115
113
|
* consumeWakeRequest clears the schedules when it consumes the marker at or after the
|
|
116
114
|
* deadline. The "wakes in" phrase is inMs itself, not the deadline minus a second clock read
|
|
117
|
-
* (the rule timedPauseBits follows): a caller-computed deadline let a millisecond
|
|
118
|
-
* the two reads print "wakes in 2699999ms" for `--in 45m`. Returns the
|
|
119
|
-
* prints verbatim and the GUI flashes. */
|
|
115
|
+
* (the rule timedPauseBits follows): a caller-computed deadline let a millisecond of clock
|
|
116
|
+
* time between the two reads print "wakes in 2699999ms" for `--in 45m`. Returns the
|
|
117
|
+
* confirmation the CLI prints verbatim and the GUI flashes. */
|
|
120
118
|
export function requestWake(root, roles, inMs) {
|
|
121
119
|
const now = Date.now();
|
|
122
120
|
// Deferred only for a genuinely positive delay: zero or less reads as immediate, so the
|
|
@@ -131,7 +129,7 @@ export function requestWake(root, roles, inMs) {
|
|
|
131
129
|
const { live, when } = markerApplyNote(root);
|
|
132
130
|
if (!deferred && !live)
|
|
133
131
|
for (const r of roles)
|
|
134
|
-
|
|
132
|
+
saveRunnerState(root, clearBackoff(loadRunnerState(root, r), now));
|
|
135
133
|
// Atomic — see requestResetCounters: a torn wake marker reads as corrupt and wakes every
|
|
136
134
|
// runner immediately, dropping a scheduled wake's notBeforeMs on the floor. A scheduled wake
|
|
137
135
|
// gets its own file so a later wake request, immediate or scheduled, cannot overwrite it.
|
|
@@ -143,12 +141,35 @@ export function requestWake(root, roles, inMs) {
|
|
|
143
141
|
writeJsonAtomic(wakeRequestPath(root), { at: now, roles });
|
|
144
142
|
// Same liveness contract as reset-counters and pause/resume: only a live team consumes the
|
|
145
143
|
// marker, so say so instead of promising a poll that will not happen.
|
|
144
|
+
const disabledNote = disabledWakeNote(root, roles);
|
|
146
145
|
if (deferred)
|
|
147
146
|
return (`wake scheduled for ${roles.join(", ")} — wakes in ${durationLabel(inMs)} — ` +
|
|
148
147
|
(live
|
|
149
148
|
? "the running team applies it within one poll after the deadline"
|
|
150
|
-
: "tumwater is not running — the next `tumwater start` applies it after the deadline")
|
|
151
|
-
|
|
149
|
+
: "tumwater is not running — the next `tumwater start` applies it after the deadline") +
|
|
150
|
+
disabledNote);
|
|
151
|
+
return `wake requested for ${roles.join(", ")} — ${applyClause(live, when, "applies it")}${disabledNote}`;
|
|
152
|
+
}
|
|
153
|
+
/** The ids among `roles` whose config entry is `enabled: false`; empty when the config
|
|
154
|
+
* cannot be read. */
|
|
155
|
+
function disabledOf(root, roles) {
|
|
156
|
+
const { config } = loadConfigCached(root);
|
|
157
|
+
if (!config)
|
|
158
|
+
return [];
|
|
159
|
+
return roles.filter((id) => config.roles[baseRoleOf(id)]?.enabled === false);
|
|
160
|
+
}
|
|
161
|
+
/** The confirmation clause naming the woken roles that are disabled: a disabled role has no
|
|
162
|
+
* runner, so the wake only ends its sleep. Empty when none is disabled or the config
|
|
163
|
+
* cannot be read (the cached read, so a broken tumwater.json never makes a wake throw). */
|
|
164
|
+
function disabledWakeNote(root, roles) {
|
|
165
|
+
const disabled = disabledOf(root, roles);
|
|
166
|
+
if (disabled.length === 0)
|
|
167
|
+
return "";
|
|
168
|
+
const one = disabled.length === 1;
|
|
169
|
+
return (`; ${disabled.join(", ")} ${one ? "is" : "are"} disabled, so the wake only ends ` +
|
|
170
|
+
`${one ? "its" : "their"} sleep — ${one ? "it" : "they"} won't run until you enable ` +
|
|
171
|
+
`${one ? "it" : "them"} with ` +
|
|
172
|
+
`tumwater config set roles.${one ? disabled[0] : "<id>"}.enabled true`);
|
|
152
173
|
}
|
|
153
174
|
/** Schedule a graceful team stop `inMs` from now (`tumwater stop --in`, the dashboard's "Stop the
|
|
154
175
|
* team later"): the marker means nothing without a live team to consume it, so a stopped one
|
|
@@ -196,17 +217,17 @@ function requestReclaim(root) {
|
|
|
196
217
|
const { live, when } = markerApplyNote(root);
|
|
197
218
|
return `reclaim requested — ${applyClause(live, when, "reclaims idle worktrees")}`;
|
|
198
219
|
}
|
|
199
|
-
/** Queue a prompt for one
|
|
220
|
+
/** Queue a prompt for one role and wake that role — the one workflow `tumwater prompt
|
|
200
221
|
* --role`, the dashboard's POST /api/prompt-role, and the TUI's Ctrl+R submit all share:
|
|
201
|
-
* submitRolePrompt enqueues into the
|
|
202
|
-
* and logs under the
|
|
203
|
-
*
|
|
222
|
+
* submitRolePrompt enqueues into the role's own queue (length-capped by the shared rule)
|
|
223
|
+
* and logs under the role, then requestWake's single-role marker brings a live team's
|
|
224
|
+
* role in within one poll instead of whenever its backoff next expires (and is safe with
|
|
204
225
|
* no team running — the same contract as `tumwater wake`). Returns requestWake's
|
|
205
226
|
* confirmation so a surface can show what the wake did. Keeping enqueue and wake in one
|
|
206
227
|
* call keeps a surface from ever queueing a prompt without the wake that delivers it
|
|
207
228
|
* promptly — the pairing is the invariant, not each surface's private discipline. An optional
|
|
208
229
|
* `notBeforeMs` defers the prompt (PLANS.md "tumwater prompt --at <duration>"); the wake
|
|
209
|
-
* still fires — it brings the
|
|
230
|
+
* still fires — it brings the role in at its time to find a deliverable queue, and a queue
|
|
210
231
|
* holding only future prompts keeps it asleep. */
|
|
211
232
|
export function submitRolePromptAndWake(root, role, text, images, notBeforeMs) {
|
|
212
233
|
submitRolePrompt(root, role, text, images, notBeforeMs);
|
|
@@ -223,7 +244,7 @@ export function requestAbort(root, role) {
|
|
|
223
244
|
writeJsonAtomic(abortRequestPath(root, role), { at: Date.now() });
|
|
224
245
|
let confirmation = `abort requested for ${role} — the running team applies it within ~2s`;
|
|
225
246
|
if (role === DIRECTOR_ROLE) {
|
|
226
|
-
// The director's in-flight prompt was dequeued from the inbox file at
|
|
247
|
+
// The director's in-flight prompt was dequeued from the inbox file at run start and an
|
|
227
248
|
// abort discards it without re-queueing — say so, since the discard is otherwise silent.
|
|
228
249
|
confirmation +=
|
|
229
250
|
"; its current in-flight prompt will be discarded (re-submit with `tumwater prompt` if you want it retried)";
|
|
@@ -263,13 +284,16 @@ export function rolePauseMessage(root, role, changed, timed, now = Date.now()) {
|
|
|
263
284
|
return `role ${role} is already paused`;
|
|
264
285
|
const { when, tail } = markerApplyNote(root);
|
|
265
286
|
const { forPhrase, note } = timedPauseBits(timed, now);
|
|
266
|
-
|
|
287
|
+
const disabledNote = disabledOf(root, [role]).length > 0
|
|
288
|
+
? `; ${role} is disabled, so it starts no runs either way — the pause holds once you enable it`
|
|
289
|
+
: "";
|
|
290
|
+
return `role ${role} paused${forPhrase} — it starts no new runs${when} (current runs finish; the rest of the team is unaffected)${note}${disabledNote}${tail}`;
|
|
267
291
|
}
|
|
268
292
|
/** The per-role resume confirmation, shared between `tumwater resume --role` and the TUI's
|
|
269
293
|
* Ctrl+P for the same no-drift reason. Carries the team-pause interplay note cmdResume
|
|
270
294
|
* phrases: the team pause is the stronger gate, so with its marker present a freshly
|
|
271
|
-
* resumed role still starts no
|
|
272
|
-
* resumption is real) — saying nothing would promise
|
|
295
|
+
* resumed role still starts no runs (the director is exempt from that gate, so its
|
|
296
|
+
* resumption is real) — saying nothing would promise running the scheduler then deny, the
|
|
273
297
|
* same honest-confirmation contract markerApplyNote's tail serves. */
|
|
274
298
|
export function roleResumeMessage(root, role, changed) {
|
|
275
299
|
if (!changed)
|
|
@@ -278,7 +302,10 @@ export function roleResumeMessage(root, role, changed) {
|
|
|
278
302
|
const teamNote = isTeamPaused(root) && role !== DIRECTOR_ROLE
|
|
279
303
|
? " (the team pause is still active — `tumwater resume` lifts it)"
|
|
280
304
|
: "";
|
|
281
|
-
|
|
305
|
+
const starts = disabledOf(root, [role]).length > 0
|
|
306
|
+
? `it is disabled, so it still starts no runs — enable it with tumwater config set roles.${role}.enabled true`
|
|
307
|
+
: "it starts new runs again";
|
|
308
|
+
return `role ${role} resumed — ${starts}${when}${teamNote}${tail}`;
|
|
282
309
|
}
|
|
283
310
|
/** The `tumwater pause --for <duration>` cap: 90 days. A timed pause is meant to answer "step
|
|
284
311
|
* away for a while and come back to a resumed team" — the same bound the report's day windows
|
|
@@ -288,7 +315,7 @@ export function roleResumeMessage(root, role, changed) {
|
|
|
288
315
|
* value whose wall-clock resume time no `Date` can even hold (a `--for 100000000d` once printed
|
|
289
316
|
* "resumes automatically at NaN:NaN:NaN"). Lives beside the marker contract it caps, which
|
|
290
317
|
* every surface shares, so no new `--for` consumer can outgrow it unnoticed. */
|
|
291
|
-
export const PAUSE_FOR_MAX_MS = 90 *
|
|
318
|
+
export const PAUSE_FOR_MAX_MS = 90 * DAY_MS;
|
|
292
319
|
/** One manual reclaim, shared by `tumwater reclaim` and the dashboard's POST /api/reclaim: with
|
|
293
320
|
* a live team the pass is requested through the marker the orchestrator consumes (so in-use
|
|
294
321
|
* worktrees are respected); with none running it runs in-process, since nothing is in use.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { logEvent, warnEvent, warnEventBestEffort, TUMWATER_SOURCE } from "../events/events.js";
|
|
5
|
-
import { removeQuiet } from "../files/files.js";
|
|
4
|
+
import { dirEntryNames, removeQuiet } from "../files/files.js";
|
|
6
5
|
import { errorMessage } from "../text/text.js";
|
|
7
6
|
import { readJsonFile, writeJsonAtomic } from "../files/json-files.js";
|
|
8
7
|
import { finiteNumber } from "../files/json-object.js";
|
|
9
|
-
import {
|
|
8
|
+
import { saveRunnerState, zeroCounters } from "../runner/runner-state.js";
|
|
9
|
+
import { loadRunnerState } from "../runner/runner-state-load.js";
|
|
10
10
|
import { canonicalRoleId } from "../roles/legacy-role-ids.js";
|
|
11
11
|
import { clearBackoff } from "../scheduling/backoff.js";
|
|
12
12
|
import { abortRequestPath, reclaimRequestPath, resetRequestPath, restartRequestPath, scheduledWakeDir, statePath, wakeRequestPath, STATE_DIR, } from "../paths.js";
|
|
@@ -76,7 +76,7 @@ function warnStatePersistFailed(root, role, err) {
|
|
|
76
76
|
}
|
|
77
77
|
/** Consume a pending reset request from `tumwater reset-counters`, if any: the CLI zeroed the
|
|
78
78
|
* state files only when no team was running; this zeroes the affected runners' in-memory
|
|
79
|
-
* copies (which then re-save), or their next
|
|
79
|
+
* copies (which then re-save), or their next run's save would resurrect the pre-reset values,
|
|
80
80
|
* and writes the zeroed state file directly for requested roles with no runner, which the CLI
|
|
81
81
|
* left to its marker. */
|
|
82
82
|
export function consumeResetRequest(root, runners) {
|
|
@@ -99,7 +99,7 @@ export function consumeResetRequest(root, runners) {
|
|
|
99
99
|
const runnerlessReset = [];
|
|
100
100
|
for (const role of runnerless) {
|
|
101
101
|
try {
|
|
102
|
-
|
|
102
|
+
saveRunnerState(root, zeroCounters(loadRunnerState(root, role)));
|
|
103
103
|
runnerlessReset.push(role);
|
|
104
104
|
}
|
|
105
105
|
catch (err) {
|
|
@@ -109,7 +109,7 @@ export function consumeResetRequest(root, runners) {
|
|
|
109
109
|
const applied = [...affected.map((r) => r.role), ...runnerlessReset];
|
|
110
110
|
if (applied.length > 0) {
|
|
111
111
|
const [only] = applied;
|
|
112
|
-
// One role → filed under that
|
|
112
|
+
// One role → filed under that role; several → one harness-level event listing them.
|
|
113
113
|
if (applied.length === 1 && only)
|
|
114
114
|
logRequestEvent(root, { source: only, type: "counters_reset" });
|
|
115
115
|
else
|
|
@@ -120,7 +120,7 @@ export function consumeResetRequest(root, runners) {
|
|
|
120
120
|
/** Consume a pending wake request from `tumwater wake [--role <id>] [--in <duration>]`, if any.
|
|
121
121
|
* Immediate wakes find the state files already cleared by the CLI when no team was running; this
|
|
122
122
|
* clears the affected runners' in-memory schedules (backoffSeconds, nextRunAt), or their next save
|
|
123
|
-
* would resurrect the pre-wake sleep window and the
|
|
123
|
+
* would resurrect the pre-wake sleep window and the roles would keep sleeping until the original
|
|
124
124
|
* backoff expired, and writes the cleared schedule directly for requested roles with no runner,
|
|
125
125
|
* which the CLI left to its marker. A scheduled wake (`--in`) carries a `notBeforeMs`: before the
|
|
126
126
|
* deadline the marker is left in place (a later poll retries it — the wake takes effect within one
|
|
@@ -129,7 +129,7 @@ export function consumeResetRequest(root, runners) {
|
|
|
129
129
|
* deliberately skipped it. A non-numeric or non-finite value (a hand edit's `1e999`) reads as
|
|
130
130
|
* immediate (defensive; validation happens at the CLI) — leaving Infinity as a deadline would hold
|
|
131
131
|
* `notBefore > Date.now()` true forever and strand the marker, which nothing else deletes. Each
|
|
132
|
-
* woken role logs the existing `wake` event with the operator reason, so the team's early
|
|
132
|
+
* woken role logs the existing `wake` event with the operator reason, so the team's early runs
|
|
133
133
|
* read in the feed as deliberate. */
|
|
134
134
|
export function consumeWakeRequest(root, runners) {
|
|
135
135
|
// A legacy wake.json (the pre-upgrade build wrote scheduled wakes here) keeps its deadline.
|
|
@@ -149,25 +149,26 @@ export function consumeWakeRequest(root, runners) {
|
|
|
149
149
|
if (!wakeNotDue(file))
|
|
150
150
|
applyWakeMarker(root, file, runners);
|
|
151
151
|
}
|
|
152
|
+
/** A scheduled-wake marker's deadline in ms, or undefined when it has none: undefined for a
|
|
153
|
+
* missing or corrupt marker and for a `notBeforeMs` that is absent, non-numeric, or non-finite
|
|
154
|
+
* (the "immediate" reading both the due-check and the pending-list apply — a hand edit's
|
|
155
|
+
* `1e999` must not read as a forever-out deadline). */
|
|
156
|
+
function wakeDeadlineMs(markerFile) {
|
|
157
|
+
const marker = readJsonFile(markerFile);
|
|
158
|
+
return marker === null ? undefined : finiteNumber(marker.notBeforeMs, undefined);
|
|
159
|
+
}
|
|
152
160
|
/** Whether a wake marker's deadline still lies ahead: a missing, non-numeric, or non-finite
|
|
153
|
-
* `notBeforeMs` reads as immediate, and a null (corrupt) marker
|
|
161
|
+
* `notBeforeMs` reads as immediate (one helper owns the reading), and a null (corrupt) marker
|
|
162
|
+
* is due. */
|
|
154
163
|
function wakeNotDue(markerFile) {
|
|
155
|
-
const
|
|
156
|
-
const notBefore = marker === null ? undefined : finiteNumber(marker.notBeforeMs, undefined);
|
|
164
|
+
const notBefore = wakeDeadlineMs(markerFile);
|
|
157
165
|
return notBefore !== undefined && notBefore > Date.now();
|
|
158
166
|
}
|
|
159
167
|
/** The scheduled-wake files in `scheduledWakeDir`, sorted; `.tmp-<pid>` leftovers of an atomic
|
|
160
168
|
* write are skipped. A missing directory is an empty list. */
|
|
161
169
|
function scheduledWakeFiles(root) {
|
|
162
170
|
const dir = scheduledWakeDir(root);
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
names = fs.readdirSync(dir);
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
return [];
|
|
169
|
-
}
|
|
170
|
-
return names
|
|
171
|
+
return dirEntryNames(dir)
|
|
171
172
|
.filter((n) => n.endsWith(".json"))
|
|
172
173
|
.sort()
|
|
173
174
|
.map((n) => path.join(dir, n));
|
|
@@ -179,8 +180,7 @@ export function pendingScheduledWakes(root, now) {
|
|
|
179
180
|
const out = [];
|
|
180
181
|
for (const file of scheduledWakeFiles(root)) {
|
|
181
182
|
const roles = requestedRoles(file);
|
|
182
|
-
const
|
|
183
|
-
const atMs = marker === null ? undefined : finiteNumber(marker.notBeforeMs, undefined);
|
|
183
|
+
const atMs = wakeDeadlineMs(file);
|
|
184
184
|
if (roles && roles.length > 0 && atMs !== undefined && atMs > now)
|
|
185
185
|
out.push({ roles, atMs });
|
|
186
186
|
}
|
|
@@ -188,26 +188,26 @@ export function pendingScheduledWakes(root, now) {
|
|
|
188
188
|
}
|
|
189
189
|
/** Drop the pending scheduled wakes (`tumwater wake --cancel`), returning how many files it
|
|
190
190
|
* removed or rewrote. Only files whose deadline still lies ahead are touched; due and corrupt
|
|
191
|
-
* files stay the consumer's to apply. Without `
|
|
192
|
-
* files
|
|
193
|
-
* too is rewritten without
|
|
194
|
-
export function cancelScheduledWakes(root,
|
|
191
|
+
* files stay the consumer's to apply. Without `roles` each pending file is removed. With `roles`,
|
|
192
|
+
* files naming none of them are skipped, a file left with no role is removed, and a file naming
|
|
193
|
+
* others too is rewritten without the listed ones (same `at` and `notBeforeMs`). */
|
|
194
|
+
export function cancelScheduledWakes(root, roles) {
|
|
195
195
|
let count = 0;
|
|
196
196
|
for (const file of scheduledWakeFiles(root)) {
|
|
197
197
|
if (!wakeNotDue(file))
|
|
198
198
|
continue;
|
|
199
|
-
if (
|
|
199
|
+
if (roles === undefined) {
|
|
200
200
|
removeQuiet(file);
|
|
201
201
|
count++;
|
|
202
202
|
continue;
|
|
203
203
|
}
|
|
204
|
-
const
|
|
205
|
-
if (!roles
|
|
204
|
+
const named = requestedRoles(file);
|
|
205
|
+
if (!named?.some((r) => roles.includes(r)))
|
|
206
206
|
continue;
|
|
207
207
|
const marker = readJsonFile(file);
|
|
208
208
|
if (!marker)
|
|
209
209
|
continue;
|
|
210
|
-
const rest =
|
|
210
|
+
const rest = named.filter((r) => !roles.includes(r));
|
|
211
211
|
if (rest.length === 0)
|
|
212
212
|
removeQuiet(file);
|
|
213
213
|
else
|
|
@@ -234,7 +234,7 @@ function applyWakeMarker(root, markerFile, runners) {
|
|
|
234
234
|
}
|
|
235
235
|
for (const role of runnerlessTargets(root, markerFile, runners)) {
|
|
236
236
|
try {
|
|
237
|
-
|
|
237
|
+
saveRunnerState(root, clearBackoff(loadRunnerState(root, role), Date.now()));
|
|
238
238
|
}
|
|
239
239
|
catch (err) {
|
|
240
240
|
warnStatePersistFailed(root, role, err);
|
|
@@ -270,10 +270,10 @@ export function consumeReclaimRequest(root, reclaim) {
|
|
|
270
270
|
consumeMarkerRequest(reclaimRequestPath(root), () => reclaim?.requestManual());
|
|
271
271
|
}
|
|
272
272
|
/** Consume per-role abort requests from `tumwater abort --role <id>`: one marker file per role (no
|
|
273
|
-
* parsing needed), so a request for an idle OR disabled
|
|
273
|
+
* parsing needed), so a request for an idle OR disabled role is still cleaned up. An in-flight run
|
|
274
274
|
* gets killed and logs exactly one event; anything else is a silent no-op — the marker's presence
|
|
275
275
|
* IS the request, removing it acknowledges. Since merge queue 3/5 a role's in-flight work is often
|
|
276
|
-
* a MERGE rather than a
|
|
276
|
+
* a MERGE rather than a run, so the merge's controller is passed in: an abort for the role being
|
|
277
277
|
* merged right now kills that too (the drain's task sees `userAborted` and discards the pinned ref
|
|
278
278
|
* when the merge ends). There can be several in-flight units at once (land-queue speed 2c) — one
|
|
279
279
|
* per change being verified, the merge slot's stack, and each verified change waiting for it
|
|
@@ -284,13 +284,8 @@ export function consumeReclaimRequest(root, reclaim) {
|
|
|
284
284
|
* refs of the not-yet-processed changes for one-at-a-time recovery, which is already the
|
|
285
285
|
* fallback), and the merge discards every stacked ref when it ends. */
|
|
286
286
|
export function consumeAbortRequests(root, runners, merges) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
markers = fs.readdirSync(path.join(root, STATE_DIR));
|
|
290
|
-
}
|
|
291
|
-
catch {
|
|
292
|
-
return; // .tumwater/ missing — nothing to consume (a fresh repo before the first tick).
|
|
293
|
-
}
|
|
287
|
+
// A missing .tumwater/ (a fresh repo before the first run) reads empty — nothing to consume.
|
|
288
|
+
const markers = dirEntryNames(path.join(root, STATE_DIR));
|
|
294
289
|
// Best-effort report of one marker's failure: the warning can itself throw when the
|
|
295
290
|
// unwritable event feed is what failed, and there is nothing left to report on then.
|
|
296
291
|
const warnFailure = (role, err) => {
|
|
@@ -319,7 +314,7 @@ export function consumeAbortRequests(root, runners, merges) {
|
|
|
319
314
|
merge.controller.abort();
|
|
320
315
|
}
|
|
321
316
|
if (runner?.state.running)
|
|
322
|
-
runner.
|
|
317
|
+
runner.abortRun();
|
|
323
318
|
}
|
|
324
319
|
catch (err) {
|
|
325
320
|
warnFailure(role, err);
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import { loadConfigSafe } from "../config/config.js";
|
|
4
3
|
import { CUSTOM_NAME_RE } from "../config/config-schema.js";
|
|
5
4
|
import { DIRECTOR_ROLE } from "../roles/roles.js";
|
|
6
|
-
import {
|
|
5
|
+
import { runnerEnabled } from "../roles/runner-ids.js";
|
|
7
6
|
import { branchExists, countCommitsInRange, currentBranch, deleteRef, isDirty, isMergedInto, refSha, targetBranch, } from "../git/git.js";
|
|
8
7
|
import { errorMessage } from "../text/text.js";
|
|
9
|
-
import {
|
|
8
|
+
import { loadRunnerState } from "../runner/runner-state-load.js";
|
|
10
9
|
import { branchName, mergeRefName, legacyLandingRefName, worktreePath } from "../paths.js";
|
|
11
10
|
import { isUsableWorktree, removeWorktree } from "../git/worktree.js";
|
|
12
11
|
import { roleWorktreeDir, slotForDir, updateSlotsState } from "../git/slots-state.js";
|
|
13
12
|
import { pausedRoles, resumeRole } from "../team/team-state.js";
|
|
14
13
|
import { git } from "../git/git-run.js";
|
|
15
|
-
/** `tumwater retire --role <id>`: remove a disabled
|
|
14
|
+
/** `tumwater retire --role <id>`: remove a disabled role's persistent worktree and branch, plus the
|
|
16
15
|
* per-role merge ref and paused-state marker that outlive it. The safety rails and the `--force`
|
|
17
16
|
* override live here, so the CLI layer (operator/operator-commands.ts) stays a thin renderer. */
|
|
18
17
|
/** The role's merge pin, falling back to the legacy-named one a not-yet-migrated repo holds. */
|
|
@@ -66,8 +65,8 @@ export async function collectRetire(root, role) {
|
|
|
66
65
|
mergeRefPresent: mergeRef !== null,
|
|
67
66
|
aheadOfMain: ahead + pinnedUnmerged,
|
|
68
67
|
dirty,
|
|
69
|
-
enabledInConfig:
|
|
70
|
-
midRun:
|
|
68
|
+
enabledInConfig: runnerEnabled(config.config, role),
|
|
69
|
+
midRun: loadRunnerState(root, role).running === true,
|
|
71
70
|
};
|
|
72
71
|
}
|
|
73
72
|
/** The artifacts retire manages, in removal order. */
|