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,14 +1,17 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import { openQuestions } from "../backlog/backlog.js";
|
|
3
2
|
import { logEventBestEffort, warnEventBestEffort } from "../events/events.js";
|
|
4
|
-
import { changeBaseRev, commitMessage, headOf } from "../git/git.js";
|
|
3
|
+
import { changeBaseRev, commitMessage, headOf, patchId } from "../git/git.js";
|
|
5
4
|
import { aheadOfMainFiles, treeDiffFiles } from "../git/git-diff.js";
|
|
6
5
|
import { resolvedDiffDiverges } from "./merge-diff.js";
|
|
7
|
-
import { conflictedFileHunks, conflictedFiles, continueRebase, ffMainToOrWhy, hasConflictMarkers, mainCommitsTouching, rebaseOntoMain, rebaseOntoMainLeaveConflicts, } from "./merge-git.js";
|
|
6
|
+
import { conflictedFileHunks, conflictedFiles, continueRebase, ffMainToOrWhy, overwriteBlockedReason, primaryCheckoutBlockers, hasConflictMarkers, mainCommitsTouching, rebaseOntoMain, rebaseOntoMainLeaveConflicts, } from "./merge-git.js";
|
|
8
7
|
import { resolveBacklogInsertConflicts } from "./backlog-conflicts.js";
|
|
9
8
|
import { abortSync } from "../git/worktree.js";
|
|
10
9
|
import { runScopedBuildCheck } from "../build/build-check-scoped.js";
|
|
11
|
-
import { detectBuildCheck, gateCommandOf } from "../build/build-check-detect.js";
|
|
10
|
+
import { detectBuildCheck, gateCommandOf, mergeCommandOf } from "../build/build-check-detect.js";
|
|
11
|
+
import { onlyKnownMainFailures } from "../baseline/main-watch.js";
|
|
12
|
+
import { unverifiedTreeOutcome } from "../build/build-check-events.js";
|
|
13
|
+
import { describeCheck, failureHeadline } from "../build/build-check-report.js";
|
|
14
|
+
import { MERGE_FLAKY_WARNING_PREFIX, quarantineTests } from "../review/known-flakes.js";
|
|
12
15
|
import { noteGreenBaseline, noteInheritedGreen } from "../baseline/main-baseline.js";
|
|
13
16
|
import { isExemptDiff } from "../review/exemptions.js";
|
|
14
17
|
import { falseFixReason } from "../verdict/fix-claim.js";
|
|
@@ -24,7 +27,7 @@ import { resolverConfig } from "../config/config-views.js";
|
|
|
24
27
|
* history linear), verify the rebased tree when it differs from what was reviewed, and
|
|
25
28
|
* fast-forward. On conflict, makes one pi-driven resolution attempt
|
|
26
29
|
* (outside the lock) before giving up. A routine conflict is normal operation, not a warning:
|
|
27
|
-
* success merges as an ordinary `merged` event and failure surfaces via the
|
|
30
|
+
* success merges as an ordinary `merged` event and failure surfaces via the run's merge_conflict
|
|
28
31
|
* result — no separate log line for the hand-off itself. A merged diff that adds entries under
|
|
29
32
|
* QUESTIONS.md's ## Open also emits one `question_posted` per new heading alongside the `merged`
|
|
30
33
|
* event, so `tumwater logs` shows what the team is asking for (plans/questions-outbox.md).
|
|
@@ -80,6 +83,11 @@ async function tryMerge(ctx, wt, summary, verifiedHead) {
|
|
|
80
83
|
// main moved between commit and merge, which under concurrency is the common case (review
|
|
81
84
|
// runs outside this lock).
|
|
82
85
|
const target = await headOf(wt, "HEAD");
|
|
86
|
+
const dirty = await primaryCheckoutBlockers(ctx.root, ctx.mainBranch, target);
|
|
87
|
+
if (dirty !== null) {
|
|
88
|
+
warnEventBestEffort(ctx.root, ctx.role, `merge blocked: ${overwriteBlockedReason(dirty)}`);
|
|
89
|
+
return "merge_blocked";
|
|
90
|
+
}
|
|
83
91
|
if (!(await verifyMerge(ctx, wt, target, verifiedHead)))
|
|
84
92
|
return "merge_blocked";
|
|
85
93
|
const mainBefore = await headOf(ctx.root, ctx.mainBranch);
|
|
@@ -98,14 +106,28 @@ async function tryMerge(ctx, wt, summary, verifiedHead) {
|
|
|
98
106
|
// Best-effort: main has already fast-forwarded, so an unwritable events feed must not turn
|
|
99
107
|
// a completed merge into a rejected one and skip syncRootInstall below — the same
|
|
100
108
|
// no-torn-feed policy as the orchestrator's other post-effect logging.
|
|
101
|
-
if (commit !== mainBefore)
|
|
102
|
-
|
|
109
|
+
if (commit !== mainBefore) {
|
|
110
|
+
// The merged commit's patch-id (`mainBefore...commit`, src/git/git.ts patchId, -U0 so a
|
|
111
|
+
// re-landing onto a moved main keeps the id): the fast-merge guardrail G2 matches it
|
|
112
|
+
// against a main_revert's to spot a change that came back unchanged (plans/fast-merge.md).
|
|
113
|
+
// Computed here from the commit that actually merged (not the pre-merge pin) so a
|
|
114
|
+
// conflict-resolved merge carries the resolved change's id too. Computed after the ff
|
|
115
|
+
// because mainBefore/commit both live in the shared object store either way.
|
|
116
|
+
const mergedPatch = await patchId(wt, mainBefore, commit).catch(() => null);
|
|
117
|
+
logEventBestEffort(ctx.root, {
|
|
118
|
+
source: ctx.role,
|
|
119
|
+
type: "merged",
|
|
120
|
+
commit,
|
|
121
|
+
summary,
|
|
122
|
+
...(mergedPatch ? { patchId: mergedPatch } : {}),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
103
125
|
logNewQuestions(ctx.root, before, ctx.role);
|
|
104
126
|
// Still under the lock: a merge that moved main's lockfile re-syncs the root install
|
|
105
127
|
// every worktree resolves through before the next merge's check runs (BUGS.md 2026-10-01).
|
|
106
128
|
await syncRootInstall(ctx.root, ctx.role);
|
|
107
129
|
return "changed";
|
|
108
|
-
});
|
|
130
|
+
}, ctx.lockTimeoutMs);
|
|
109
131
|
}
|
|
110
132
|
/** The backlog-structure cross-check both verifyMerge paths run (exempt and code diffs
|
|
111
133
|
* alike): when the tree ahead of main duplicates or drops a `## ` section heading, warn on the
|
|
@@ -146,12 +168,16 @@ export async function exemptSkipBlockReason(ctx, wt, files) {
|
|
|
146
168
|
* - a no-op rebase whose exact tree the gate verified (`rebasedHead === verifiedHead`): main
|
|
147
169
|
* did not move under us, so the merge tree is byte-identical to what the gate's pre-check
|
|
148
170
|
* already ran green. Seed the red-main baseline with the SHA that becomes main: every role's
|
|
149
|
-
* next fresh
|
|
171
|
+
* next fresh run then hits the cache instead of re-running the full suite on an
|
|
150
172
|
* already-verified tree. A no-op rebase the gate did NOT verify — its pre-check was skipped
|
|
151
173
|
* (a timeout, a killed run) — falls through and runs the merge-scope check, which fails
|
|
152
174
|
* closed on the same timeout; an exempt diff or a project with no declared check falls
|
|
153
|
-
* through to its own arm below.
|
|
154
|
-
*
|
|
175
|
+
* through to its own arm below. Taken (Fast merge "Defaults") whenever a `check.mergeCommand`
|
|
176
|
+
* resolves — to its own value or, when absent, to `check.gateCommand`. In the fallback case
|
|
177
|
+
* (no separate `mergeCommand`) the in-lock command IS the gate command the gate already ran
|
|
178
|
+
* green on the rebased head, so the in-lock run would add nothing; with a separate
|
|
179
|
+
* `check.mergeCommand` the gate's green was the gate command, not the merge one. Either way
|
|
180
|
+
* it skips and seeds nothing (the main watch decides main's verdict).
|
|
155
181
|
* - doc-only delta ahead of main (the gate's own exemption test): cannot break the build.
|
|
156
182
|
* - a code change whose rebased tree differs from the gate-verified one only in exempt paths
|
|
157
183
|
* (onlyExemptDriftFromVerified): main moved under the review by doc commits alone, so the
|
|
@@ -168,10 +194,15 @@ async function verifyMerge(ctx, wt, rebasedHead, verifiedHead) {
|
|
|
168
194
|
// re-review sets verifiedHead to the resolved head, and that second rebase is then a no-op).
|
|
169
195
|
// A no-op rebase the gate never verified — its pre-check timed out or was killed and left
|
|
170
196
|
// GateResult.verifiedHead undefined — falls through and runs the merge-scope check, which
|
|
171
|
-
// fails closed on the same timeout. With a check.gateCommand
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
197
|
+
// fails closed on the same timeout. With a check.gateCommand or check.mergeCommand
|
|
198
|
+
// configured (Fast merge "Defaults": the lock runs the gate command when mergeCommand is
|
|
199
|
+
// absent): in the fallback case (no separate `mergeCommand`) the in-lock command IS the gate
|
|
200
|
+
// command the gate already ran green on the rebased head, so the verified no-op rebase adds
|
|
201
|
+
// nothing — skip it; with a separate `mergeCommand` the gate's green was the gate command.
|
|
202
|
+
// Either way it seeds nothing — a cheaper command's green never seeds the baseline: main's
|
|
203
|
+
// verdicts come from the main watch's full check on the tip.
|
|
204
|
+
if (rebasedHead === verifiedHead && mergeCommandOf(ctx.config) !== undefined)
|
|
205
|
+
return true;
|
|
175
206
|
if (rebasedHead === verifiedHead && gateCommandOf(ctx.config) === undefined) {
|
|
176
207
|
noteGreenBaseline(rebasedHead);
|
|
177
208
|
return true;
|
|
@@ -204,19 +235,40 @@ async function verifyMerge(ctx, wt, rebasedHead, verifiedHead) {
|
|
|
204
235
|
// runScopedBuildCheck, shared with the review gate's pre-check. The merge cell names the
|
|
205
236
|
// check (or its wait for a permit) while it runs, then the merge again for the ff.
|
|
206
237
|
setMergeStage(ctx.root, ctx.role, "build-check");
|
|
207
|
-
const
|
|
238
|
+
const runMergeCheck = () => runScopedBuildCheck(ctx.root, ctx.role, "merge", wt, ctx.config, undefined, undefined, undefined, checkWaitStage(ctx.root, [ctx.role]));
|
|
239
|
+
let check = await runMergeCheck();
|
|
240
|
+
if (check?.outcome.status === "failed" && !unverifiedTreeOutcome(check.outcome) && !onlyKnownMainFailures(ctx.root, check.outcome.failingTests)) {
|
|
241
|
+
// One immediate re-run of the same tree: a pass makes the failed run a flake. The change
|
|
242
|
+
// merges and each test the failed run named is quarantined.
|
|
243
|
+
const failed = check.outcome;
|
|
244
|
+
const retry = await runMergeCheck();
|
|
245
|
+
if (retry?.outcome.status === "passed") {
|
|
246
|
+
warnEventBestEffort(ctx.root, ctx.role, `${MERGE_FLAKY_WARNING_PREFIX}${failureHeadline(failed.outputTail) ?? describeCheck(check.check)}`);
|
|
247
|
+
quarantineTests(ctx.root, failed.failingTests, failed.outputTail);
|
|
248
|
+
check = retry;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
208
251
|
setMergeStage(ctx.root, ctx.role, "merging");
|
|
209
252
|
if (!check)
|
|
210
253
|
return true; // No declared check: nothing to run, exactly like the gate skipping its pre-check.
|
|
211
254
|
if (check.outcome.status === "failed") {
|
|
255
|
+
// Failing only what main already fails is not a red change: the fast-forward goes ahead, with
|
|
256
|
+
// no strike and no green seeded for a tree that did not pass.
|
|
257
|
+
const known = unverifiedTreeOutcome(check.outcome) ? null : onlyKnownMainFailures(ctx.root, check.outcome.failingTests);
|
|
258
|
+
if (known) {
|
|
259
|
+
warnEventBestEffort(ctx.root, ctx.role, `merge check fails only main's known failures: ${known.join(", ")}`);
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
212
262
|
ctx.onMergeCheckRed?.(check.check, check.outcome);
|
|
213
263
|
return false;
|
|
214
264
|
}
|
|
215
265
|
if (check.outcome.status === "skipped")
|
|
216
266
|
return true; // No npm / broken toolchain — the helper already warned.
|
|
217
|
-
// Green on exactly the tree that becomes main: seed it so the next
|
|
267
|
+
// Green on exactly the tree that becomes main: seed it so the next run's red-main baseline
|
|
218
268
|
// check is a cache hit instead of one redundant full-suite run.
|
|
219
|
-
|
|
269
|
+
// A mergeCommand green is not the full check's: main's verdict comes from the main watch.
|
|
270
|
+
if (mergeCommandOf(ctx.config) === undefined)
|
|
271
|
+
noteGreenBaseline(rebasedHead);
|
|
220
272
|
return true;
|
|
221
273
|
}
|
|
222
274
|
/** True, after seeding the red-main baseline, when the tree about to merge differs from the tree
|
|
@@ -229,14 +281,20 @@ async function verifyMerge(ctx, wt, rebasedHead, verifiedHead) {
|
|
|
229
281
|
* that only rewrote SHAs) is the verified bytes themselves and seeds an observed green. Any
|
|
230
282
|
* other drift seeds an inherited one, which attribution re-checks before trusting
|
|
231
283
|
* (main-baseline.ts's observedOnly). Not taken without a verified head (the pre-check was skipped
|
|
232
|
-
* or never ran)
|
|
284
|
+
* or never ran). With an in-lock command configured (a gate or merge command — Fast merge
|
|
285
|
+
* "Defaults": the lock runs the gate command when mergeCommand is absent) the drift merges
|
|
286
|
+
* without a seed: the main watch decides main's verdict.
|
|
233
287
|
* A git failure means "not proven" and the check runs. */
|
|
234
288
|
async function onlyExemptDriftFromVerified(ctx, wt, rebasedHead, verifiedHead) {
|
|
235
|
-
if (verifiedHead === undefined
|
|
289
|
+
if (verifiedHead === undefined)
|
|
290
|
+
return false;
|
|
291
|
+
if (gateCommandOf(ctx.config) !== undefined && mergeCommandOf(ctx.config) === undefined)
|
|
236
292
|
return false;
|
|
237
293
|
const drift = await treeDiffFiles(wt, verifiedHead, rebasedHead);
|
|
238
294
|
if (drift === null || !isExemptDiff(drift, ctx.exemptPaths))
|
|
239
295
|
return false;
|
|
296
|
+
if (mergeCommandOf(ctx.config) !== undefined)
|
|
297
|
+
return true; // no seed: the watch decides main's verdict
|
|
240
298
|
if (drift.length === 0)
|
|
241
299
|
noteGreenBaseline(rebasedHead);
|
|
242
300
|
else
|
|
@@ -269,12 +327,13 @@ async function resolveConflict(ctx, wt, preMergeHead) {
|
|
|
269
327
|
const files = await conflictedFiles(wt);
|
|
270
328
|
// Deterministic first pass (plans/parallel-work-instances.md, part 3/7): insert-only
|
|
271
329
|
// conflicts in the backlog markdown — two merges pasting different entries as the first
|
|
272
|
-
// under ## Done
|
|
273
|
-
//
|
|
330
|
+
// under ## Done, or new entries above an entry the other side deleted — carry no authored
|
|
331
|
+
// bytes, so they are settled without a model run. Only the files it could not resolve (code,
|
|
332
|
+
// or a same-entry edit) reach the resolver below.
|
|
274
333
|
const remaining = await resolveBacklogInsertConflicts(wt, files);
|
|
275
334
|
if (remaining.length === 0) {
|
|
276
335
|
// A second stop is only possible when the branch holds more than the one insert-only
|
|
277
|
-
// commit. One attempt per
|
|
336
|
+
// commit. One attempt per run.
|
|
278
337
|
return continueOrAbortRebase(wt);
|
|
279
338
|
}
|
|
280
339
|
// The prompt names the project's own check, detected the way the in-lock re-check detects it,
|
|
@@ -299,10 +358,10 @@ async function resolveConflict(ctx, wt, preMergeHead) {
|
|
|
299
358
|
change,
|
|
300
359
|
main: commits,
|
|
301
360
|
mainOmitted: omitted,
|
|
302
|
-
}), `tumwater-${ctx.role}-${ctx.
|
|
361
|
+
}), `tumwater-${ctx.role}-${ctx.runNumber}-conflict`,
|
|
303
362
|
// The resolver rides the strong tier (plans/model-tiers.md part 4/8): resolution is rare,
|
|
304
363
|
// tolerant of latency, and edits code inside merge. Its spend still folds into the
|
|
305
|
-
// authoring role's usage — the fold belongs to the
|
|
364
|
+
// authoring role's usage — the fold belongs to the runner's wiring, not to the config.
|
|
306
365
|
resolverConfig(ctx.config));
|
|
307
366
|
}
|
|
308
367
|
finally {
|
|
@@ -312,7 +371,7 @@ async function resolveConflict(ctx, wt, preMergeHead) {
|
|
|
312
371
|
await abortSync(wt);
|
|
313
372
|
return false;
|
|
314
373
|
}
|
|
315
|
-
// A second stop is only possible when pi itself authored extra commits during the
|
|
316
|
-
// One resolution attempt per
|
|
374
|
+
// A second stop is only possible when pi itself authored extra commits during the run.
|
|
375
|
+
// One resolution attempt per run.
|
|
317
376
|
return continueOrAbortRebase(wt);
|
|
318
377
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** Operator reordering of a waiting merge (PLANS.md `tumwater queue --top <n>`): moving the
|
|
2
|
+
* Nth queued merge to the head of the execution order. Split out of merge-queue.ts so the
|
|
3
|
+
* queue stays the pure reader/writer it is: this module owns the reorder half of an
|
|
4
|
+
* unattempted entry, renaming its queue file to a stamp one below the queue's minimum — the
|
|
5
|
+
* merge-queue twin of inbox-top.ts (which does the same for the prompt queue). It mirrors
|
|
6
|
+
* inbox-top.ts's rename-and-event pairing and merge-cancel.ts's stance: no lock, one file per
|
|
7
|
+
* entry, tolerant of a concurrent drop. */
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import { logEventBestEffort } from "../events/events.js";
|
|
11
|
+
import { errCode } from "../errno.js";
|
|
12
|
+
import { queueFileName, queueFileStamp } from "../files/file-queue.js";
|
|
13
|
+
import { queuedMergeFiles } from "./merge-queue.js";
|
|
14
|
+
/** Move the Nth queued merge — 1-based, as `tumwater queue` numbers it and `--cancel`
|
|
15
|
+
* resolves it — to the head of the execution order, so it is the next due entry the queue
|
|
16
|
+
* takes when ready. Position 1 reports `already-first` and changes nothing; an out-of-range
|
|
17
|
+
* position (below 1 or past the readable depth) reports `out-of-range` with the current
|
|
18
|
+
* depth and changes nothing. The move is one rename: the entry JSON is never rewritten, so
|
|
19
|
+
* every field (`enqueuedAt`, `notBefore`, `retry`, `revisionRound`, `priorReview`) is
|
|
20
|
+
* unchanged; giving the file a stamp one below the queue's readable minimum sorts it first
|
|
21
|
+
* (falling back to `Date.now()` when no stamp parses), stepping one lower while the
|
|
22
|
+
* destination exists (a concurrent writer may have just taken the name we computed). One
|
|
23
|
+
* `merge_topped` event is logged under the entry's role. The rename is guarded: if the
|
|
24
|
+
* target vanished between the list and the rename (a concurrent drain took the change), the
|
|
25
|
+
* move answers `gone` and changes nothing rather than throwing. No lock and no pin write:
|
|
26
|
+
* the module mirrors merge-cancel.ts's "one file per entry, tolerant of a concurrent drop"
|
|
27
|
+
* stance. */
|
|
28
|
+
export function moveQueuedMergeToTop(root, position) {
|
|
29
|
+
const files = queuedMergeFiles(root);
|
|
30
|
+
if (position < 1 || position > files.length)
|
|
31
|
+
return { status: "out-of-range", depth: files.length };
|
|
32
|
+
const target = files[position - 1];
|
|
33
|
+
if (position === 1) {
|
|
34
|
+
return {
|
|
35
|
+
status: "already-first",
|
|
36
|
+
role: target.entry.role,
|
|
37
|
+
sha: target.entry.sha,
|
|
38
|
+
summary: target.entry.summary,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const stamps = files
|
|
42
|
+
.map((q) => queueFileStamp(path.basename(q.file)))
|
|
43
|
+
.filter((s) => s !== null);
|
|
44
|
+
let stamp = (stamps.length > 0 ? Math.min(...stamps) : Date.now()) - 1;
|
|
45
|
+
const dir = path.dirname(target.file);
|
|
46
|
+
let newFile = path.join(dir, queueFileName(stamp, 0, ".json"));
|
|
47
|
+
while (fs.existsSync(newFile)) {
|
|
48
|
+
stamp--;
|
|
49
|
+
newFile = path.join(dir, queueFileName(stamp, 0, ".json"));
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
fs.renameSync(target.file, newFile);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// The queue drained the target between our list and this rename (an orchestrator took
|
|
56
|
+
// the change): a concurrent drop is a normal race, never an error — the entry has
|
|
57
|
+
// already left the queue, so there is nothing to top. Any other rename failure is real.
|
|
58
|
+
if (errCode(err) === "ENOENT")
|
|
59
|
+
return { status: "gone" };
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
logEventBestEffort(root, {
|
|
63
|
+
source: target.entry.role,
|
|
64
|
+
type: "merge_topped",
|
|
65
|
+
commit: target.entry.sha,
|
|
66
|
+
summary: target.entry.summary,
|
|
67
|
+
});
|
|
68
|
+
return { status: "topped", role: target.entry.role, sha: target.entry.sha, summary: target.entry.summary };
|
|
69
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { gateRoleConfig } from "../gates/budget-gates.js";
|
|
5
4
|
import { reviewRunConfig, seamConcurrencyKey } from "../config/config-views.js";
|
|
6
5
|
import { providerWaitStage } from "./merge-slot.js";
|
|
7
6
|
import { withProviderLease } from "../concurrency/provider-lease.js";
|
|
8
|
-
import {
|
|
7
|
+
import { RoleRunner } from "../runner/runner.js";
|
|
9
8
|
import { branchHead, isMergedInto } from "../git/git.js";
|
|
10
9
|
import { addMergeChange, mergeUsage, removeMergeChange, setMergeChangeStatus } from "./merge-slot.js";
|
|
11
10
|
import { dropMerge, entryRevisionFields, queuedMergeFiles, staleHeadFile } from "./merge-queue.js";
|
|
@@ -28,21 +27,21 @@ export function verifyLimit(maxConcurrent) {
|
|
|
28
27
|
/** Resolve a merge entry's authoring runner: the live runner when the role is enabled
|
|
29
28
|
* (runners are never removed from the array on disable — only a warning event fires); a role
|
|
30
29
|
* disabled before this process started has no runner, so a throwaway one supplies the same
|
|
31
|
-
* wiring (
|
|
30
|
+
* wiring (runner-pi.ts, runMergePi, foldMergeUsage) and a disk-loaded state to fold and save
|
|
32
31
|
* on. Both share the live config, like every runner — the director takes directorConfig (the live
|
|
33
32
|
* config, its budget-gate exemption, unless a used-up window moved it), every other role takes
|
|
34
33
|
* roleConfig (gateRoleConfig's single home for the rule; the scheduler assigns the live runners
|
|
35
34
|
* by it every poll). */
|
|
36
|
-
function resolveAuthor(ctx, role) {
|
|
35
|
+
export function resolveAuthor(ctx, role) {
|
|
37
36
|
const { root, mainBranch, signal, runners, liveConfig, roleConfig, directorConfig } = ctx;
|
|
38
37
|
return (runners.find((r) => r.role === role) ??
|
|
39
|
-
new
|
|
38
|
+
new RoleRunner(root, role, gateRoleConfig(role, liveConfig, roleConfig, directorConfig), mainBranch, signal));
|
|
40
39
|
}
|
|
41
40
|
/** Drop a torn queue head. A torn head (torn or foreign — a partial write an older build left,
|
|
42
41
|
* a hand-placed file) makes headMerge read null forever — nothing else drops it, stranding
|
|
43
|
-
* every live entry behind it and pinning their authors'
|
|
42
|
+
* every live entry behind it and pinning their authors' runs via the interlock. Drop it with
|
|
44
43
|
* one warning; the healthy entries behind it are verified in the same poll. The crashed
|
|
45
|
-
* entry's commit, if any, still rides its merge ref into next-
|
|
44
|
+
* entry's commit, if any, still rides its merge ref into next-run leftover recovery
|
|
46
45
|
* (BUGS.md 2026-09-17). */
|
|
47
46
|
function dropTornHead(root) {
|
|
48
47
|
const stale = staleHeadFile(root);
|
|
@@ -98,6 +97,9 @@ export async function drainVerify(ctx, p) {
|
|
|
98
97
|
return;
|
|
99
98
|
if (liveRoles(p).has(entry.role))
|
|
100
99
|
continue;
|
|
100
|
+
// A re-queued review_error waits out its delay; the entry stays queued and holds its author.
|
|
101
|
+
if (entry.notBefore !== undefined && Date.now() < entry.notBefore)
|
|
102
|
+
continue;
|
|
101
103
|
if (await mergedIntoMain(ctx.root, entry.sha, ctx.mainBranch, mainHead)) {
|
|
102
104
|
dropMerge(file);
|
|
103
105
|
removeMergeChange(ctx.root, entry.role);
|
|
@@ -163,10 +165,10 @@ export function startVerify(ctx, p, entry, file) {
|
|
|
163
165
|
// so a review run that lands on a different provider than the verification's review seam
|
|
164
166
|
// re-keys it there, and a re-key park is shown by providerWaitStage; its slot wait drops
|
|
165
167
|
// the key (pool-slot rule).
|
|
166
|
-
verdict = await withProviderLease(lease, semaphore, () => verifyRequest({ root, mainBranch, config: author.config, signal: () => verify.controller.signal, runGatePi: (opts) => author.runGatePi(opts) }, {
|
|
168
|
+
verdict = await withProviderLease(lease, semaphore, () => verifyRequest({ root, mainBranch, config: author.config, signal: () => verify.controller.signal, runGatePi: (opts) => author.runGatePi(opts), reviewHeld: () => ctx.startHeld() }, {
|
|
167
169
|
role,
|
|
168
170
|
sha: entry.sha,
|
|
169
|
-
|
|
171
|
+
runNumber: entry.run,
|
|
170
172
|
summary: entry.summary,
|
|
171
173
|
body: entry.body,
|
|
172
174
|
highFriction: entry.highFriction,
|
|
@@ -198,7 +200,7 @@ export function startVerify(ctx, p, entry, file) {
|
|
|
198
200
|
}
|
|
199
201
|
else {
|
|
200
202
|
const result = verdict.kind === "stack" ? "aborted" : verdict.result;
|
|
201
|
-
await settleMergeOutcome(root, entry, author.state, result, Date.now() - startedAt, usage, file);
|
|
203
|
+
await settleMergeOutcome(root, entry, author.state, result, Date.now() - startedAt, usage, file, mainBranch);
|
|
202
204
|
if (verify.userAborted)
|
|
203
205
|
await discardPinnedRefs(root, [role]);
|
|
204
206
|
}
|
package/dist/src/notes-cli.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// vocabulary: plain
|
|
3
2
|
/** The `tumwater-notes` command — the second bin entry (dist/src/notes-cli.js), the one
|
|
4
3
|
* carrier for writing a role's note on every backend (plans/notes-command.md "The
|
|
5
4
|
* `tumwater-notes` command"). A run finds it on its PATH (src/agent/notes-shim.ts) and hands
|
|
@@ -14,9 +13,12 @@
|
|
|
14
13
|
* show print the note (nothing when there is none)
|
|
15
14
|
* clear empty the note
|
|
16
15
|
*
|
|
17
|
-
* Exits 0 on success, 1 on a refusal (empty input, over-cap note,
|
|
18
|
-
* TUMWATER_NOTES_PATH), and 2 with usage for
|
|
16
|
+
* Exits 0 on success, 1 on a refusal (empty input, over-cap note, a note file that cannot be
|
|
17
|
+
* written or read, or no TUMWATER_NOTES_PATH), and 2 with usage for an unknown subcommand (a
|
|
18
|
+
* known subcommand with bad arguments refuses with usage and exit 1). */
|
|
19
19
|
import fs from "node:fs";
|
|
20
|
+
import { readTextIfExists } from "./files/files.js";
|
|
21
|
+
import { errorMessage } from "./text/text.js";
|
|
20
22
|
import { validateRoleNote, writeRoleNote } from "./agent/role-notes-write.js";
|
|
21
23
|
/** The note's path, read ONLY from the environment — never from an argument. */
|
|
22
24
|
const NOTES_PATH = process.env.TUMWATER_NOTES_PATH;
|
|
@@ -29,14 +31,10 @@ const USAGE = [
|
|
|
29
31
|
function readStdin() {
|
|
30
32
|
return fs.readFileSync(0, "utf8");
|
|
31
33
|
}
|
|
32
|
-
/** Read the note's current text; a missing note reads as empty (show prints nothing).
|
|
34
|
+
/** Read the note's current text; a missing note reads as empty (show prints nothing). Any
|
|
35
|
+
* other read failure (EACCES, EISDIR) throws, so it is never mistaken for an empty note. */
|
|
33
36
|
function readNote() {
|
|
34
|
-
|
|
35
|
-
return fs.readFileSync(NOTES_PATH, "utf8");
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return "";
|
|
39
|
-
}
|
|
37
|
+
return readTextIfExists(NOTES_PATH) ?? "";
|
|
40
38
|
}
|
|
41
39
|
/** Refuse with `message` on stderr and exit 1 — the exit code for every expected refusal. */
|
|
42
40
|
function refuse(message) {
|
|
@@ -51,7 +49,12 @@ function writeNote(text) {
|
|
|
51
49
|
const error = validateRoleNote(text);
|
|
52
50
|
if (error)
|
|
53
51
|
return refuse(error);
|
|
54
|
-
|
|
52
|
+
try {
|
|
53
|
+
writeRoleNote(NOTES_PATH, text);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return refuse(`could not save the note: ${errorMessage(err)}`);
|
|
57
|
+
}
|
|
55
58
|
process.stdout.write(`notes saved (${Buffer.byteLength(text, "utf8")} bytes)\n`);
|
|
56
59
|
return 0;
|
|
57
60
|
}
|
|
@@ -72,8 +75,8 @@ function main() {
|
|
|
72
75
|
try {
|
|
73
76
|
text = fs.readFileSync(flagArg, "utf8");
|
|
74
77
|
}
|
|
75
|
-
catch {
|
|
76
|
-
return refuse(`cannot read ${flagArg}`);
|
|
78
|
+
catch (err) {
|
|
79
|
+
return refuse(`cannot read ${flagArg}: ${errorMessage(err)}`);
|
|
77
80
|
}
|
|
78
81
|
return writeNote(text);
|
|
79
82
|
}
|
|
@@ -81,12 +84,24 @@ function main() {
|
|
|
81
84
|
case "show":
|
|
82
85
|
if (flag !== undefined)
|
|
83
86
|
return refuse(USAGE);
|
|
84
|
-
|
|
87
|
+
let note;
|
|
88
|
+
try {
|
|
89
|
+
note = readNote();
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return refuse(`could not read the note: ${errorMessage(err)}`);
|
|
93
|
+
}
|
|
94
|
+
process.stdout.write(note);
|
|
85
95
|
return 0;
|
|
86
96
|
case "clear":
|
|
87
97
|
if (flag !== undefined)
|
|
88
98
|
return refuse(USAGE);
|
|
89
|
-
|
|
99
|
+
try {
|
|
100
|
+
writeRoleNote(NOTES_PATH, "");
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
return refuse(`could not clear the note: ${errorMessage(err)}`);
|
|
104
|
+
}
|
|
90
105
|
process.stdout.write("notes cleared\n");
|
|
91
106
|
return 0;
|
|
92
107
|
default:
|