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,30 +1,34 @@
|
|
|
1
1
|
import { allRoleIds, customRole, DIRECTOR_ROLE, roleById, unknownRoleMessage } from "../roles/roles.js";
|
|
2
|
-
import { baseRoleOf } from "../roles/
|
|
2
|
+
import { baseRoleOf } from "../roles/runner-ids.js";
|
|
3
3
|
import { planBacklogTarget } from "../scheduling/claims.js";
|
|
4
4
|
import { configForRole } from "../config/config-views.js";
|
|
5
5
|
import { NOTHING_TO_DO } from "../verdict/reply-contract.js";
|
|
6
6
|
import { dequeuePromptEntry, dequeueRolePromptEntry, peekPrompt, peekRolePrompt } from "../inbox/inbox.js";
|
|
7
7
|
import { stripNotBeforeMarker } from "../inbox/prompt-not-before.js";
|
|
8
|
-
import {
|
|
9
|
-
import { buildDirectorPrompt,
|
|
8
|
+
import { briefFile } from "../brief/brief.js";
|
|
9
|
+
import { buildDirectorPrompt, buildRunPrompt } from "../prompt/prompt.js";
|
|
10
10
|
import { readPrinciples } from "../prompt/principles.js";
|
|
11
11
|
import { buildCutOffNote } from "../prompt/prompt-followup.js";
|
|
12
|
-
import { buildAssignmentNote, buildConflictDiscardNote, buildHeldEntriesNote, buildMergedPrerequisitesNote, buildRejectedReviewNote, buildWaitingPartsNote } from "../prompt/gate-
|
|
12
|
+
import { buildAssignmentNote, buildConflictDiscardNote, buildHeldEntriesNote, buildMergedPrerequisitesNote, buildRejectedReviewNote, buildWaitingPartsNote } from "../prompt/gate-notes.js";
|
|
13
13
|
import { eligibleEntries, mergedPrerequisiteRanges, waitingPartRanges } from "../backlog/backlog-eligibility.js";
|
|
14
|
-
import {
|
|
14
|
+
import { notesBacklogBlock } from "../prompt/notes-backlog.js";
|
|
15
|
+
import { donePlans, openBugEntries, plannedPlanEntries } from "../backlog/backlog.js";
|
|
16
|
+
import { detectBuildCheck, gateCommandOf } from "../build/build-check-detect.js";
|
|
15
17
|
import { flakeCounts, KNOWN_FLAKE_WINDOW_MS } from "../review/known-flakes.js";
|
|
16
18
|
import { readEventsSince } from "../events/event-window.js";
|
|
17
|
-
import {
|
|
19
|
+
import { selfcheckDigest } from "./selfcheck-digest.js";
|
|
18
20
|
import { readQaCoverage, renderCoverageBlock } from "./qa-coverage.js";
|
|
21
|
+
import { readFeedbackFilings, renderFilingsBlock } from "./feedback-filings.js";
|
|
19
22
|
import { roleNotesPath } from "../paths.js";
|
|
20
23
|
import * as path from "node:path";
|
|
21
|
-
import { pathTokens } from "
|
|
22
|
-
import { readTextOrNull, statOrNull } from "../files/files.js";
|
|
24
|
+
import { pathTokens } from "../text/text.js";
|
|
25
|
+
import { isDirectory, readTextOrNull, statOrNull } from "../files/files.js";
|
|
23
26
|
import { dayKey } from "../text/datetime.js";
|
|
24
|
-
import { appendPromptNote } from "../text/format.js";
|
|
27
|
+
import { appendPromptNote, prependPromptNote } from "../text/format.js";
|
|
25
28
|
import { ROLE_NOTES_MAX_BYTES } from "../agent/role-notes-write.js";
|
|
26
29
|
import { truncateWithNote } from "../text/text.js";
|
|
27
|
-
import {
|
|
30
|
+
import { renderBacklogStructureBlock } from "../backlog/backlog-structure.js";
|
|
31
|
+
import { renderBacklogIndexBlock } from "../backlog/backlog-index.js";
|
|
28
32
|
/** The repo paths the entry at `file` lines `range` names that are absent from the checkout at
|
|
29
33
|
* `root`: tokens whose first segment is a directory there but which do not exist themselves —
|
|
30
34
|
* the self-check's top-level-directory rule, read from the working tree. */
|
|
@@ -35,19 +39,19 @@ function missingEntryPaths(root, file, range) {
|
|
|
35
39
|
const entry = text.split("\n").slice(range.start - 1, range.end).join("\n");
|
|
36
40
|
return pathTokens(entry).filter((p) => {
|
|
37
41
|
const top = p.slice(0, p.indexOf("/"));
|
|
38
|
-
return
|
|
42
|
+
return isDirectory(path.join(root, top)) && statOrNull(path.join(root, p)) === null;
|
|
39
43
|
});
|
|
40
44
|
}
|
|
41
|
-
/** Read a role's
|
|
45
|
+
/** Read a role's notes through files.ts's readTextOrNull — the one read-and-swallow
|
|
42
46
|
* step — or undefined when it is missing or unreadable (an empty or whitespace-only note is
|
|
43
|
-
* left for
|
|
44
|
-
* runtime-state file must never fail a
|
|
47
|
+
* left for buildRunPrompt to omit). The degradation is deliberate: a torn or absent
|
|
48
|
+
* runtime-state file must never fail a run.
|
|
45
49
|
*
|
|
46
50
|
* The note is bounded on read as well as on write: the write path rejects text over
|
|
47
51
|
* ROLE_NOTES_MAX_BYTES, but the file lives in hand-editable runtime state, so an oversized
|
|
48
52
|
* note (an old build, a manual edit) would otherwise ride into every one of this role's
|
|
49
|
-
* prefill-heavy
|
|
50
|
-
*
|
|
53
|
+
* prefill-heavy run prompts uncapped. The same defensive backstop readPrinciples and the
|
|
54
|
+
* brief text reader apply to their hand-editable files. A UTF-8 character is never
|
|
51
55
|
* fewer than one byte, so a note the tool accepted (≤ 4096 bytes) is always ≤ 4096 chars
|
|
52
56
|
* and never trips this bound; only an over-cap file is cut, with truncateWithNote's visible
|
|
53
57
|
* marker naming the loss. */
|
|
@@ -78,42 +82,47 @@ function flakyChecksNote(root) {
|
|
|
78
82
|
`names one of these, that bug is still firing. When \`## Open\` holds no entries, a check ` +
|
|
79
83
|
`excused 2 or more times is a confirmed latent bug: take it as this run's latent-bug candidate.`);
|
|
80
84
|
}
|
|
81
|
-
/** Assemble the prompt a
|
|
82
|
-
* concern, split out of
|
|
83
|
-
* content. Returns null when the
|
|
85
|
+
/** Assemble the prompt a role's next run runs on — the whole "what should this run see"
|
|
86
|
+
* concern, split out of runner.ts so the runner owns only lifecycle and the prompt owns only
|
|
87
|
+
* content. Returns null when the role has nothing to run this run (a director with an empty
|
|
84
88
|
* inbox); `userPrompt` carries the dequeued user request — the director's, or a per-role one
|
|
85
89
|
* queued by `tumwater prompt --role <id>` — back so the runner can record it as pending and
|
|
86
|
-
* re-queue it if the
|
|
90
|
+
* re-queue it if the run leaves it unfulfilled; `userPromptFile` is the queue file it came
|
|
87
91
|
* from, so that bookkeeping can clean the prompt's attachments by that file's stem. */
|
|
88
|
-
export function
|
|
89
|
-
// One read of the brief yields both the prompt it carries and the owning file's name
|
|
90
|
-
// (brief.ts's readBrief): readInitialPrompt + briefFile were two full reads of the same
|
|
91
|
-
// file on this per-tick path.
|
|
92
|
-
const briefState = readBrief(root);
|
|
93
|
-
const initialPrompt = briefState?.prompt ?? "";
|
|
92
|
+
export function assembleRunPrompt({ root, config, role, state, preview = false, backend }) {
|
|
94
93
|
// The brief's owning file (TUMWATER.md first, README.md as the compatibility path —
|
|
95
94
|
// plans/portability.md §7a/7), named in both prompt builders' rules instead of a hardcoded
|
|
96
95
|
// README.md. "README.md" is the fallback for a repo with no marked file yet — the team
|
|
97
96
|
// runs blind on prompts either way, so the name in the rules should still point somewhere.
|
|
98
|
-
const brief =
|
|
99
|
-
// The project's design principles ride along in every prompt —
|
|
100
|
-
// all
|
|
97
|
+
const brief = briefFile(root) ?? "README.md";
|
|
98
|
+
// The project's design principles ride along in every prompt — run and director alike — so
|
|
99
|
+
// all roles share one standard of taste. Empty when the repo has no PRINCIPLES.md.
|
|
101
100
|
const principles = readPrinciples(root);
|
|
102
101
|
// The project's resolved check (plans/portability.md §6/7): the prompt names the actual
|
|
103
102
|
// verify command instead of asserting npm. Detection is a handful of stat calls — a
|
|
104
|
-
// per-
|
|
103
|
+
// per-run recompute keeps a config edit live on the next run.
|
|
105
104
|
const check = detectBuildCheck(root, config) ?? undefined;
|
|
106
|
-
// The bounded actionable index every
|
|
107
|
-
// per-
|
|
108
|
-
// both branches; the worktree starts at main at
|
|
105
|
+
// The bounded actionable index every run and director prompt carries, replacing the model's
|
|
106
|
+
// per-run `grep -n '^##'` map of the backlog files (BUGS.md 2026-10-06). One render serves
|
|
107
|
+
// both branches; the worktree starts at main at run start, so the ranges hold then.
|
|
109
108
|
const backlogIndex = renderBacklogIndexBlock(root);
|
|
109
|
+
// A change rejected in review is the only cross-run memory of what was built and why it
|
|
110
|
+
// failed — every run starts a fresh session, so the full reasons (with the reviewed head and
|
|
111
|
+
// timestamp) ride along on the next prompt until the role's next reviewed change replaces
|
|
112
|
+
// them. buildRunPrompt renders it at the very top, before the role charter, so the author
|
|
113
|
+
// cannot miss it (plans/reviewer-as-role.md part 4/4). A revision run carries the reasons in
|
|
114
|
+
// buildRevisionNote instead (the rejected diff is already in the worktree), so the note is
|
|
115
|
+
// suppressed here exactly as it was before this part.
|
|
116
|
+
const rejectedReview = (state.lastReview?.verdict === "reject" || state.lastReview?.verdict === "reverted") && !state.revision
|
|
117
|
+
? buildRejectedReviewNote(state.lastReview, role)
|
|
118
|
+
: undefined;
|
|
110
119
|
let prompt;
|
|
111
120
|
let userPrompt = null;
|
|
112
121
|
let userPromptFile = null;
|
|
113
122
|
if (role === DIRECTOR_ROLE) {
|
|
114
123
|
// A deferred director prompt's `tumwater:not-before` marker line is plumbing
|
|
115
124
|
// (src/inbox/prompt-not-before.ts),
|
|
116
|
-
// not content — stripped here exactly like the per-role path below, so the director's
|
|
125
|
+
// not content — stripped here exactly like the per-role path below, so the director's run
|
|
117
126
|
// prompt and the runner's re-queue both see the operator's text alone.
|
|
118
127
|
const entry = preview ? null : dequeuePromptEntry(root);
|
|
119
128
|
const dequeued = preview ? peekPrompt(root) : entry?.text ?? null;
|
|
@@ -121,42 +130,41 @@ export function assembleTickPrompt({ root, config, role, state, preview = false,
|
|
|
121
130
|
return null;
|
|
122
131
|
userPrompt = stripNotBeforeMarker(dequeued);
|
|
123
132
|
userPromptFile = entry?.file ?? null;
|
|
124
|
-
|
|
125
|
-
// and that file carries the same text.
|
|
126
|
-
prompt = buildDirectorPrompt(userPrompt, compact ? "" : initialPrompt, principles, check, brief, undefined, backlogIndex, agent ?? configForRole(config, DIRECTOR_ROLE).agent, compact, config.roles[DIRECTOR_ROLE]?.instructions);
|
|
133
|
+
prompt = buildDirectorPrompt(userPrompt, principles, check, brief, undefined, backlogIndex, backend ?? configForRole(config, DIRECTOR_ROLE).agent, config.roles[DIRECTOR_ROLE]?.instructions);
|
|
127
134
|
}
|
|
128
135
|
else {
|
|
129
|
-
// The
|
|
136
|
+
// The role id keys its inbox and notes; every lookup ABOUT the role — catalog charter,
|
|
130
137
|
// instructions, the qa/selfcheck/clean blocks — resolves through the base role, so
|
|
131
138
|
// `feature-2` runs the feature charter and `roles.feature.instructions` while keeping its
|
|
132
139
|
// own queue and notes (plans/parallel-work-instances.md).
|
|
133
140
|
const base = baseRoleOf(role);
|
|
134
|
-
// Catalog first, then user-defined
|
|
141
|
+
// Catalog first, then user-defined roles (plans/user-defined-loops.md): a custom's task
|
|
135
142
|
// is its entire find-something-to-do text and the title identifies it in the prompt.
|
|
136
143
|
const custom = config.customRoles.find((c) => c.name === role);
|
|
137
144
|
const resolved = roleById(role) ?? (custom ? customRole(custom.name, custom.task) : undefined);
|
|
138
145
|
if (!resolved) {
|
|
139
146
|
// The shared unknownRoleMessage (parseRoleFlag, the operator commands) names the valid
|
|
140
|
-
// ids; this defensive path — a runner asked to
|
|
147
|
+
// ids; this defensive path — a runner asked to run a role no catalog entry or
|
|
141
148
|
// customRoles task can answer — says the same, so if it ever fires it reads as the
|
|
142
149
|
// harness bug it is instead of a bare dead end.
|
|
143
150
|
const validIds = [...allRoleIds(), ...config.customRoles.map((c) => c.name)];
|
|
144
151
|
throw new Error(unknownRoleMessage(role, validIds));
|
|
145
152
|
}
|
|
146
153
|
// A queued per-role prompt is dequeued here, before the prompt is built, so its text rides
|
|
147
|
-
// in the
|
|
154
|
+
// in the run's prompt; PendingPrompt records it as pending and re-queues it on every
|
|
148
155
|
// unfulfilled outcome — including a red-main gate block, which returns before any run.
|
|
149
156
|
// A deferred prompt's `tumwater:not-before` marker line is plumbing
|
|
150
157
|
// (src/inbox/prompt-not-before.ts), not
|
|
151
|
-
// content: it is stripped at delivery, so the
|
|
158
|
+
// content: it is stripped at delivery, so the role sees the operator's text alone and the
|
|
152
159
|
// runner's re-queue writes clean text.
|
|
153
160
|
const entry = preview ? null : dequeueRolePromptEntry(root, role);
|
|
154
161
|
const dequeued = preview ? peekRolePrompt(root, role) : entry?.text ?? null;
|
|
155
162
|
const request = dequeued === null ? null : stripNotBeforeMarker(dequeued);
|
|
156
|
-
// The selfcheck
|
|
157
|
-
// worktree, so its evidence module renders it (
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
// The selfcheck and feedback observers' evidence is the harness's own event log, one level
|
|
164
|
+
// outside this worktree, so its evidence module renders it (selfcheckDigest) and the run
|
|
165
|
+
// injects it.
|
|
166
|
+
const digest = base === "selfcheck" || base === "feedback" ? selfcheckDigest(root) : undefined;
|
|
167
|
+
// The `qa` observer's flow rotation needs a memory of what it last exercised; every run
|
|
160
168
|
// is a fresh session, and a passing cheap check leaves nothing in the repo. The ledger is
|
|
161
169
|
// runtime state, and a missing or unreadable one degrades to no block
|
|
162
170
|
// (plans/observer-roles.md 2/2).
|
|
@@ -169,23 +177,49 @@ export function assembleTickPrompt({ root, config, role, state, preview = false,
|
|
|
169
177
|
coverage = undefined;
|
|
170
178
|
}
|
|
171
179
|
}
|
|
180
|
+
// The `feedback` observer's per-install filing ledger: what this install already filed as
|
|
181
|
+
// GitHub issues on znation/tumwater, so the fresh session's charter dedupes against a
|
|
182
|
+
// "previously filed" block instead of the network alone. Runtime state, and a missing or
|
|
183
|
+
// unreadable ledger degrades to no block (plans/feedback-role.md 2/2).
|
|
184
|
+
// The render returns undefined for an empty ledger, so the prompt is unchanged in the common
|
|
185
|
+
// first-run case.
|
|
186
|
+
let filings;
|
|
187
|
+
if (base === "feedback") {
|
|
188
|
+
try {
|
|
189
|
+
filings = renderFilingsBlock(readFeedbackFilings(root));
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
filings = undefined;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
172
195
|
// The clean role's deterministic backlog repair: stranded plan headings in the primary
|
|
173
196
|
// checkout's PLANS.md (plans, part 3/4), rendered like the digest and coverage blocks.
|
|
174
197
|
// An unreadable or clean file gives no block, so the prompt is unchanged in the common case.
|
|
175
198
|
const backlogStructure = base === "clean" ? renderBacklogStructureBlock(root) : undefined;
|
|
176
|
-
// The role's
|
|
199
|
+
// The role's notes (PLANS.md "Role notebook"): its own earlier runs' note. A missing,
|
|
177
200
|
// empty, or unreadable file yields undefined, so the block is omitted; the write
|
|
178
|
-
// instruction in
|
|
179
|
-
// reads this path, so it carries no
|
|
201
|
+
// instruction in buildRunPrompt is present either way. The director branch above never
|
|
202
|
+
// reads this path, so it carries no notes.
|
|
180
203
|
const notes = readRoleNote(root, role);
|
|
181
204
|
// The note file's mtime is when the notes were last saved (writes replace the file), so the
|
|
182
205
|
// run can weigh how stale they are.
|
|
183
206
|
const savedStat = notes === undefined ? null : statOrNull(roleNotesPath(root, role));
|
|
184
207
|
const notesSavedDay = savedStat ? dayKey(savedStat.mtimeMs) : undefined;
|
|
208
|
+
// The live state of each entry and series the notes name, so a stale note cannot keep a hold
|
|
209
|
+
// the backlog has lifted (notes-backlog.ts). The three readers are already stat-cached. A
|
|
210
|
+
// note naming nothing live yields undefined and no block — the common case, since most notes
|
|
211
|
+
// are about the code, not the backlog.
|
|
212
|
+
const notesBacklog = notes === undefined || notes.trim() === ""
|
|
213
|
+
? undefined
|
|
214
|
+
: notesBacklogBlock(notes, {
|
|
215
|
+
planned: plannedPlanEntries(root),
|
|
216
|
+
openBugs: openBugEntries(root),
|
|
217
|
+
donePlanTitles: donePlans(root),
|
|
218
|
+
});
|
|
185
219
|
// The plan charter's stop threshold is derived from config (plans/parallel-work-
|
|
186
|
-
// instances.md "…keeping the plan
|
|
220
|
+
// instances.md "…keeping the plan role ahead", part 5c/7). The catalog text stays a
|
|
187
221
|
// config-free description of the rule; this note supplies the concrete eligible-plan count
|
|
188
|
-
// so the plan
|
|
222
|
+
// so the plan role stops only once every configured feature instance has a plan waiting.
|
|
189
223
|
const planTargetNote = base === "plan"
|
|
190
224
|
? `This run's stop target: when PLANS.md \`## Planned\` already holds ` +
|
|
191
225
|
`${planBacklogTarget(config)} or more eligible plans, end with ${NOTHING_TO_DO}. ` +
|
|
@@ -194,30 +228,31 @@ export function assembleTickPrompt({ root, config, role, state, preview = false,
|
|
|
194
228
|
: undefined;
|
|
195
229
|
const flakyChecks = base === "bugfix" ? flakyChecksNote(root) : undefined;
|
|
196
230
|
// A run holding a claim with no queued request is assigned its one entry (the assignment
|
|
197
|
-
// note below).
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const variant = compact ? { assigned: state.claim !== undefined && request === null } : undefined;
|
|
202
|
-
prompt = buildTickPrompt({
|
|
231
|
+
// note below). Such a run reads that entry first, so the backlog index — what an unassigned
|
|
232
|
+
// run chooses its task from — stays out.
|
|
233
|
+
const assigned = state.claim !== undefined && request === null;
|
|
234
|
+
prompt = buildRunPrompt({
|
|
203
235
|
role: resolved,
|
|
204
|
-
initialPrompt: compact ? "" : initialPrompt,
|
|
205
236
|
principles,
|
|
206
237
|
digest,
|
|
207
238
|
coverage,
|
|
239
|
+
filings,
|
|
208
240
|
backlogStructure,
|
|
209
|
-
backlogIndex:
|
|
241
|
+
backlogIndex: assigned ? undefined : backlogIndex,
|
|
210
242
|
notes,
|
|
211
243
|
notesSavedDay,
|
|
244
|
+
notesBacklog,
|
|
212
245
|
planTargetNote,
|
|
213
246
|
flakyChecks,
|
|
214
247
|
extraInstructions: config.roles[base]?.instructions,
|
|
215
248
|
check,
|
|
249
|
+
gateCommand: gateCommandOf(config),
|
|
216
250
|
briefFile: brief,
|
|
217
|
-
agent:
|
|
218
|
-
|
|
251
|
+
agent: backend ?? configForRole(config, role).agent,
|
|
252
|
+
assigned,
|
|
253
|
+
rejectedReview,
|
|
219
254
|
// A per-role prompt queued by `tumwater prompt --role <id>` rides as an explicit user
|
|
220
|
-
// request block; its text is also the
|
|
255
|
+
// request block; its text is also the run's userPrompt,
|
|
221
256
|
// so the runner's pending-prompt machinery (re-queue on unfulfilled, clear on merge)
|
|
222
257
|
// treats it exactly like the director's dequeued request.
|
|
223
258
|
userRequest: request ?? undefined,
|
|
@@ -229,8 +264,8 @@ export function assembleTickPrompt({ root, config, role, state, preview = false,
|
|
|
229
264
|
}
|
|
230
265
|
// A work instance holding a claim is told which entry is its one task (plans/parallel-work-
|
|
231
266
|
// instances.md "Claims", part 4/7) — the harness picked it, so the charter's choosing step
|
|
232
|
-
// is replaced. A
|
|
233
|
-
// claim for the next non-request
|
|
267
|
+
// is replaced. A run that dequeued a user request runs that request instead and keeps the
|
|
268
|
+
// claim for the next non-request run. The range is looked up by key in the current checkout,
|
|
234
269
|
// so it survives a moved section boundary.
|
|
235
270
|
const claim = role === DIRECTOR_ROLE ? undefined : state.claim;
|
|
236
271
|
if (claim && userPrompt === null) {
|
|
@@ -238,28 +273,28 @@ export function assembleTickPrompt({ root, config, role, state, preview = false,
|
|
|
238
273
|
prompt = appendPromptNote(prompt, buildAssignmentNote(claim, range, range ? missingEntryPaths(root, claim.file, range) : []));
|
|
239
274
|
const merged = claim.file === "PLANS.md" ? mergedPrerequisiteRanges(root, claim.title) : [];
|
|
240
275
|
if (merged.length > 0)
|
|
241
|
-
prompt = appendPromptNote(prompt, buildMergedPrerequisitesNote(merged
|
|
276
|
+
prompt = appendPromptNote(prompt, buildMergedPrerequisitesNote(merged));
|
|
242
277
|
}
|
|
243
278
|
if (claim && userPrompt === null && claim.file === "PLANS.md") {
|
|
244
279
|
const waiting = waitingPartRanges(root, claim.title);
|
|
245
280
|
if (waiting.length > 0)
|
|
246
|
-
prompt = appendPromptNote(prompt, buildWaitingPartsNote(waiting
|
|
281
|
+
prompt = appendPromptNote(prompt, buildWaitingPartsNote(waiting));
|
|
247
282
|
}
|
|
248
283
|
if (!claim && userPrompt === null && role !== DIRECTOR_ROLE && state.heldElsewhere?.length) {
|
|
249
284
|
prompt = appendPromptNote(prompt, buildHeldEntriesNote(state.heldElsewhere));
|
|
250
285
|
}
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
if (
|
|
255
|
-
prompt =
|
|
286
|
+
// The director builds its prompt through buildDirectorPrompt, which does not take the rejected
|
|
287
|
+
// note, so the standing rejection leads its prompt here instead (a director-authored change can
|
|
288
|
+
// be rejected too). Kept uniform with the authoring roles: the reasons open the run.
|
|
289
|
+
if (rejectedReview !== undefined && role === DIRECTOR_ROLE) {
|
|
290
|
+
prompt = prependPromptNote(prompt, rejectedReview);
|
|
256
291
|
}
|
|
257
292
|
// Likewise a change leftover recovery discarded as unmergeable: named until the role queues
|
|
258
|
-
// its next change (the discarding
|
|
293
|
+
// its next change (the discarding run itself appends it after recovery — see runOnce).
|
|
259
294
|
const discard = state.conflictDiscard;
|
|
260
295
|
if (discard)
|
|
261
296
|
prompt = appendPromptNote(prompt, buildConflictDiscardNote(discard.summary, discard.attempts));
|
|
262
|
-
// A fresh
|
|
297
|
+
// A fresh run after the previous run(s) were cut off at the context ceiling (the role
|
|
263
298
|
// stopped resuming, or never resumed — the director re-runs its prompt fresh): the only
|
|
264
299
|
// memory that the last attempt was too big for the window is this note.
|
|
265
300
|
if ((state.cutOffStreak ?? 0) > 0) {
|
|
@@ -1,26 +1,52 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { backendSessionResumable } from "../agent/agent-backend.js";
|
|
2
2
|
import { latestSession } from "../agent/session-ledger.js";
|
|
3
3
|
import { RETRIABLE_MERGE_RESULTS } from "../merge/merge-core.js";
|
|
4
4
|
import { sessionDir } from "../paths.js";
|
|
5
5
|
import { buildHandoffPrompt, buildResumePrompt } from "../prompt/prompt-followup.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
export function
|
|
6
|
+
import { appendPromptNote } from "../text/format.js";
|
|
7
|
+
/** Whether a run takes back the work an earlier run of its role left unsettled
|
|
8
|
+
* (src/git/slot-salvage.ts): an authoring run of the role's own work does, while a promptless
|
|
9
|
+
* recovery run (it authors nothing) and a user's request (the edits are not about it) leave the
|
|
10
|
+
* patch waiting for a later run. */
|
|
11
|
+
export function takesSalvagedWork(plan) {
|
|
12
|
+
return plan.prompt !== null && plan.userPrompt === null;
|
|
13
|
+
}
|
|
14
|
+
/** The plan of a run whose fresh checkout just had its role's salvaged work restored into it
|
|
15
|
+
* (`restored`, the patch's root-relative path). A fresh run becomes a `salvaged` handoff over
|
|
16
|
+
* those edits: a fresh session told they are there, which skips the fresh path's reset as every
|
|
17
|
+
* handoff does. A resume or handoff keeps its plan: its kept slot was lost, and the restore gives
|
|
18
|
+
* it back the edits it expects. `note`, when the restore left conflicts or hunks it could not
|
|
19
|
+
* place (buildSalvageRestoreNote), is appended to either prompt. */
|
|
20
|
+
export function planWithSalvagedWork(role, plan, restored, note) {
|
|
21
|
+
if (plan.prompt === null)
|
|
22
|
+
return { ...plan, restored };
|
|
23
|
+
const handingOff = !plan.resuming && !plan.handoff;
|
|
24
|
+
const prompt = handingOff ? buildHandoffPrompt(role, plan.prompt, "salvaged") : plan.prompt;
|
|
25
|
+
return {
|
|
26
|
+
...plan,
|
|
27
|
+
...(handingOff ? { handoff: true, resumeCause: "salvaged" } : {}),
|
|
28
|
+
prompt: note === undefined ? prompt : appendPromptNote(prompt, note),
|
|
29
|
+
restored,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Decide how a run starts, consuming the resume flags as a side effect (the flag is
|
|
33
|
+
* consumed here so a resume that fails falls back to a normal fresh run; another shutdown
|
|
34
|
+
* mid-resume sets it again) and reclaiming the interrupted run's re-queued prompt. Returns
|
|
35
|
+
* null — meaning the run is skipped — when the role has nothing to run (an empty director
|
|
36
|
+
* inbox); a role runner's assembly never returns null. */
|
|
37
|
+
export function planRunStart(opts) {
|
|
12
38
|
const s = opts.state;
|
|
13
39
|
// The last merge's failure, read before the clear below: a merge that failed
|
|
14
40
|
// non-terminally kept its pin (the merger's own vocabulary — review_error, merge_conflict,
|
|
15
|
-
// merge_blocked — with the detail it wrote into `lastError`), and when this
|
|
16
|
-
// re-queues that pin, the failure must still feed the error streak (
|
|
41
|
+
// merge_blocked — with the detail it wrote into `lastError`), and when this run's recovery
|
|
42
|
+
// re-queues that pin, the failure must still feed the error streak (finishRecoveryRun).
|
|
17
43
|
const priorMergeFailure = s.lastResult !== undefined && RETRIABLE_MERGE_RESULTS.has(s.lastResult)
|
|
18
44
|
? (s.lastError ?? `merge failed: ${s.lastResult}`)
|
|
19
45
|
: undefined;
|
|
20
46
|
s.lastError = undefined;
|
|
21
|
-
// A
|
|
47
|
+
// A run interrupted by a harness shutdown left its pi session and its worktree's
|
|
22
48
|
// uncommitted edits in place: resume that session instead of starting fresh. The flag
|
|
23
|
-
// is consumed here so a resume that fails falls back to a normal fresh
|
|
49
|
+
// is consumed here so a resume that fails falls back to a normal fresh run; another
|
|
24
50
|
// shutdown mid-resume sets it again. Nothing to resume (sessions pruned, or pi never
|
|
25
51
|
// started) also falls back to fresh.
|
|
26
52
|
//
|
|
@@ -34,7 +60,7 @@ export function planTickStart(opts) {
|
|
|
34
60
|
const resumePending = s.resumePending === true;
|
|
35
61
|
// The previous run's own verdict that its `--resume` target is gone is authoritative over the
|
|
36
62
|
// transcript check: a lingering transcript (a torn/empty file, or the fake's planted one) must
|
|
37
|
-
// not re-arm a resume the backend just rejected, or the
|
|
63
|
+
// not re-arm a resume the backend just rejected, or the role re-resumes and re-fails forever.
|
|
38
64
|
const resumeMissing = s.resumeMissing === true;
|
|
39
65
|
const latest = resumePending ? latestSession(dir) : null;
|
|
40
66
|
// Our backend decides whether the newest session of this kind is actually resumable: pi
|
|
@@ -42,18 +68,18 @@ export function planTickStart(opts) {
|
|
|
42
68
|
// claude resumes an id (safe across a newer gate line). A backend with no `resumable` hook
|
|
43
69
|
// falls back to the backend-neutral session-file check.
|
|
44
70
|
const resumable = resumePending && !resumeMissing
|
|
45
|
-
?
|
|
71
|
+
? backendSessionResumable(dir, { kind: "author", agent }, opts.claudeConfigDir)
|
|
46
72
|
: false;
|
|
47
73
|
const resumableSession = resumable;
|
|
48
74
|
// A newest line our backend cannot resume is not ours to continue: another kind's or
|
|
49
75
|
// another agent's session, OR our own author line whose session the backend no longer holds
|
|
50
76
|
// (a `--resume` that answered "No conversation found" mid-run, leaving `resumeMissing`, or a
|
|
51
77
|
// pruned session dir). In every such case the interrupted run's uncommitted edits still
|
|
52
|
-
// deserve a handoff rather than the fresh path's worktree reset: the next
|
|
53
|
-
// session over the kept worktree, carrying the assembled
|
|
78
|
+
// deserve a handoff rather than the fresh path's worktree reset: the next run runs a FRESH
|
|
79
|
+
// session over the kept worktree, carrying the assembled run prompt plus the handoff
|
|
54
80
|
// bridge (plans/agent-backends.md "Sessions"). The failed `resumeMissing` run arms
|
|
55
81
|
// `resumePending` and records `resumeMissing` from the verdict (its error turn carries no
|
|
56
|
-
// assistant content), so the very next
|
|
82
|
+
// assistant content), so the very next run ends up here with the worktree intact. A dir with
|
|
57
83
|
// no ledger line at all is not a handoff — there is no recorded interrupted session to carry
|
|
58
84
|
// forward.
|
|
59
85
|
// Captured before the flag is consumed below; a stale cause must not leak into a later resume.
|
|
@@ -63,7 +89,7 @@ export function planTickStart(opts) {
|
|
|
63
89
|
// and claude keeps no flags across `--resume` (plans/agent-backends.md §7, part 7a/7).
|
|
64
90
|
const budgetCapped = pendingResumeCause === "budget-capped";
|
|
65
91
|
// An image-blind run with an image-capable peer tier ends `aborted` with this cause
|
|
66
|
-
// (plans/image-input.md part 4/5): the next
|
|
92
|
+
// (plans/image-input.md part 4/5): the next run runs a FRESH session on the TARGET seam
|
|
67
93
|
// over the kept worktree — never `--continue` — because the old session carries pi's
|
|
68
94
|
// "image omitted" note and the target may be a different backend.
|
|
69
95
|
const imageReroute = pendingResumeCause === "image-reroute";
|
|
@@ -73,7 +99,7 @@ export function planTickStart(opts) {
|
|
|
73
99
|
s.resumeMissing = undefined;
|
|
74
100
|
// An interruption during the review gate leaves the author's work fully committed — there
|
|
75
101
|
// is nothing left to finish in its session. Recover (and re-review) the leftover commits
|
|
76
|
-
// via a fresh
|
|
102
|
+
// via a fresh run instead: continuing the author session would burn a run on finished
|
|
77
103
|
// work, and any uncommitted edits are the reviewer's stray output, discarded by the fresh
|
|
78
104
|
// path's reset below.
|
|
79
105
|
const resumeBlocked = s.phase === "review";
|
|
@@ -81,14 +107,14 @@ export function planTickStart(opts) {
|
|
|
81
107
|
// when its transcript is still resumable: it hands off instead.
|
|
82
108
|
const resuming = resumableSession && !resumeBlocked && !budgetCapped && !imageReroute;
|
|
83
109
|
const handingOff = handoff && !resumeBlocked;
|
|
84
|
-
// Reclaim the prompt the interrupted
|
|
85
|
-
// the resumed session still owns that request in its context, so this
|
|
110
|
+
// Reclaim the prompt the interrupted run re-queued for its resume (src/inbox/pending-prompt.ts):
|
|
111
|
+
// the resumed session still owns that request in its context, so this run's bookkeeping
|
|
86
112
|
// operates on the queue's copy. A handoff runs a FRESH session, so it does not reclaim — the
|
|
87
|
-
// fresh
|
|
113
|
+
// fresh run dequeues the re-queued request like any other. Consumed even when this run does
|
|
88
114
|
// not resume, so a stale record never survives into a later resume.
|
|
89
115
|
opts.pending.reclaimForResume(s, resuming);
|
|
90
116
|
// Why the resume: a named quiet-kill means the last run died on a stalled tool call (the
|
|
91
|
-
// bridge then warns against re-running it unchanged) or on the
|
|
117
|
+
// bridge then warns against re-running it unchanged) or on the run deadline while still
|
|
92
118
|
// making progress (the bridge asks for the smallest finish against the same limit); a
|
|
93
119
|
// cut-off streak means the last run ran out of context and pi compacted the session (the
|
|
94
120
|
// bridge asks for the smallest finish); otherwise a shutdown/crash. A handoff names itself,
|
|
@@ -105,7 +131,7 @@ export function planTickStart(opts) {
|
|
|
105
131
|
prompt = buildResumePrompt(opts.role, resumeCause, agent);
|
|
106
132
|
}
|
|
107
133
|
else {
|
|
108
|
-
const assembled = opts.
|
|
134
|
+
const assembled = opts.runPrompt(agent);
|
|
109
135
|
// The image-reroute handoff note names the model the blind run was on and the image it
|
|
110
136
|
// read; those facts ride the one-shot marker, which is consumed here. A handoff's fresh
|
|
111
137
|
// session also needs the to-be-run model named for its own side of the note, but the
|
|
@@ -122,9 +148,12 @@ export function planTickStart(opts) {
|
|
|
122
148
|
? buildHandoffPrompt(opts.role, assembled, resumeCause, rerouteFacts)
|
|
123
149
|
: assembled;
|
|
124
150
|
}
|
|
125
|
-
if (prompt === null)
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
if (prompt === null) {
|
|
152
|
+
if (!opts.recoveryOnly)
|
|
153
|
+
return null;
|
|
154
|
+
return { priorMergeFailure, resuming: false, handoff: false, resumeCause, prompt: null, userPrompt: null };
|
|
155
|
+
}
|
|
156
|
+
// The raw user prompt a director run is executing (null for role runners), so an
|
|
128
157
|
// unfulfilled outcome below can re-queue it. Captured before the field is cleared.
|
|
129
158
|
const userPrompt = opts.pending.get();
|
|
130
159
|
return { priorMergeFailure, resuming, handoff: handingOff, resumeCause, prompt, userPrompt };
|