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,50 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isJsonObject, nonNegativeNumber } from "../files/json-object.js";
|
|
1
|
+
import { APPROVED_PATCH_IDS_MAX, freshRunnerState, SKIP_REASONS } from "./runner-state.js";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import {
|
|
3
|
+
import { dirEntryNames } from "../files/files.js";
|
|
4
|
+
import { readJsonFile } from "../files/json-files.js";
|
|
5
|
+
import { isJsonObject, nonNegativeNumber, validEntries } from "../files/json-object.js";
|
|
5
6
|
import { cachedByStat } from "../files/stat-cache.js";
|
|
6
7
|
import { statePath, STATE_DIR } from "../paths.js";
|
|
7
|
-
import { baseRoleOf } from "../roles/
|
|
8
|
+
import { baseRoleOf } from "../roles/runner-ids.js";
|
|
8
9
|
import { allRoleIds } from "../roles/roles.js";
|
|
9
|
-
/** The
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* clock arithmetic (backoff ladders, wakes, yield ring) in backoff.ts; the generic file
|
|
14
|
-
* helpers are src/files/files.ts and the JSON convention
|
|
15
|
-
* itself is src/files/json-files.ts. */
|
|
16
|
-
/** How many times a progressing-timeout review may be resumed before the next attempt
|
|
17
|
-
* reviews the change from scratch (LoopState.reviewResume). The budget bounds a reviewer that
|
|
18
|
-
* keeps timing out mid-progress, so a wedged review converges to a fresh run instead of
|
|
19
|
-
* resuming the same session forever. */
|
|
20
|
-
export const REVIEW_RESUME_LIMIT = 2;
|
|
21
|
-
const SKIP_REASONS = [
|
|
22
|
-
"no-free-entry",
|
|
23
|
-
"claim-hold",
|
|
24
|
-
"deferred-backlog",
|
|
25
|
-
"deferred-no-work",
|
|
26
|
-
"deferred-plans-full",
|
|
27
|
-
];
|
|
28
|
-
/** A new LoopState for one role, before its first tick. */
|
|
29
|
-
export function freshLoopState(role) {
|
|
30
|
-
return {
|
|
31
|
-
role,
|
|
32
|
-
runs: 0,
|
|
33
|
-
commits: 0,
|
|
34
|
-
nextRunAt: 0,
|
|
35
|
-
backoffSeconds: 0,
|
|
36
|
-
lastMainHead: "",
|
|
37
|
-
generatedTokens: 0,
|
|
38
|
-
peakContextTokens: 0,
|
|
39
|
-
totalCostUsd: 0,
|
|
40
|
-
dayStamp: "",
|
|
41
|
-
dayCostUsd: 0,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
10
|
+
/** The read side of the runner's persisted state: the tolerant load of one role's file (heals
|
|
11
|
+
* every field a hand edit or an older build could leave unusable, and renames the keys an older
|
|
12
|
+
* build wrote) and the live-claim scan over every role's file. The type, the fresh-state
|
|
13
|
+
* defaults, the atomic save and zeroCounters stay in runner-state.ts. */
|
|
44
14
|
/** A persisted numeric field, healed to a finite non-negative number (the fallback when it is
|
|
45
|
-
* absent, foreign, NaN/±Infinity, or negative). Every numeric field on
|
|
15
|
+
* absent, foreign, NaN/±Infinity, or negative). Every numeric field on RunnerState — top-level or
|
|
46
16
|
* inside one of the nested records below — is a count, a duration, an epoch-ms stamp, or an
|
|
47
|
-
* amount, so zero is the only sensible repair; the `
|
|
17
|
+
* amount, so zero is the only sensible repair; the `run-usage.ts` and `budget.ts` writers floor
|
|
48
18
|
* the same three quantities they own. A guard count (mergeConflicts, mergeCheckFailures,
|
|
49
19
|
* unreviewFailures) heals to 0 like the rest: a corrupt strike tally re-accumulates from the
|
|
50
20
|
* next attempt, the same "the field is unusable, start the count over" repair the top-level heal
|
|
@@ -55,19 +25,12 @@ function healNumber(value, fallback) {
|
|
|
55
25
|
/** Heal the per-pair daily spend map: a non-object reads as `{}`, entries that are not
|
|
56
26
|
* non-negative finite numbers drop, and a `__proto__` key stays an own property. */
|
|
57
27
|
function healPairCosts(value) {
|
|
58
|
-
|
|
59
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
60
|
-
return out;
|
|
61
|
-
for (const [k, v] of Object.entries(value)) {
|
|
62
|
-
if (typeof v === "number" && Number.isFinite(v) && v >= 0)
|
|
63
|
-
Object.defineProperty(out, k, { value: v, enumerable: true, writable: true, configurable: true });
|
|
64
|
-
}
|
|
65
|
-
return out;
|
|
28
|
+
return validEntries(value, (v) => nonNegativeNumber(v, -1) >= 0);
|
|
66
29
|
}
|
|
67
30
|
/** Heal one OPTIONAL numeric field of a persisted record in place, preserving the field's
|
|
68
31
|
* absent shape: an absent field stays absent (an older state file with no such field reads
|
|
69
32
|
* unchanged), while a present field heals by healNumber's rule. Every optional numeric field
|
|
70
|
-
*
|
|
33
|
+
* loadRunnerState repairs — the eight top-level stamps/streaks and conflictHandback.round —
|
|
71
34
|
* goes through this one guard, so the "absent stays absent" repair cannot be dropped from
|
|
72
35
|
* one field while another keeps it. */
|
|
73
36
|
function healOptionalNumber(rec, key) {
|
|
@@ -77,7 +40,7 @@ function healOptionalNumber(rec, key) {
|
|
|
77
40
|
}
|
|
78
41
|
/** Heal one persisted nested record: absent stays absent; a present non-object (a string,
|
|
79
42
|
* number, NaN, null, or array) is dropped to undefined — every consumer of these records reads
|
|
80
|
-
* them with property access or mutation (merge-core sets `lastReview.exhausted`,
|
|
43
|
+
* them with property access or mutation (merge-core sets `lastReview.exhausted`, run-apply
|
|
81
44
|
* adds to `mergeConflicts.count`), and property access on a torn scalar reads undefined where
|
|
82
45
|
* the mutation throws "Cannot create property … on string" in a strict ES module; a present
|
|
83
46
|
* object keeps its shape with each numeric field repaired by `heal`. */
|
|
@@ -87,13 +50,13 @@ function healRecord(value, heal) {
|
|
|
87
50
|
heal(value);
|
|
88
51
|
return value;
|
|
89
52
|
}
|
|
90
|
-
/** Keys an older build wrote, mapped to their current names.
|
|
53
|
+
/** Keys an older build wrote, mapped to their current names. loadRunnerState copies the old key's
|
|
91
54
|
* value to the new key when the new key is absent and always drops the old key, so a file holding
|
|
92
55
|
* both keeps the new value and the next save writes only current keys. */
|
|
93
56
|
const LEGACY_STATE_KEYS = {
|
|
94
|
-
ticks: "runs", //
|
|
95
|
-
lastTickStartedAt: "lastRunStartedAt", //
|
|
96
|
-
lastTickEndedAt: "lastRunEndedAt", //
|
|
57
|
+
ticks: "runs", // retired-name-ok: legacy name
|
|
58
|
+
lastTickStartedAt: "lastRunStartedAt", // retired-name-ok: legacy name
|
|
59
|
+
lastTickEndedAt: "lastRunEndedAt", // retired-name-ok: legacy name
|
|
97
60
|
// retired-name-ok:start old state keys
|
|
98
61
|
landingReviewError: "mergeReviewError", // vocabulary-ok: legacy state key read from old files
|
|
99
62
|
landingCheckFailures: "mergeCheckFailures", // vocabulary-ok: legacy state key read from old files
|
|
@@ -110,7 +73,7 @@ function migrateLegacyStateKeys(saved) {
|
|
|
110
73
|
delete saved[oldKey];
|
|
111
74
|
}
|
|
112
75
|
}
|
|
113
|
-
/** Load the
|
|
76
|
+
/** Load the runner's persisted state; never throws — a missing or unreadable file yields a
|
|
114
77
|
* fresh state, fields absent from an older file fall back to defaults, and a numeric field
|
|
115
78
|
* (top-level or nested) that is present but not a finite non-negative number is healed to 0,
|
|
116
79
|
* a nested record that is present but not a plain object is dropped, and lastReview.reasons is
|
|
@@ -119,11 +82,19 @@ function migrateLegacyStateKeys(saved) {
|
|
|
119
82
|
* keeps a hand-edited string, null, or negative out of the schedulers and the dashboards'
|
|
120
83
|
* arithmetic instead of relying on each reader to guard it. Optional fields that are absent
|
|
121
84
|
* stay absent, so an older state file's shape is preserved. */
|
|
122
|
-
export function
|
|
85
|
+
export function loadRunnerState(root, role) {
|
|
123
86
|
const saved = readJsonFile(statePath(root, role));
|
|
124
87
|
if (saved !== undefined && saved !== null && isJsonObject(saved))
|
|
125
88
|
migrateLegacyStateKeys(saved);
|
|
126
|
-
const s = { ...
|
|
89
|
+
const s = { ...freshRunnerState(role), ...(saved ?? {}) };
|
|
90
|
+
// The file name is the identity: a state file written by an older build (or moved by
|
|
91
|
+
// migrateLegacyRoleIds) can carry a stale `role` inside, and saveRunnerState writes
|
|
92
|
+
// statePath(root, state.role) — so letting the saved value win would save the file back
|
|
93
|
+
// under a retired id and leak a frozen copy for every later start to reload (the frozen-copy
|
|
94
|
+
// split behind BUGS.md 2026-10-23). Force the role the caller asked for, whatever the file
|
|
95
|
+
// says, so the state saves (and loads) under the name it was asked for.
|
|
96
|
+
// (Retired-name-ok: legacy role id read from an old state file.)
|
|
97
|
+
s.role = role;
|
|
127
98
|
s.runs = healNumber(s.runs, 0);
|
|
128
99
|
s.commits = healNumber(s.commits, 0);
|
|
129
100
|
s.nextRunAt = healNumber(s.nextRunAt, 0);
|
|
@@ -142,10 +113,21 @@ export function loadLoopState(root, role) {
|
|
|
142
113
|
healOptionalNumber(s, "quietKillStreak");
|
|
143
114
|
healOptionalNumber(s, "consecutiveErrors");
|
|
144
115
|
healOptionalNumber(s, "noChangeStreak");
|
|
116
|
+
// The breaker's excused-streak ack is a count like the others — a corrupt value (a negative,
|
|
117
|
+
// NaN, or foreign type) would otherwise silently defeat or mistime the breaker's bar on the
|
|
118
|
+
// next poll, so it heals to 0 (a fresh ack) the same way the error streak itself does.
|
|
119
|
+
healOptionalNumber(s, "streakAcked");
|
|
145
120
|
healOptionalNumber(s, "unreviewFailures");
|
|
146
121
|
healOptionalNumber(s, "parkedSince");
|
|
147
122
|
if (s.skipReason !== undefined && !SKIP_REASONS.includes(s.skipReason))
|
|
148
123
|
delete s.skipReason;
|
|
124
|
+
// The one-id field an older state file carries folds into the list.
|
|
125
|
+
const legacy = saved?.lastApprovedPatchId;
|
|
126
|
+
delete s.lastApprovedPatchId;
|
|
127
|
+
if (s.approvedPatchIds !== undefined || typeof legacy === "string") {
|
|
128
|
+
const ids = [...(Array.isArray(s.approvedPatchIds) ? s.approvedPatchIds : []), ...(typeof legacy === "string" ? [legacy] : [])];
|
|
129
|
+
s.approvedPatchIds = ids.filter((id) => typeof id === "string").slice(-APPROVED_PATCH_IDS_MAX);
|
|
130
|
+
}
|
|
149
131
|
if (s.reviewResume !== undefined) {
|
|
150
132
|
const r = healRecord(s.reviewResume, (v) => {
|
|
151
133
|
v.attempts = healNumber(v.attempts, 0);
|
|
@@ -165,7 +147,7 @@ export function loadLoopState(root, role) {
|
|
|
165
147
|
v.at = healNumber(v.at, 0);
|
|
166
148
|
// The one nested non-numeric slot whose foreign type throws rather than reads odd: the
|
|
167
149
|
// rejected-review notes build a numbered list from `reasons` (numberedList's `.map`), so
|
|
168
|
-
// a hand-edited scalar or a missing field would crash the next
|
|
150
|
+
// a hand-edited scalar or a missing field would crash the next run's prompt. Rebuild it
|
|
169
151
|
// to its string entries — the same "the field is unusable, start it over" repair the
|
|
170
152
|
// numeric fields get.
|
|
171
153
|
v.reasons = Array.isArray(v.reasons)
|
|
@@ -186,6 +168,15 @@ export function loadLoopState(root, role) {
|
|
|
186
168
|
? v.reasonCounts.map((c) => healNumber(c, 0))
|
|
187
169
|
: undefined;
|
|
188
170
|
});
|
|
171
|
+
// A torn retry record reads as none: leftover recovery is the backstop for the pin.
|
|
172
|
+
if (s.mainRedRetry !== undefined &&
|
|
173
|
+
!(isJsonObject(s.mainRedRetry) &&
|
|
174
|
+
typeof s.mainRedRetry.head === "string" &&
|
|
175
|
+
isJsonObject(s.mainRedRetry.entry) &&
|
|
176
|
+
typeof s.mainRedRetry.entry.role === "string" &&
|
|
177
|
+
typeof s.mainRedRetry.entry.summary === "string" &&
|
|
178
|
+
(s.mainRedRetry.waitingSince === undefined || typeof s.mainRedRetry.waitingSince === "number")))
|
|
179
|
+
s.mainRedRetry = undefined;
|
|
189
180
|
if (s.mergeConflicts !== undefined)
|
|
190
181
|
s.mergeConflicts = healRecord(s.mergeConflicts, (v) => {
|
|
191
182
|
v.count = healNumber(v.count, 0);
|
|
@@ -217,9 +208,9 @@ export function loadLoopState(root, role) {
|
|
|
217
208
|
v.probeAt = healNumber(v.probeAt, 0);
|
|
218
209
|
v.cooldownMs = healNumber(v.cooldownMs, 0);
|
|
219
210
|
});
|
|
220
|
-
// The image-reroute marker is a one-shot nested record consumed by the next
|
|
211
|
+
// The image-reroute marker is a one-shot nested record consumed by the next run; a torn
|
|
221
212
|
// scalar is dropped (its consumers read/mutate fields), and the tier is repaired to a
|
|
222
|
-
// known tier name so a corrupt tier cannot route a rerouted
|
|
213
|
+
// known tier name so a corrupt tier cannot route a rerouted run to a phantom seam.
|
|
223
214
|
if (s.imageReroute !== undefined) {
|
|
224
215
|
const healed = healRecord(s.imageReroute, () => { });
|
|
225
216
|
s.imageReroute =
|
|
@@ -229,51 +220,11 @@ export function loadLoopState(root, role) {
|
|
|
229
220
|
}
|
|
230
221
|
return s;
|
|
231
222
|
}
|
|
232
|
-
/** Persist the loop's state atomically via writeJsonAtomic, so a crash mid-write cannot
|
|
233
|
-
* leave a torn file behind. Its per-pid tmp name matters here: two processes can write one
|
|
234
|
-
* role's state concurrently — the orchestrator saves at tick end and around its review gate
|
|
235
|
-
* while `tumwater reset-counters` rewrites the same file from the CLI process — and per-pid
|
|
236
|
-
* names give each writer its own tmp with a clean last-writer-wins. */
|
|
237
|
-
export function saveLoopState(root, state) {
|
|
238
|
-
writeJsonAtomic(statePath(root, state.role), state);
|
|
239
|
-
}
|
|
240
|
-
/** Zero the accumulated counters (runs, commits, tokens, cost) so a fresh observation
|
|
241
|
-
* window can begin. Pure: returns a new state and preserves everything else — scheduling
|
|
242
|
-
* fields (nextRunAt, backoffSeconds), wake tracking (lastMainHead), and the last-result
|
|
243
|
-
* fields. peakContextTokens is zeroed too: under per-tick semantics it holds the loop's
|
|
244
|
-
* last completed tick's peak, so a fresh window must clear it or sleeping loops keep
|
|
245
|
-
* showing their old value until they next tick. The daily cost budget window (dayStamp/
|
|
246
|
-
* dayCostUsd) is deliberately NOT zeroed: the budget is a safety valve, not an observation
|
|
247
|
-
* window — zeroing today's spend would let the cap be bypassed by running reset-counters.
|
|
248
|
-
*/
|
|
249
|
-
export function zeroCounters(s) {
|
|
250
|
-
return { ...s, runs: 0, commits: 0, generatedTokens: 0, peakContextTokens: 0, totalCostUsd: 0 };
|
|
251
|
-
}
|
|
252
|
-
/** The skip reason a loop's persisted state yields when it does not run this round: a pending
|
|
253
|
-
* resume wait outranks backoff, anything else is idle. The shared tail of the two skip
|
|
254
|
-
* classifiers — once-round.ts's settleSkipped (which adds the disabled arm) and cli/cli-run.ts's
|
|
255
|
-
* once-summary (which adds the team-pause arm) — so the two surfaces cannot drift on which
|
|
256
|
-
* reason wins when several conditions hold at once. */
|
|
257
|
-
export function stateSkipReason(s) {
|
|
258
|
-
if (s.resumePending)
|
|
259
|
-
return "resume pending";
|
|
260
|
-
if (s.backoffSeconds > 0)
|
|
261
|
-
return "backoff"; // vocabulary-ok: skip-reason code is a state value until the state rename
|
|
262
|
-
return "idle";
|
|
263
|
-
}
|
|
264
|
-
/** True when the loop still owes follow-up work for its current task after a tick: a rejected
|
|
265
|
-
* change it must revise (state.revision) or a session it must resume (state.resumePending).
|
|
266
|
-
* Both keep the instance bound to the same task, so the claim-release rule
|
|
267
|
-
* (tick/tick-finalize.ts), the conflict-handback rule (tick/tick-apply.ts), and claims.ts's
|
|
268
|
-
* idle test all read this one predicate instead of repeating the revision/resume conjunction. */
|
|
269
|
-
export function hasOutstandingWork(s) {
|
|
270
|
-
return s.revision !== undefined || s.resumePending === true;
|
|
271
|
-
}
|
|
272
223
|
/** Per-role cache of the persisted live claim, keyed by the state file's stat — one stat per
|
|
273
224
|
* role per call in steady state, a read only when the file changed (cachedByStat's contract),
|
|
274
225
|
* so a team poll's allLiveClaims does no read I/O once every claim is known. */
|
|
275
226
|
const liveClaimCache = new Map();
|
|
276
|
-
/** Every
|
|
227
|
+
/** Every runner id whose persisted claim must be considered — the built-in catalog roles
|
|
277
228
|
* (allRoleIds) plus the parallel-work instance ids (feature-2, bugfix-3, …) that have a state
|
|
278
229
|
* file. Instances are not in the built-in catalog, so they are enumerated from the state
|
|
279
230
|
* directory rather than the config: a claim lives in its role's state file, and reading the
|
|
@@ -282,17 +233,13 @@ const liveClaimCache = new Map();
|
|
|
282
233
|
* base). Any state file whose id resolves away from itself under baseRoleOf is an instance
|
|
283
234
|
* id; the named non-role files in that directory (orchestrator.json, claude-account.json, …)
|
|
284
235
|
* resolve to themselves and so are skipped, as are the base roles allRoleIds already names. */
|
|
285
|
-
function
|
|
236
|
+
function liveClaimRunnerIds(root) {
|
|
286
237
|
const ids = new Set(allRoleIds());
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
catch {
|
|
295
|
-
// no state directory yet — the catalog roles (fresh, claim-less) suffice
|
|
238
|
+
// No state directory yet reads empty — the catalog roles (fresh, claim-less) suffice.
|
|
239
|
+
for (const name of dirEntryNames(path.join(root, STATE_DIR, "state"))) {
|
|
240
|
+
const id = name.replace(/\.json$/, "");
|
|
241
|
+
if (baseRoleOf(id) !== id)
|
|
242
|
+
ids.add(id);
|
|
296
243
|
}
|
|
297
244
|
return [...ids];
|
|
298
245
|
}
|
|
@@ -308,10 +255,10 @@ function liveClaimLoopIds(root) {
|
|
|
308
255
|
* under `feature-2`; callers fold it to its base. */
|
|
309
256
|
export function allLiveClaims(root) {
|
|
310
257
|
const out = new Map();
|
|
311
|
-
for (const id of
|
|
258
|
+
for (const id of liveClaimRunnerIds(root)) {
|
|
312
259
|
const file = statePath(root, id);
|
|
313
260
|
const claim = cachedByStat(liveClaimCache, file, file, () => {
|
|
314
|
-
const s =
|
|
261
|
+
const s = loadRunnerState(root, id);
|
|
315
262
|
return s.claim ? { role: id, key: s.claim.key } : null;
|
|
316
263
|
}, (v) => ({ ...v }));
|
|
317
264
|
if (claim)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { writeJsonAtomic } from "../files/json-files.js";
|
|
2
|
+
import { statePath } from "../paths.js";
|
|
3
|
+
import { tryOrWarn } from "../events/events.js";
|
|
4
|
+
/** The runner's persisted state file — one JSON object per role under .tumwater/ — and the
|
|
5
|
+
* observation-window counter reset: fresh-state defaults, the atomic save, and zeroCounters
|
|
6
|
+
* (the tolerant load and the live-claim scan are runner-state-load.ts). The scheduling POLICY
|
|
7
|
+
* that mutates this state from run outcomes and operator wakes — the outcome application and
|
|
8
|
+
* resume limits in run-apply.ts, the clock arithmetic (backoff ladders, wakes, yield ring) in
|
|
9
|
+
* backoff.ts; the generic file helpers are src/files/files.ts and the JSON convention itself is
|
|
10
|
+
* src/files/json-files.ts. */
|
|
11
|
+
/** How many times a progressing-timeout review may be resumed before the next attempt
|
|
12
|
+
* reviews the change from scratch (RunnerState.reviewResume). The budget bounds a reviewer that
|
|
13
|
+
* keeps timing out mid-progress, so a wedged review converges to a fresh run instead of
|
|
14
|
+
* resuming the same session forever. */
|
|
15
|
+
export const REVIEW_RESUME_LIMIT = 2;
|
|
16
|
+
/** Approved patch-ids a runner remembers (RunnerState.approvedPatchIds). */
|
|
17
|
+
export const APPROVED_PATCH_IDS_MAX = 8;
|
|
18
|
+
export const SKIP_REASONS = [
|
|
19
|
+
"no-free-entry",
|
|
20
|
+
"claim-hold",
|
|
21
|
+
"deferred-backlog",
|
|
22
|
+
"deferred-no-work",
|
|
23
|
+
"deferred-plans-full",
|
|
24
|
+
"instances-full",
|
|
25
|
+
"maintenance-due-cap",
|
|
26
|
+
"yield-gap",
|
|
27
|
+
];
|
|
28
|
+
/** A new RunnerState for one role, before its first run. */
|
|
29
|
+
export function freshRunnerState(role) {
|
|
30
|
+
return {
|
|
31
|
+
role,
|
|
32
|
+
runs: 0,
|
|
33
|
+
commits: 0,
|
|
34
|
+
nextRunAt: 0,
|
|
35
|
+
backoffSeconds: 0,
|
|
36
|
+
lastMainHead: "",
|
|
37
|
+
generatedTokens: 0,
|
|
38
|
+
peakContextTokens: 0,
|
|
39
|
+
totalCostUsd: 0,
|
|
40
|
+
dayStamp: "",
|
|
41
|
+
dayCostUsd: 0,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Persist the runner's state atomically via writeJsonAtomic, so a crash mid-write cannot
|
|
45
|
+
* leave a torn file behind. Its per-pid tmp name matters here: two processes can write one
|
|
46
|
+
* role's state concurrently — the orchestrator saves at run end and around its review gate
|
|
47
|
+
* while `tumwater reset-counters` rewrites the same file from the CLI process — and per-pid
|
|
48
|
+
* names give each writer its own tmp with a clean last-writer-wins. */
|
|
49
|
+
export function saveRunnerState(root, state) {
|
|
50
|
+
writeJsonAtomic(statePath(root, state.role), state);
|
|
51
|
+
}
|
|
52
|
+
/** Save for callers that cannot afford a throw — the orchestrator's poll loop, which has no
|
|
53
|
+
* catch, and a run's end, where a throw would skip run_end and the launch task's breaker fold:
|
|
54
|
+
* a failed write warns under the role's id and returns. The in-memory state stays ahead of the
|
|
55
|
+
* file until the runner's next successful save. */
|
|
56
|
+
export function saveRunnerStateBestEffort(root, state) {
|
|
57
|
+
tryOrWarn(root, state.role, "save the role's state", () => saveRunnerState(root, state));
|
|
58
|
+
}
|
|
59
|
+
/** Zero the accumulated counters (runs, commits, tokens, cost) so a fresh observation
|
|
60
|
+
* window can begin. Pure: returns a new state and preserves everything else — scheduling
|
|
61
|
+
* fields (nextRunAt, backoffSeconds), wake tracking (lastMainHead), and the last-result
|
|
62
|
+
* fields. peakContextTokens is zeroed too: under per-run semantics it holds the runner's
|
|
63
|
+
* last completed run's peak, so a fresh window must clear it or sleeping runners keep
|
|
64
|
+
* showing their old value until they next run. The daily cost budget window (dayStamp/
|
|
65
|
+
* dayCostUsd) is deliberately NOT zeroed: the budget is a safety valve, not an observation
|
|
66
|
+
* window — zeroing today's spend would let the cap be bypassed by running reset-counters.
|
|
67
|
+
*/
|
|
68
|
+
export function zeroCounters(s) {
|
|
69
|
+
return { ...s, runs: 0, commits: 0, generatedTokens: 0, peakContextTokens: 0, totalCostUsd: 0 };
|
|
70
|
+
}
|
|
71
|
+
/** The skip reason a runner's persisted state yields when it does not run this round: a pending
|
|
72
|
+
* resume wait outranks backoff, anything else is idle. The shared tail of the two skip
|
|
73
|
+
* classifiers — once-round.ts's settleSkipped (which adds the disabled arm) and cli/cli-start.ts's
|
|
74
|
+
* once-summary (which adds the team-pause arm) — so the two surfaces cannot drift on which
|
|
75
|
+
* reason wins when several conditions hold at once. */
|
|
76
|
+
export function stateSkipReason(s) {
|
|
77
|
+
if (s.resumePending)
|
|
78
|
+
return "resume pending";
|
|
79
|
+
if (s.backoffSeconds > 0)
|
|
80
|
+
return "backoff"; // vocabulary-ok: skip-reason code is a state value until the state rename
|
|
81
|
+
return "idle";
|
|
82
|
+
}
|
|
83
|
+
/** True when the runner still owes follow-up work for its current task after a run: a rejected
|
|
84
|
+
* change it must revise (state.revision) or a session it must resume (state.resumePending).
|
|
85
|
+
* Both keep the instance bound to the same task, so the claim-release rule
|
|
86
|
+
* (run/run-finalize.ts), the conflict-handback rule (run/run-apply.ts), and claims.ts's
|
|
87
|
+
* idle test all read this one predicate instead of repeating the revision/resume conjunction. */
|
|
88
|
+
export function hasOutstandingWork(s) {
|
|
89
|
+
return s.revision !== undefined || s.resumePending === true;
|
|
90
|
+
}
|
|
91
|
+
/** True when the runner got an explicit operator wake after its last completed run
|
|
92
|
+
* (state.wokenAt newer than lastRunEndedAt), so the scheduler ships its min-interval gap and
|
|
93
|
+
* the need-based deferral stands the runner down. The single home of the "freshly woken since
|
|
94
|
+
* the last run end" test, previously repeated inline by the due-check, plan deferral, and
|
|
95
|
+
* need-based deferral (scheduling.ts's isEligible, planDeferral, and deferRunReason) and by
|
|
96
|
+
* the orchestrator's fresh-demand and plans-full stand-down tests (scheduling-skip.ts's
|
|
97
|
+
* per-runner demand and scheduling-admit.ts's stand-down guard), so those surfaces cannot drift
|
|
98
|
+
* on what counts
|
|
99
|
+
* as a fresh wake. */
|
|
100
|
+
export function freshlyWoken(s) {
|
|
101
|
+
return s.wokenAt !== undefined && s.wokenAt > (s.lastRunEndedAt ?? 0);
|
|
102
|
+
}
|