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,43 +1,65 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import { loadConfigSafe } from "../config/config.js";
|
|
3
2
|
import { envPath } from "../files/files.js";
|
|
4
3
|
import { orchestratorAlive, readOrchestratorInfo } from "../team/orchestrator-info.js";
|
|
5
4
|
import { systemProcessProbe } from "../process/process-table.js";
|
|
6
5
|
import { checkOrphans } from "./doctor-orphans.js";
|
|
7
|
-
import { checkBudget,
|
|
6
|
+
import { checkBudget, checkErrorStreaks, checkMainRed, checkMaintenanceQuota, checkPause, checkProviderHolds, checkQuietHours, checkSubscriptionWindow } from "./doctor-holds.js";
|
|
8
7
|
import { checkRootInstall } from "./doctor-install.js";
|
|
9
8
|
import { checkNotify } from "./doctor-notify.js";
|
|
10
9
|
import { checkLaunchServicesPorts } from "./doctor-launch-services.js";
|
|
11
10
|
import { plural } from "../text/phrases.js";
|
|
12
11
|
import { shortSha } from "../text/format.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
12
|
+
import { failOutcome } from "./outcome.js";
|
|
13
|
+
import { errorMessage } from "../text/text.js";
|
|
14
|
+
import { checkBackendBinary, checkBrief, checkBootstrap, checkClaudeTranscripts, checkExecPath, checkFunes, checkGitBinary, checkConfigKeyNames, checkInit, checkOpencodeVersion, checkNodeVersion, checkPiExtensions, checkRepo, } from "./doctor-checks.js";
|
|
15
|
+
import { checkBuild, checkBuildCheck, checkCiParity, checkDiskSpace, checkEventLog, checkFlakyChecks, checkPostMergeChurn, checkQaCoverage, checkStateDir, } from "./doctor-project-checks.js";
|
|
16
|
+
import { sampleFreeBytes } from "../gates/disk-gate.js";
|
|
17
|
+
import { checkCheckContention, checkLeftoverRoleBranches, checkMergeLock, checkProviderConcurrency, checkProviderContention, checkTumwaterPins, checkWorkInstances, checkWorktreePool, } from "./doctor-contention-checks.js";
|
|
18
|
+
import { checkFeedbackFiling, checkImageInput, checkTierModels } from "./doctor-model-checks.js";
|
|
19
|
+
import { checkFallbackChain, checkFallbackModel } from "./doctor-fallback-checks.js";
|
|
20
|
+
import { checkAnthroApiKey, checkClaudeAuth, checkClaudeVersion } from "./doctor-claude-checks.js";
|
|
21
|
+
import { checkOpencode } from "./doctor-opencode-checks.js";
|
|
22
|
+
import { checkCodexAccount, checkCodexAuth, checkCodexHome, checkCodexLeaks, checkCodexModels, checkCodexSessions, checkCodexWriteGuard, checkCodexVersion, } from "./doctor-codex-checks.js";
|
|
23
|
+
import { backendQualifiedSelector } from "../config/config-views.js";
|
|
16
24
|
import { seamsInUse } from "../config/config-fallbacks.js";
|
|
17
|
-
import { checkBacklogHeadings, checkFixClaims, checkLiveFinished, checkPlanDependencies,
|
|
25
|
+
import { checkBacklogHeadings, checkFixClaims, checkLiveFinished, checkPlanDependencies, checkStalledEntries, checkStrandedPlans, checkValidationGaps } from "./doctor-backlog-plans.js";
|
|
26
|
+
import { checkRefusedEntries, checkRegressedFixes, checkStaleBugs, checkStaleQuestions, checkWaitingBugs } from "./doctor-backlog-bugs.js";
|
|
27
|
+
import { checkMergeQueueFiles, checkPostReviews, checkStaleQueuedMerges, checkStalePrompts, checkUncommittedBacklog } from "./doctor-backlog.js";
|
|
18
28
|
/** Pre-flight environment check (`tumwater doctor`). The harness's preconditions are
|
|
19
29
|
* scattered across fail-fast checks that each command re-runs on its own (requireReadyRepo in
|
|
20
30
|
* cli.ts walks git-binary → repo → tumwater.json → commits and stops at the first failure;
|
|
21
|
-
*
|
|
31
|
+
* cmdStart adds pi-on-PATH and orchestrator-alive), so a user facing a partially initialized or
|
|
22
32
|
* drifted environment gets one error at a time. doctor runs every check, reports each result
|
|
23
33
|
* individually instead of stopping at the first failure, and exits 0/1 so it can be scripted —
|
|
24
34
|
* the pre-flight sibling of `status --json`, which queries live team state while doctor
|
|
25
35
|
* verdicts on the environment. Every check is read-only against .tumwater/ (works with or
|
|
26
36
|
* without a running harness) and none runs the project's build/test: that can take minutes and
|
|
27
37
|
* belongs in the review gate / red-main check, not a pre-flight. The individual checks live in
|
|
28
|
-
* doctor-checks.ts
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
* doctor-checks.ts, doctor-project-checks.ts, doctor-contention-checks.ts, doctor-model-checks.ts,
|
|
39
|
+
* doctor-claude-checks.ts, doctor-opencode-checks.ts, doctor-backlog.ts,
|
|
40
|
+
* doctor-backlog-plans.ts and doctor-backlog-bugs.ts;
|
|
41
|
+
* this module composes them into the report. */
|
|
42
|
+
/** Run one doctor check, turning a throw or rejection into a fail row: a check that crashed
|
|
43
|
+
* verified nothing, so it must not read as a pass or abort the report. */
|
|
44
|
+
export async function runDoctorCheck(run) {
|
|
45
|
+
try {
|
|
46
|
+
return await run();
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return failOutcome(`the check threw: ${errorMessage(err)}`);
|
|
50
|
+
}
|
|
34
51
|
}
|
|
35
52
|
/** Run every check in order and compose the report. Read-only against .tumwater/ by
|
|
36
53
|
* construction — no check removes or repairs anything (the state-dir probe writes a temp file
|
|
37
54
|
* and deletes it again; the orphan check reports processes, never signals them) — so doctor
|
|
38
55
|
* works identically with or without a running harness. `probe` feeds the orphan and port
|
|
39
|
-
* checks, so tests can pin the report without reading the host's process table.
|
|
40
|
-
|
|
56
|
+
* checks, so tests can pin the report without reading the host's process table. `diskSample`
|
|
57
|
+
* feeds the disk check the same way, so tests can pin the report without reading the host's
|
|
58
|
+
* real free space (a hosted runner under the 10 GB floor would fail its row). `only` runs
|
|
59
|
+
* just the named checks, in report order; a skipped check is never evaluated, and a named
|
|
60
|
+
* conditional check that returns null leaves no row. A check that throws becomes its own fail
|
|
61
|
+
* row, and every other check still runs. */
|
|
62
|
+
export async function runDoctor(root, pathEnv = envPath(), probe = systemProcessProbe, probeImages = "never", only, diskSample = sampleFreeBytes) {
|
|
41
63
|
// Loaded once for the checks that read config (repo's baseBranch); a broken file stays
|
|
42
64
|
// undefined — checkInit reports it verbatim — so doctor still runs every other check.
|
|
43
65
|
const { config } = loadConfigSafe(root);
|
|
@@ -48,7 +70,7 @@ export async function runDoctor(root, pathEnv = envPath(), probe = systemProcess
|
|
|
48
70
|
const routing = config
|
|
49
71
|
? seamsInUse(config).map((s) => ({
|
|
50
72
|
label: s.label,
|
|
51
|
-
selector: s.model !== undefined ?
|
|
73
|
+
selector: s.model !== undefined ? backendQualifiedSelector({ ...s, model: s.model }) : "no model declared",
|
|
52
74
|
backend: s.agent ?? "pi",
|
|
53
75
|
}))
|
|
54
76
|
: [];
|
|
@@ -56,80 +78,107 @@ export async function runDoctor(root, pathEnv = envPath(), probe = systemProcess
|
|
|
56
78
|
const header = orchestratorAlive(root, info) && info
|
|
57
79
|
? `tumwater doctor — running (pid ${info.pid}${info.build ? `, version ${shortSha(info.build.sha)}${info.build.stale ? " — update available" : ""}${info.build.restartBlocked ? " (restart blocked)" : ""}` : ""})`
|
|
58
80
|
: "tumwater doctor — not running";
|
|
59
|
-
const
|
|
60
|
-
{ name: "node",
|
|
61
|
-
{ name: "
|
|
62
|
-
{ name: "
|
|
63
|
-
{ name: "
|
|
64
|
-
{ name: "
|
|
65
|
-
{ name: "
|
|
81
|
+
const steps = [
|
|
82
|
+
{ name: "node", run: () => checkNodeVersion() },
|
|
83
|
+
{ name: "node binary", run: () => checkExecPath() },
|
|
84
|
+
{ name: "git binary", run: () => checkGitBinary(pathEnv) },
|
|
85
|
+
{ name: "repo", run: () => checkRepo(root, config) },
|
|
86
|
+
{ name: "init", run: () => checkInit(root) },
|
|
87
|
+
{ name: "config key names", run: () => checkConfigKeyNames(root) },
|
|
88
|
+
{ name: "brief", run: () => checkBrief(root) },
|
|
66
89
|
// New-project bootstrap (plans/work-ratio.md, part 2/2): an active hold reports as ok
|
|
67
90
|
// information, never a warning — a young project is not misconfigured.
|
|
68
|
-
{ name: "bootstrap",
|
|
69
|
-
{ name: "pause",
|
|
70
|
-
{ name: "quiet hours",
|
|
71
|
-
{ name: "budget",
|
|
72
|
-
{ name: "claude window",
|
|
73
|
-
{ name: "
|
|
74
|
-
{ name: "
|
|
75
|
-
{ name: "
|
|
76
|
-
{ name: "provider
|
|
77
|
-
{ name: "
|
|
78
|
-
{ name: "
|
|
79
|
-
{ name: "
|
|
80
|
-
{ name: "
|
|
81
|
-
{ name: "
|
|
82
|
-
{ name: "
|
|
83
|
-
{ name: "
|
|
84
|
-
{ name: "
|
|
85
|
-
{ name: "claude
|
|
86
|
-
{ name: "
|
|
87
|
-
{ name: "
|
|
88
|
-
{ name: "
|
|
91
|
+
{ name: "bootstrap", run: () => checkBootstrap(root, config) },
|
|
92
|
+
{ name: "pause", run: () => checkPause(root) },
|
|
93
|
+
{ name: "quiet hours", run: () => checkQuietHours(config) },
|
|
94
|
+
{ name: "budget", run: () => checkBudget(root, config, (orchestratorAlive(root, info) && info?.budget) || null) },
|
|
95
|
+
{ name: "claude window", run: () => checkSubscriptionWindow("claude", root, undefined, (orchestratorAlive(root, info) && info?.chain) || undefined, config) },
|
|
96
|
+
{ name: "codex window", run: () => checkSubscriptionWindow("codex", root, undefined, (orchestratorAlive(root, info) && info?.chain) || undefined, config) },
|
|
97
|
+
{ name: "provider waits", run: () => checkProviderHolds(root) },
|
|
98
|
+
{ name: "maintenance allowance", run: () => checkMaintenanceQuota(root, config, orchestratorAlive(root, info)) },
|
|
99
|
+
{ name: "provider concurrency", run: () => checkProviderConcurrency(config, root) },
|
|
100
|
+
{ name: "provider contention", run: () => checkProviderContention(root, config) },
|
|
101
|
+
{ name: "check contention", run: () => checkCheckContention(root, config) },
|
|
102
|
+
{ name: "repeated failures", run: () => checkErrorStreaks(root, config) },
|
|
103
|
+
{ name: "main's check", run: () => checkMainRed(root, config) },
|
|
104
|
+
{ name: "over-cap model", run: () => checkFallbackModel(root, undefined, (orchestratorAlive(root, info) && info?.fallbackDemoted) || null) },
|
|
105
|
+
{ name: "fallback chain", run: () => checkFallbackChain(root) },
|
|
106
|
+
{ name: "models for each tier", run: () => checkTierModels(root) },
|
|
107
|
+
{ name: "image input", run: () => checkImageInput(root, undefined, undefined, undefined, probeImages, pathEnv) },
|
|
108
|
+
{ name: "claude auth", run: () => checkClaudeAuth(root, pathEnv) },
|
|
109
|
+
{ name: "claude api key", run: () => checkAnthroApiKey(root, undefined, pathEnv) },
|
|
110
|
+
{ name: "claude version", run: () => checkClaudeVersion(root, pathEnv) },
|
|
111
|
+
{ name: "opencode version", run: () => checkOpencodeVersion(root, pathEnv) },
|
|
112
|
+
{ name: "opencode", run: () => checkOpencode(root, pathEnv) },
|
|
113
|
+
{ name: "codex version", run: () => checkCodexVersion(root, pathEnv) },
|
|
114
|
+
{ name: "codex login", run: () => checkCodexAuth(root, pathEnv) },
|
|
115
|
+
{ name: "codex home", run: () => checkCodexHome(root) },
|
|
116
|
+
{ name: "codex models", run: () => checkCodexModels(root) },
|
|
117
|
+
{ name: "codex account", run: () => checkCodexAccount(root) },
|
|
118
|
+
{ name: "codex leaks", run: () => checkCodexLeaks(root) },
|
|
119
|
+
{ name: "codex sessions", run: () => checkCodexSessions(root) },
|
|
120
|
+
{ name: "codex write guard", run: () => checkCodexWriteGuard(root, pathEnv) },
|
|
121
|
+
{ name: "backend binary", run: () => checkBackendBinary(root, pathEnv) },
|
|
122
|
+
{ name: "feedback filing", run: () => checkFeedbackFiling(root, pathEnv) },
|
|
89
123
|
// The transcript total is informational and only read when a seam routes to claude.
|
|
90
|
-
{ name: "claude transcripts",
|
|
124
|
+
{ name: "claude transcripts", run: () => checkClaudeTranscripts(config) },
|
|
91
125
|
// The receive-side guards name what a running team skips, so an operator tool meant for the
|
|
92
126
|
// team (a pi package, an extension file, an mcp.json server, or funes) does not vanish
|
|
93
127
|
// silently. Each check returns null to omit its row: the pi row for a team with no pi seam,
|
|
94
|
-
// the funes row when funes is not on PATH
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{ name: "
|
|
98
|
-
{ name: "
|
|
99
|
-
{ name: "
|
|
100
|
-
{ name: "
|
|
101
|
-
{ name: "
|
|
102
|
-
{ name: "
|
|
103
|
-
{ name: "
|
|
128
|
+
// the funes row when funes is not on PATH, and the claude/codex window rows when that backend
|
|
129
|
+
// has no subscription account.
|
|
130
|
+
{ name: "pi extensions", run: () => checkPiExtensions(config, undefined, pathEnv) },
|
|
131
|
+
{ name: "funes", run: () => checkFunes(config, pathEnv) },
|
|
132
|
+
{ name: "state dir", run: () => checkStateDir(root) },
|
|
133
|
+
{ name: "event log", run: () => checkEventLog(root) },
|
|
134
|
+
{ name: "notify", run: () => checkNotify(root, config) },
|
|
135
|
+
{ name: "merge lock", run: () => checkMergeLock(root) },
|
|
136
|
+
{ name: "workspaces", run: () => checkWorktreePool(root) },
|
|
137
|
+
{ name: "parallel instances", run: () => checkWorkInstances(root, config) },
|
|
138
|
+
{ name: "leftover role branches", run: () => checkLeftoverRoleBranches(root, config) },
|
|
139
|
+
{ name: "tumwater pins", run: () => checkTumwaterPins(root) },
|
|
104
140
|
// The disk floor (plans/disk-floor.md, part 1/4): a broken config stays undefined (checkInit
|
|
105
141
|
// reports it), so the check falls back to the 10 GB default rather than skipping itself.
|
|
106
|
-
{ name: "disk space",
|
|
107
|
-
{ name: "check",
|
|
108
|
-
{ name: "
|
|
142
|
+
{ name: "disk space", run: () => checkDiskSpace(root, config?.diskHoldGB ?? 10, config?.diskReclaimGB ?? 40, diskSample) },
|
|
143
|
+
{ name: "check", run: () => checkBuildCheck(root, config) },
|
|
144
|
+
{ name: "ci parity", run: () => checkCiParity(root, config, Date.now()) },
|
|
145
|
+
{ name: "qa coverage", run: () => checkQaCoverage(root, Date.now()) },
|
|
146
|
+
{ name: "root install", run: () => checkRootInstall(root) },
|
|
109
147
|
// The review gate's flake memory, surfaced read-only: naming which checks the gate kept
|
|
110
148
|
// excusing as flakes (ok/warn only, so a flaky suite never fails a scripted pre-flight).
|
|
111
|
-
{ name: "flaky checks",
|
|
112
|
-
{ name: "
|
|
113
|
-
{ name: "
|
|
114
|
-
{ name: "
|
|
115
|
-
{ name: "
|
|
116
|
-
{ name: "backlog
|
|
117
|
-
{ name: "
|
|
118
|
-
{ name: "
|
|
119
|
-
{ name: "
|
|
120
|
-
{ name: "
|
|
121
|
-
{ name: "stale
|
|
122
|
-
{ name: "
|
|
123
|
-
{ name: "
|
|
124
|
-
{ name: "
|
|
125
|
-
{ name: "stale
|
|
126
|
-
{ name: "
|
|
149
|
+
{ name: "flaky checks", run: () => checkFlakyChecks(root) },
|
|
150
|
+
{ name: "post-merge churn", run: () => checkPostMergeChurn(root) },
|
|
151
|
+
{ name: "claimed bugs", run: () => checkFixClaims(root) },
|
|
152
|
+
{ name: "validation gaps", run: () => checkValidationGaps(root) },
|
|
153
|
+
{ name: "plans stuck in Planned", run: () => checkStrandedPlans(root) },
|
|
154
|
+
{ name: "backlog headings", run: () => checkBacklogHeadings(root) },
|
|
155
|
+
{ name: "backlog archive", run: () => checkLiveFinished(root) },
|
|
156
|
+
{ name: "refusals", run: () => checkRefusedEntries(root) },
|
|
157
|
+
{ name: "stalled entries", run: () => checkStalledEntries(root) },
|
|
158
|
+
{ name: "plan dependencies", run: () => checkPlanDependencies(root) },
|
|
159
|
+
{ name: "stale questions", run: () => checkStaleQuestions(root) },
|
|
160
|
+
{ name: "stale bugs", run: () => checkStaleBugs(root) },
|
|
161
|
+
{ name: "waiting bugs", run: () => checkWaitingBugs(root) },
|
|
162
|
+
{ name: "regressed fixes", run: () => checkRegressedFixes(root) },
|
|
163
|
+
{ name: "stale merge-queue entries", run: () => checkStaleQueuedMerges(root, Date.now()) },
|
|
164
|
+
{ name: "merge-queue files", run: () => checkMergeQueueFiles(root) },
|
|
165
|
+
{ name: "post-merge reviews", run: () => checkPostReviews(root) },
|
|
166
|
+
{ name: "stale prompts", run: () => checkStalePrompts(root, Date.now()) },
|
|
167
|
+
{ name: "uncommitted backlog", run: () => checkUncommittedBacklog(root, config) },
|
|
127
168
|
// The running team's own view of its build (staleness and what auto-restart made of it)
|
|
128
169
|
// when there is one; without it the check still stands on its own git comparison.
|
|
129
|
-
{ name: "update",
|
|
130
|
-
{ name: "orphans",
|
|
131
|
-
{ name: "macOS port leak",
|
|
170
|
+
{ name: "update", run: () => checkBuild(root, undefined, undefined, (orchestratorAlive(root, info) && info?.build) || null) },
|
|
171
|
+
{ name: "orphans", run: () => checkOrphans(root, probe) },
|
|
172
|
+
{ name: "macOS port leak", run: () => checkLaunchServicesPorts(probe) },
|
|
132
173
|
];
|
|
174
|
+
const checks = [];
|
|
175
|
+
for (const step of steps) {
|
|
176
|
+
if (only && !only.includes(step.name))
|
|
177
|
+
continue;
|
|
178
|
+
const outcome = await runDoctorCheck(step.run);
|
|
179
|
+
if (outcome !== null)
|
|
180
|
+
checks.push({ name: step.name, ...outcome });
|
|
181
|
+
}
|
|
133
182
|
const problems = checks.filter((c) => c.level === "fail").length;
|
|
134
183
|
return { header, checks, routing, verdict: problems === 0 ? "ready to run" : plural(problems, "problem") };
|
|
135
184
|
}
|
|
@@ -11,14 +11,17 @@ import os from "node:os";
|
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import { randomInt } from "node:crypto";
|
|
13
13
|
import zlib from "node:zlib";
|
|
14
|
+
import { DAY_MS } from "../text/datetime.js";
|
|
14
15
|
import { PiStreamParser } from "../pi/pi-stream.js";
|
|
15
|
-
import {
|
|
16
|
+
import { resolveBackendBin } from "../pi/pi-bin.js";
|
|
16
17
|
import { imageProbesPath } from "../paths.js";
|
|
18
|
+
import { removeTree } from "../files/files.js";
|
|
17
19
|
import { readJsonFile, writeJsonAtomic } from "../files/json-files.js";
|
|
20
|
+
import { isJsonObject, validEntries } from "../files/json-object.js";
|
|
18
21
|
import { boundedProbe } from "./probe.js";
|
|
19
22
|
/** How long a cached image-probe verdict stays valid while the claim is unchanged (7 days) —
|
|
20
23
|
* a model's endpoint can change underneath a stale pass, so the answer is dated. */
|
|
21
|
-
export const IMAGE_PROBE_TTL_MS = 7 *
|
|
24
|
+
export const IMAGE_PROBE_TTL_MS = 7 * DAY_MS;
|
|
22
25
|
/** The probe's deadline (90 s): a local model may need to load, far beyond the quick credential
|
|
23
26
|
* probes, but a doctor run is still a pre-flight — a hung backend must lose, not stall the
|
|
24
27
|
* report. */
|
|
@@ -41,10 +44,20 @@ export function imageProbeEntryFresh(entry, now = Date.now()) {
|
|
|
41
44
|
entry.claim &&
|
|
42
45
|
now - entry.at <= IMAGE_PROBE_TTL_MS);
|
|
43
46
|
}
|
|
47
|
+
/** Whether a parsed cache entry has the shape of an `ImageProbeCacheEntry`. */
|
|
48
|
+
function isImageProbeCacheEntry(value) {
|
|
49
|
+
return (isJsonObject(value) &&
|
|
50
|
+
(value.verdict === "pass" || value.verdict === "fail") &&
|
|
51
|
+
typeof value.detail === "string" &&
|
|
52
|
+
typeof value.at === "number" &&
|
|
53
|
+
Number.isFinite(value.at) &&
|
|
54
|
+
typeof value.claim === "boolean");
|
|
55
|
+
}
|
|
44
56
|
/** Read the probe cache, or an empty cache when the file is missing or torn (the harness's
|
|
45
|
-
* no-data policy: a crashed writer never takes down a reader).
|
|
57
|
+
* no-data policy: a crashed writer never takes down a reader). An entry of a foreign shape is
|
|
58
|
+
* dropped, so it reads as unprobed, and the doctor's next cache write leaves it out. */
|
|
46
59
|
export function readImageProbeCache(root) {
|
|
47
|
-
return readJsonFile(imageProbesPath(root))
|
|
60
|
+
return validEntries(readJsonFile(imageProbesPath(root)), isImageProbeCacheEntry);
|
|
48
61
|
}
|
|
49
62
|
/** Write the probe cache atomically (tmp+rename) so a concurrent reader never sees a torn
|
|
50
63
|
* file. */
|
|
@@ -167,7 +180,7 @@ function isFourDigitNumber(text) {
|
|
|
167
180
|
/** Probe one claimed pi seam with a real image (plans/image-input.md "Doctor"). Runs the
|
|
168
181
|
* resolved pi binary in a run's environment (the caller passes `env`, typically
|
|
169
182
|
* `childEnv({ PATH: pathEnv })` which inherits `PI_CODING_AGENT_DIR`, so the probe reads the
|
|
170
|
-
* same models.json a real
|
|
183
|
+
* same models.json a real run would):
|
|
171
184
|
*
|
|
172
185
|
* `pi --print --mode json --no-session --no-tools --no-mcp --provider P --model M
|
|
173
186
|
* --thinking <level> @<png> "Reply with only the 4-digit number shown in the image."`
|
|
@@ -183,13 +196,13 @@ function isFourDigitNumber(text) {
|
|
|
183
196
|
* injectable so tests need no real pi. */
|
|
184
197
|
export async function runImageProbe(config, seam, env, timeoutMs = PROBE_TIMEOUT_MS, code) {
|
|
185
198
|
const envBin = env.TUMWATER_PI_BIN?.trim();
|
|
186
|
-
// Resolve from the run's own environment (env override → config), exactly as a real
|
|
199
|
+
// Resolve from the run's own environment (env override → config), exactly as a real run's
|
|
187
200
|
// spawn would rather than only the harness process env — so tests can point a fake pi there.
|
|
188
201
|
const bin = envBin
|
|
189
202
|
? envBin.includes("/")
|
|
190
203
|
? path.resolve(envBin)
|
|
191
204
|
: envBin
|
|
192
|
-
:
|
|
205
|
+
: resolveBackendBin(config).bin;
|
|
193
206
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "tumwater-image-probe-"));
|
|
194
207
|
const wrong = []; // the per-attempt "answered …" detail for a wrong answer
|
|
195
208
|
const misread = []; // whether each wrong answer was a (wrong) 4-digit number
|
|
@@ -239,6 +252,6 @@ export async function runImageProbe(config, seam, env, timeoutMs = PROBE_TIMEOUT
|
|
|
239
252
|
: { verdict: "fail", detail: `two wrong answers (${wrong[1]})` };
|
|
240
253
|
}
|
|
241
254
|
finally {
|
|
242
|
-
|
|
255
|
+
removeTree(dir);
|
|
243
256
|
}
|
|
244
257
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** The doctor report's one-line outcome contract, in its own module so every check module
|
|
2
2
|
* shares the shape and its constructors without a runtime import cycle: doctor-checks.ts
|
|
3
3
|
* value-imports claudeSeamsInUse from doctor-model-checks.ts, so that pair cannot value-import
|
|
4
|
-
* each other. doctor-checks.ts, doctor-
|
|
5
|
-
* doctor-
|
|
4
|
+
* each other. doctor-checks.ts, doctor-project-checks.ts, doctor-contention-checks.ts,
|
|
5
|
+
* doctor-model-checks.ts, doctor-orphans.ts, doctor-backlog.ts, doctor-holds.ts, and
|
|
6
|
+
* doctor-launch-services.ts all import from here. */
|
|
6
7
|
/** Constructors for the one CheckOutcome shape, so each check names its verdict
|
|
7
8
|
* (`okOutcome(detail)`) instead of repeating the two-field literal. */
|
|
8
9
|
export function okOutcome(detail) {
|
package/dist/src/doctor/probe.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { releaseChildHandles, signalTree } from "../process/process.js";
|
|
2
|
+
import { captureCappedOutput, releaseChildHandles, signalTree } from "../process/process.js";
|
|
3
|
+
/** How long a backend credential/version probe may take before it counts as unknown — a doctor
|
|
4
|
+
* run is a pre-flight, not a place to wait out a hung backend. Shared by the pi, claude, opencode
|
|
5
|
+
* and codex credential probes (doctor-pi-checks.ts, doctor-claude-checks.ts,
|
|
6
|
+
* doctor-opencode-checks.ts, doctor-codex-checks.ts). */
|
|
7
|
+
export const AUTH_CHECK_TIMEOUT_MS = 15_000;
|
|
8
|
+
/** Cap on a credential probe's captured stdout. The auth reply is a tiny JSON object; a binary
|
|
9
|
+
* whose output is a runaway log (or a pipe held open with no EOF) must not grow `out` without
|
|
10
|
+
* bound. Sibling probes share EXEC_MAX_BUFFER, but 1 MiB is already orders of magnitude past any
|
|
11
|
+
* real reply, so it is the tighter bound here. Over it the probe kills the child and reads as
|
|
12
|
+
* unknown. */
|
|
13
|
+
export const AUTH_CHECK_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
3
14
|
/** One bounded probe of a resolved binary: `null` when the spawn threw, the child errored,
|
|
4
15
|
* the deadline passed, or stdout overran `maxOutputBytes` — every case the caller must read as
|
|
5
16
|
* "could not check", never as a verdict. The child runs detached and at the deadline (or on
|
|
@@ -10,7 +21,9 @@ import { releaseChildHandles, signalTree } from "../process/process.js";
|
|
|
10
21
|
* full stderr pipe would block the child. `timeoutMs` and `maxOutputBytes` are test seams;
|
|
11
22
|
* production callers pass their own named constants. One home of the shape, shared by the
|
|
12
23
|
* model checks' credential/version probes, the tier-model probes, and the image-input probe —
|
|
13
|
-
* before the split each doctor module carried its own byte-for-byte copy.
|
|
24
|
+
* before the split each doctor module carried its own byte-for-byte copy. The credential probes'
|
|
25
|
+
* named budgets (AUTH_CHECK_TIMEOUT_MS, AUTH_CHECK_MAX_OUTPUT_BYTES) live beside it here, so the
|
|
26
|
+
* backend check modules share them without importing them from one another. */
|
|
14
27
|
export function boundedProbe(bin, args, env, timeoutMs, maxOutputBytes) {
|
|
15
28
|
return new Promise((resolve) => {
|
|
16
29
|
let child;
|
|
@@ -29,8 +42,6 @@ export function boundedProbe(bin, args, env, timeoutMs, maxOutputBytes) {
|
|
|
29
42
|
resolve(null);
|
|
30
43
|
return;
|
|
31
44
|
}
|
|
32
|
-
let out = "";
|
|
33
|
-
let bytes = 0;
|
|
34
45
|
let settled = false;
|
|
35
46
|
let timer;
|
|
36
47
|
const settle = (result) => {
|
|
@@ -46,19 +57,17 @@ export function boundedProbe(bin, args, env, timeoutMs, maxOutputBytes) {
|
|
|
46
57
|
resolve(result);
|
|
47
58
|
};
|
|
48
59
|
// Deadline or runaway output: kill the whole group, tear down the handles, and settle —
|
|
49
|
-
// do not wait for a `'close'` the group may never deliver.
|
|
60
|
+
// do not wait for a `'close'` the group may never deliver. Overrun shares the path: it
|
|
61
|
+
// passes `abort` as captureCappedOutput's onOverflow.
|
|
50
62
|
const abort = () => {
|
|
51
63
|
signalTree(child, "SIGKILL");
|
|
52
64
|
settle(null);
|
|
53
65
|
};
|
|
54
66
|
timer = setTimeout(abort, timeoutMs);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (bytes > maxOutputBytes)
|
|
59
|
-
abort();
|
|
60
|
-
});
|
|
67
|
+
// Captured stdout is capped at maxOutputBytes (reads past it abort); stderr was piped to
|
|
68
|
+
// ignore at spawn, so captureCappedOutput attaches nothing to it.
|
|
69
|
+
const captured = captureCappedOutput(child, maxOutputBytes, abort);
|
|
61
70
|
child.on("error", () => settle(null));
|
|
62
|
-
child.on("close", (code) => settle({ code, stdout:
|
|
71
|
+
child.on("close", (code) => settle({ code, stdout: captured.stdout }));
|
|
63
72
|
});
|
|
64
73
|
}
|