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
|
@@ -4,25 +4,27 @@ import { commitPathsAndDiscardRest, fileContentAt } from "../git/git.js";
|
|
|
4
4
|
import { writeTextAtomic } from "../files/files.js";
|
|
5
5
|
import { parseEntryDetails } from "../backlog/backlog-md.js";
|
|
6
6
|
import { actionableEntryRanges, stripEntryStamp } from "../backlog/backlog-structure.js";
|
|
7
|
-
import { entryHoldAgainst,
|
|
8
|
-
import {
|
|
7
|
+
import { entryHoldAgainst, refusalNote, withoutRefusalNotes } from "../backlog/backlog-eligibility.js";
|
|
8
|
+
import { entryKey, plannedPartKeys } from "../backlog/backlog-parts.js";
|
|
9
|
+
import { baseRoleOf } from "../roles/runner-ids.js";
|
|
9
10
|
import { changedFiles } from "../git/git-diff.js";
|
|
10
11
|
import { resetWorktreeToMain } from "../git/worktree.js";
|
|
11
12
|
import { buildCommitMessage, commitTrailer, stampedSubject } from "../git/commit-message.js";
|
|
13
|
+
import { errorMessage } from "../text/text.js";
|
|
12
14
|
import { labeledLine } from "./reply-contract.js";
|
|
13
15
|
import { logEventBestEffort } from "../events/events.js";
|
|
14
|
-
import { clearSupersededRejection } from "../
|
|
15
|
-
/** Handling a refused
|
|
16
|
+
import { clearSupersededRejection } from "../run/run-apply.js";
|
|
17
|
+
/** Handling a refused run (plans/refusal-and-thrash.md): the run declined its work and ended
|
|
16
18
|
* with TUMWATER_REFUSED. Only the markdown objection note may merge — it is the durable record
|
|
17
|
-
* that blocks the entry for later
|
|
19
|
+
* that blocks the entry for later runs; any non-markdown half-work is discarded, tracked edits
|
|
18
20
|
* via reset --hard and untracked files via clean -fd (the committed note is safe at HEAD by
|
|
19
21
|
* then). A refusal with no note left resets the worktree cleanly and lets the reason live in
|
|
20
22
|
* the event + lastSummary only. The note commit merges directly: md-only diffs are review-exempt
|
|
21
23
|
* by construction under the gate's exemption patterns, so routing it through the gate would burn
|
|
22
|
-
* nothing but add a failure mode for a record that is not code. Split out of
|
|
23
|
-
* keeps the
|
|
24
|
+
* nothing but add a failure mode for a record that is not code. Split out of runner.ts — which
|
|
25
|
+
* keeps the run lifecycle around it — because this is a self-contained concern with its own
|
|
24
26
|
* policy and its own git surface (commit-only-the-notes / reset); the only things it borrows
|
|
25
|
-
* from the
|
|
27
|
+
* from the runner are identity, the pre-commit counters for the trailer, and the runner's shared
|
|
26
28
|
* merge wiring. */
|
|
27
29
|
/** A self-contradicted refusal: the reply declares a refusal yet also carries a SUMMARY —
|
|
28
30
|
* the contract field of a work-completed reply — beside non-markdown work in the worktree.
|
|
@@ -79,11 +81,14 @@ export function newlyRefusedTitles(baseMd, headMd, section) {
|
|
|
79
81
|
})
|
|
80
82
|
.map((e) => stripEntryStamp(e.title));
|
|
81
83
|
}
|
|
82
|
-
/** Handle a refused
|
|
83
|
-
*
|
|
84
|
+
/** Handle a refused run and return its outcome. Never throws: a failed note merge (a non-merge
|
|
85
|
+
* result, or a throw such as a lock timeout) is recorded in `state.lastError` and reported
|
|
86
|
+
* through the "refused" result like any other non-merge. A failed write of the stripped PLANS.md
|
|
87
|
+
* logs a warning and leaves PLANS.md out of the commit, so the other notes still merge. */
|
|
84
88
|
export async function handleRefusal(ctx, state, wt, pi) {
|
|
85
89
|
const reason = (pi.refusedReason ?? "").trim() || "no reason given";
|
|
86
90
|
const plansFile = path.join(wt, "PLANS.md");
|
|
91
|
+
let plansUnstripped = false;
|
|
87
92
|
if (fs.existsSync(plansFile)) {
|
|
88
93
|
const { md, dropped } = withoutBlockedPlanRefusals(await fileContentAt(wt, ctx.mainBranch, "PLANS.md"), fs.readFileSync(plansFile, "utf8"));
|
|
89
94
|
if (dropped.length > 0) {
|
|
@@ -92,17 +97,27 @@ export async function handleRefusal(ctx, state, wt, pi) {
|
|
|
92
97
|
// permanently dropping the plan entries past the cut point. writeTextAtomic is the
|
|
93
98
|
// harness's one write-to-temp-then-rename entry point — the same one backlog-write.ts
|
|
94
99
|
// and question-answer.ts use for the markdown backlog files.
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
try {
|
|
101
|
+
writeTextAtomic(plansFile, md);
|
|
102
|
+
for (const title of dropped) {
|
|
103
|
+
logEventBestEffort(ctx.root, {
|
|
104
|
+
source: ctx.role,
|
|
105
|
+
type: "warning",
|
|
106
|
+
message: `dropped refusal note on blocked plan "${title}": its prerequisite is still planned, so the block already holds it`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
plansUnstripped = true;
|
|
97
112
|
logEventBestEffort(ctx.root, {
|
|
98
113
|
source: ctx.role,
|
|
99
114
|
type: "warning",
|
|
100
|
-
message: `
|
|
115
|
+
message: `could not drop refusal note(s) on blocked plan(s) ${dropped.map((t) => `"${t}"`).join(", ")}: ${errorMessage(err)}; PLANS.md is left out of this refusal's commit`,
|
|
101
116
|
});
|
|
102
117
|
}
|
|
103
118
|
}
|
|
104
119
|
}
|
|
105
|
-
const notes = (await changedFiles(wt)).filter((f) => f.toLowerCase().endsWith(".md"));
|
|
120
|
+
const notes = (await changedFiles(wt)).filter((f) => f.toLowerCase().endsWith(".md") && !(plansUnstripped && f === "PLANS.md"));
|
|
106
121
|
const entries = [];
|
|
107
122
|
// Best-effort naming for the entry_refused event; it must never fail the refusal.
|
|
108
123
|
for (const { file, section } of [{ file: "PLANS.md", section: "Planned" }, { file: "BUGS.md", section: "Open" }]) {
|
|
@@ -119,7 +134,7 @@ export async function handleRefusal(ctx, state, wt, pi) {
|
|
|
119
134
|
if (notes.length > 0) {
|
|
120
135
|
// Subject + trailer only — a refusal carries no WHY/RISK/VERIFIED body; the reason is
|
|
121
136
|
// the subject, and the trailer's turn count is the same field the friction flag reads.
|
|
122
|
-
const message = buildCommitMessage(stampedSubject(baseRoleOf(ctx.role), `refuse — ${reason}`), null, commitTrailer(ctx.role, state.runs, ctx.turns, state.peakContextTokens));
|
|
137
|
+
const message = buildCommitMessage(stampedSubject(baseRoleOf(ctx.role), `refuse — ${reason}`), null, commitTrailer(ctx.role, state.runs, ctx.turns, state.peakContextTokens, undefined, undefined, ctx.modelLine));
|
|
123
138
|
commit = (await commitPathsAndDiscardRest(wt, message, notes)) ?? undefined;
|
|
124
139
|
}
|
|
125
140
|
if (!commit) {
|
|
@@ -128,7 +143,16 @@ export async function handleRefusal(ctx, state, wt, pi) {
|
|
|
128
143
|
await resetWorktreeToMain(wt, ctx.mainBranch);
|
|
129
144
|
return { result: "refused", summary: reason };
|
|
130
145
|
}
|
|
131
|
-
|
|
146
|
+
let result;
|
|
147
|
+
try {
|
|
148
|
+
result = await ctx.merge(wt, `refused: ${reason}`);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
// A lock timeout or `cannot acquire lock`: the note stays committed on the branch for
|
|
152
|
+
// leftover recovery, as for merge_conflict.
|
|
153
|
+
state.lastError = `refusal note merge failed: ${errorMessage(err)}`;
|
|
154
|
+
return { result: "refused", summary: reason, commit };
|
|
155
|
+
}
|
|
132
156
|
if (result !== "changed")
|
|
133
157
|
state.lastError = `refusal note merge failed: ${result}`;
|
|
134
158
|
// A merged refusal note is a merged change too: it supersedes a stale rejection exactly as
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
/** The machine-detectable half of the reply contract every pi run must follow: the
|
|
3
|
-
* TUMWATER_NOTHING_TO_DO sentinel a
|
|
4
|
-
* TUMWATER_REFUSED line a
|
|
2
|
+
* TUMWATER_NOTHING_TO_DO sentinel a role emits when it found nothing to do, the
|
|
3
|
+
* TUMWATER_REFUSED line a role emits when it declines its task, and the review gate's VERDICT
|
|
5
4
|
* line. This module owns both halves of that contract: the constants and detection the harness
|
|
6
5
|
* uses to parse pi's replies — so the run's stream parser (pi-stream.ts) and the review gate
|
|
7
6
|
* (review.ts) detect it without reaching into prompt construction, and the verdict line's shape
|
|
@@ -21,24 +20,10 @@
|
|
|
21
20
|
* commonRules. Backlog files are included because this repo's own suite reads them
|
|
22
21
|
* (backlog-structure and validation-gap tests). The header says small changes are welcome on
|
|
23
22
|
* purpose: a first wording ("one false claim rejects the whole change") made the improve role
|
|
24
|
-
* decline real, small improvements in lab A/B runs, citing the reviewer.
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
change rejected; a small, correct change is welcome:
|
|
29
|
-
- State only what you verified. SUMMARY, WHY, RISK, VERIFIED, and every doc comment or
|
|
30
|
-
PLANS.md/BUGS.md line you write are claims about the code.
|
|
31
|
-
- Back each universal word — "all", "every", "only", "none remain", "untested", "byte-identical",
|
|
32
|
-
"unchanged" — with the check that proves it (a grep over the source, the tests, and the docs;
|
|
33
|
-
a diff), or drop the word.
|
|
34
|
-
- Never state test or suite counts: say what you ran and what you saw. When the merge gate runs
|
|
35
|
-
the project's check, tumwater attests the numbers itself.
|
|
36
|
-
- Verify after your LAST edit, per "Leave the project working" above: an edit made after the last
|
|
37
|
-
green run is unverified, so verify again before you end. Order your work so the final
|
|
38
|
-
verification comes after your last edit, backlog files included.`;
|
|
39
|
-
/** CLAIMS_RULE in the compact prompt every run gets (prompt.ts commonRules): the same
|
|
40
|
-
* four directives, without the reasons. */
|
|
41
|
-
export const CLAIMS_RULE_COMPACT = `Claims — a reviewer checks your change against the code before it merges, and an inaccurate
|
|
23
|
+
* decline real, small improvements in lab A/B runs, citing the reviewer. The rules state the
|
|
24
|
+
* directives without these reasons, since every turn re-sends them (prompt.ts). Must not mention
|
|
25
|
+
* the reviewer's VERDICT form (prompt tests count it in the review prompt only). */
|
|
26
|
+
export const CLAIMS_RULE = `Claims — a reviewer checks your change against the code before it merges, and an inaccurate
|
|
42
27
|
claim gets it rejected; a small, correct change is welcome:
|
|
43
28
|
- State only what you verified: SUMMARY, WHY, RISK, VERIFIED, and every doc comment or
|
|
44
29
|
PLANS.md/BUGS.md line you write are claims about the code.
|
|
@@ -47,7 +32,7 @@ claim gets it rejected; a small, correct change is welcome:
|
|
|
47
32
|
- Never state test or suite counts: say what you ran and what you saw.
|
|
48
33
|
- Verify after your LAST edit, backlog files included: an edit after the last green run is
|
|
49
34
|
unverified.`;
|
|
50
|
-
/** Sentinel a
|
|
35
|
+
/** Sentinel a role's pi run outputs when it found nothing worth doing (REPLY_ENDINGS below
|
|
51
36
|
* instructs it). */
|
|
52
37
|
export const NOTHING_TO_DO = "TUMWATER_NOTHING_TO_DO";
|
|
53
38
|
/** True when `text` declares there was nothing to do (src/pi/pi-stream.ts's stream parser scans
|
|
@@ -55,12 +40,12 @@ export const NOTHING_TO_DO = "TUMWATER_NOTHING_TO_DO";
|
|
|
55
40
|
export function isNothingToDo(text) {
|
|
56
41
|
return text.includes(NOTHING_TO_DO);
|
|
57
42
|
}
|
|
58
|
-
/** Sentinel a
|
|
43
|
+
/** Sentinel a role's pi run outputs when it declines its task (see plans/refusal-and-thrash.md):
|
|
59
44
|
* `TUMWATER_REFUSED: <one-line reason>`. The reason is the durable objection — it becomes the
|
|
60
|
-
* commit subject of the refusal note and the
|
|
45
|
+
* commit subject of the refusal note and the run's lastSummary. */
|
|
61
46
|
export const REFUSED_SENTINEL = "TUMWATER_REFUSED";
|
|
62
47
|
/** The four-line SUMMARY/WHY/RISK/VERIFIED block itself — the machine-parsed half of the
|
|
63
|
-
* closing contract, shared verbatim by REPLY_ENDINGS (
|
|
48
|
+
* closing contract, shared verbatim by REPLY_ENDINGS (run, director, and resume prompts) and
|
|
64
49
|
* prompt-followup.ts's buildSummaryRequestPrompt (the follow-up that recovers a missing block),
|
|
65
50
|
* so they cannot drift (sibling of the NOTHING_TO_DO sentinel below, and parsed field-by-field
|
|
66
51
|
* by git/commit-message.ts's labeledLine). */
|
|
@@ -70,10 +55,10 @@ export const SUMMARY_BLOCK = ` SUMMARY: <imperative one-line description of the
|
|
|
70
55
|
VERIFIED: <what you ran and observed beyond the suite total (tumwater attests the counts), e.g. "the project's check; repro script showed X before, Y after" — write none when nothing was run>`;
|
|
71
56
|
/** The reply contract's closing rule: the three mutually exclusive ways a run ends, with the
|
|
72
57
|
* exact SUMMARY/WHY/RISK/VERIFIED block git/commit-message.ts parses into the commit message as the
|
|
73
|
-
* last one. Shared by the
|
|
58
|
+
* last one. Shared by the run and director rules (prompt.ts's commonRules) and
|
|
74
59
|
* prompt-followup.ts's resume bridge, so a resumed run ends under the same contract as a fresh
|
|
75
60
|
* one. Written as an either/or list because, stated as separate rules, models filled in every
|
|
76
|
-
* one: 56 team
|
|
61
|
+
* one: 56 team runs ended a SUMMARY block with the nothing sentinel too, and
|
|
77
62
|
* `TUMWATER_REFUSED: none` on completed work once made the harness discard it. The nothing
|
|
78
63
|
* ending says "end your reply with", not "reply with the single line", so a run that must
|
|
79
64
|
* report something first (the director's answer to a question, qa's FLOW line) can. */
|
|
@@ -110,7 +95,7 @@ export function labeledLine(text, label) {
|
|
|
110
95
|
* exists. Anchored at line start like the VERDICT line, so prose that merely mentions the
|
|
111
96
|
* sentinel mid-sentence cannot set the reason. THE ONLY source of a refusal (BUGS.md
|
|
112
97
|
* 2026-09-23): a bare sentinel and a mid-sentence mention are not refusals, and a reason
|
|
113
|
-
* that negates the refusal is not one either (isNegatedRefusal) — four
|
|
98
|
+
* that negates the refusal is not one either (isNegatedRefusal) — four runs ended ordinary
|
|
114
99
|
* work-completed replies with `TUMWATER_REFUSED: none` and the harness destroyed their
|
|
115
100
|
* tested work. */
|
|
116
101
|
export function extractRefusal(text) {
|
|
@@ -126,7 +111,7 @@ export function extractRefusal(text) {
|
|
|
126
111
|
* alike, each of ‘ ’ “ ” decorating either side of the token (a model's quote direction
|
|
127
112
|
* is not reliable, so both classes carry all four);
|
|
128
113
|
* - the whole reason itself stating absence: `nothing to do`, `nothing to refuse`,
|
|
129
|
-
* `nothing refused`, `no refusal` — a
|
|
114
|
+
* `nothing refused`, `no refusal` — a role that found no work reaches for these phrases
|
|
130
115
|
* exactly as it reaches for `none`, and they are not objections (BUGS.md 2026-09-28);
|
|
131
116
|
* - with trailing sentence punctuation: `None.`, `N/A!`, `none…` — punctuation is formatting
|
|
132
117
|
* (the one-character ellipsis beside its ASCII dots);
|
|
@@ -172,7 +157,7 @@ export function isNegatedRefusal(reason) {
|
|
|
172
157
|
// `none "nothing to do"`, `none “nothing to do”`). The hyphen stays in the class beside the
|
|
173
158
|
// en/em dashes: `none - …` is the same appended-note dress as `none — …` and dropping it
|
|
174
159
|
// would regress `n/a - …` replies to genuine refusals. The alternation also carries the
|
|
175
|
-
// whole-reason absence statements a
|
|
160
|
+
// whole-reason absence statements a role writes instead of `none` (`nothing to do`, `nothing to
|
|
176
161
|
// refuse`, `nothing refused`, `no refusal` — same negating intent, BUGS.md 2026-09-28).
|
|
177
162
|
// Anchored right after the token, so a reason that continues with a word (`none of the
|
|
178
163
|
// attempted fixes work`, `nothing to do with the review`, `no refusal of my own`) stays a
|
|
@@ -184,7 +169,7 @@ export function isNegatedRefusal(reason) {
|
|
|
184
169
|
}
|
|
185
170
|
return false;
|
|
186
171
|
}
|
|
187
|
-
/** Extract the `qa`
|
|
172
|
+
/** Extract the `qa` run's result-carrying `FLOW: <name> — <passed|bug>` line (plans/observer-
|
|
188
173
|
* roles.md 2/2); null when absent. Built on `labeledLine`, so the label is anchored at line
|
|
189
174
|
* start and a mid-sentence mention is ignored. The verdict is required: a bare `FLOW: <name>`
|
|
190
175
|
* — or a reply truncated mid-verdict (`FLOW: gui-budget-cap — pa`) — is not a result, and
|
|
@@ -200,6 +185,32 @@ export function extractFlow(text) {
|
|
|
200
185
|
return null;
|
|
201
186
|
return { flow: match[1].trim(), result: match[2].toLowerCase() };
|
|
202
187
|
}
|
|
188
|
+
/** The shortest a filed title may be to count as a declared result. A truncated `FILED:` line
|
|
189
|
+
* cuts the title off mid-word (`FILED: #12 — tit` where the run meant `title`); requiring a
|
|
190
|
+
* title of at least this many characters keeps a torn line from being latched as a filing the
|
|
191
|
+
* run never completed, while real titles — the charter composes them from a cluster key and its
|
|
192
|
+
* count — are far longer. */
|
|
193
|
+
const MIN_FILED_TITLE_LENGTH = 4;
|
|
194
|
+
/** Extract the `feedback` run's result-carrying `FILED: #<n> — <title>` line (plans/feedback-
|
|
195
|
+
* role.md 2/2); null when absent. Built on `labeledLine`, so the label is anchored at line
|
|
196
|
+
* start and a mid-sentence mention is ignored. The issue number and a non-empty, non-truncated
|
|
197
|
+
* title are required: a bare `FILED:`, a line with no number (`FILED: title`), or a reply
|
|
198
|
+
* truncated mid-title (`FILED: #12 — tit`) is not a filing, and returning null leaves the
|
|
199
|
+
* per-install ledger unadvanced rather than latching a record the run never declared (the same
|
|
200
|
+
* lesson as qa's FLOW line, BUGS.md 2026-09-23). Splits on the dash/em-dash token before the
|
|
201
|
+
* title so a title that itself contains a hyphen stays intact. */
|
|
202
|
+
export function extractFiled(text) {
|
|
203
|
+
const value = labeledLine(text, "FILED");
|
|
204
|
+
if (!value)
|
|
205
|
+
return null;
|
|
206
|
+
const match = /^#(\d+)\s*[—-]\s*(.+)$/.exec(value);
|
|
207
|
+
if (!match?.[1] || !match[2])
|
|
208
|
+
return null;
|
|
209
|
+
const title = match[2].trim();
|
|
210
|
+
if (title.length < MIN_FILED_TITLE_LENGTH)
|
|
211
|
+
return null;
|
|
212
|
+
return { issue: Number(match[1]), title };
|
|
213
|
+
}
|
|
203
214
|
// The review gate's verdict line as stated in buildReviewPrompt (prompt.ts): the reviewer
|
|
204
215
|
// ends with exactly `VERDICT: approve` or `VERDICT: reject`. Anchored at line start so prose
|
|
205
216
|
// that merely mentions "VERDICT:" mid-sentence cannot set the outcome, but leading horizontal
|
package/dist/src/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tumwater",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Opinionated autonomous development harness built on pi. You write the initial prompt; role-driven loops build the project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"test:coverage": "node scripts/live-checkout-guard.mjs && tsc --incremental && node scripts/stamp-build.mjs && node dist/test/test-runner.js --coverage",
|
|
44
44
|
"smoke:claude": "node scripts/smoke-claude.mjs",
|
|
45
45
|
"smoke:opencode": "node scripts/smoke-opencode.mjs",
|
|
46
|
+
"smoke:codex": "node scripts/smoke-codex.mjs",
|
|
46
47
|
"tumwater": "node scripts/tumwater.mjs"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { gitTry } from "../git/git-run.js";
|
|
2
|
-
import { resetWorktreeToMain } from "../git/worktree.js";
|
|
3
|
-
import { conflictedFiles } from "../merge/merge-git.js";
|
|
4
|
-
/** The persistence count at which one repeated objection makes a rejection final
|
|
5
|
-
* (plans/revise-rejected.md; the repeat-finality policy in BUGS.md 2026-10-14): each numbered
|
|
6
|
-
* reason records how many consecutive revisions it has survived — a new (untagged) objection is
|
|
7
|
-
* 0, a `(prior N)` restatement is 1 plus the prior round's count for that objection — and when
|
|
8
|
-
* any reason reaches this count it was raised and then survived two revisions, so the change has
|
|
9
|
-
* stopped converging and the rejection is final. A converging chain keeps revising while each
|
|
10
|
-
* round's objections differ. */
|
|
11
|
-
export const REVISION_FINAL_ON_REPEAT = 2;
|
|
12
|
-
/** The hard backstop on one change's revision chain (plans/revise-rejected.md; BUGS.md
|
|
13
|
-
* 2026-10-14): at this many total rejections the rejection is final even when the objections
|
|
14
|
-
* never repeat, so a non-converging author cannot revise forever. Replaces the old fixed
|
|
15
|
-
* `REVISION_LIMIT = 2`, which ended a converging chain at its third rejection whenever each
|
|
16
|
-
* round raised a new objection. */
|
|
17
|
-
export const REVISION_BACKSTOP = 6;
|
|
18
|
-
/** Re-apply a rejected change `sha` onto current main as uncommitted edits in `wt`
|
|
19
|
-
* (plans/revise-rejected.md): the author's next tick starts from the rejected diff instead of
|
|
20
|
-
* re-deriving it. `git cherry-pick --no-commit <merge-base>..<sha>` leaves the edits in the
|
|
21
|
-
* worktree without committing. On any git failure — most often a conflict with what main merged
|
|
22
|
-
* since the rejection, but also a missing object — the cherry-pick is aborted and the worktree
|
|
23
|
-
* reset to main, and false is returned so the caller falls back to a plain rejection note. */
|
|
24
|
-
export async function applyRevision(wt, mainBranch, sha) {
|
|
25
|
-
const base = await gitTry(wt, "merge-base", mainBranch, sha);
|
|
26
|
-
if (base === null) {
|
|
27
|
-
await resetWorktreeToMain(wt, mainBranch);
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
const picked = await gitTry(wt, "cherry-pick", "--no-commit", `${base}..${sha}`);
|
|
31
|
-
if (picked === null) {
|
|
32
|
-
await gitTry(wt, "cherry-pick", "--abort");
|
|
33
|
-
await resetWorktreeToMain(wt, mainBranch);
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
/** Re-apply a handed-back change's diff onto current main with the conflict markers left in
|
|
39
|
-
* place as ordinary uncommitted edits, so its author can resolve them (PLANS.md "Robust
|
|
40
|
-
* conflict landing, part 2/2"). Like applyRevision it cherry-picks
|
|
41
|
-
* `<merge-base>..<sha> --no-commit` onto the worktree's current main. A conflicted pick is NOT
|
|
42
|
-
* aborted: the unmerged paths are read off the index, a mixed `git reset` drops the index state
|
|
43
|
-
* so the marker-bearing files are plain working-tree edits the tick can commit normally, and
|
|
44
|
-
* `{ applied: true, conflicted }` is returned. A failure with no unmerged paths (a missing
|
|
45
|
-
* object, a bad revision) aborts, resets to main, and returns `{ applied: false }` so the
|
|
46
|
-
* caller falls back to a fresh start. Never throws for a git-level failure. */
|
|
47
|
-
export async function applyWithConflicts(wt, mainBranch, sha) {
|
|
48
|
-
const base = await gitTry(wt, "merge-base", mainBranch, sha);
|
|
49
|
-
if (base === null) {
|
|
50
|
-
await resetWorktreeToMain(wt, mainBranch);
|
|
51
|
-
return { applied: false, conflicted: [] };
|
|
52
|
-
}
|
|
53
|
-
const picked = await gitTry(wt, "cherry-pick", "--no-commit", `${base}..${sha}`);
|
|
54
|
-
// conflictedFiles, not a raw diff: it decodes git's C-quoted names, so a non-ASCII
|
|
55
|
-
// conflicted path is the on-disk name the prompt and hasConflictMarkers can use.
|
|
56
|
-
const conflicted = await conflictedFiles(wt);
|
|
57
|
-
if (conflicted.length > 0) {
|
|
58
|
-
// Keep the marker-bearing files, drop only the index's unmerged stage entries: a mixed
|
|
59
|
-
// reset leaves the working tree untouched, so the markers are uncommitted edits the tick
|
|
60
|
-
// stages and commits like any other author work.
|
|
61
|
-
await gitTry(wt, "reset");
|
|
62
|
-
return { applied: true, conflicted };
|
|
63
|
-
}
|
|
64
|
-
if (picked === null) {
|
|
65
|
-
await gitTry(wt, "cherry-pick", "--abort");
|
|
66
|
-
await resetWorktreeToMain(wt, mainBranch);
|
|
67
|
-
return { applied: false, conflicted: [] };
|
|
68
|
-
}
|
|
69
|
-
return { applied: true, conflicted: [] };
|
|
70
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/** Loop-id arithmetic for parallel work instances (plans/parallel-work-instances.md, part 1/7). A
|
|
2
|
-
* base role may run several loop ids — `feature`, `feature-2`, `bugfix-3`, … — each keying
|
|
3
|
-
* its own persisted state, branch, refs, session and notebook, while every lookup that is ABOUT the
|
|
4
|
-
* role (its catalog charter, model, tier, caps, scheduling class) resolves through the base role.
|
|
5
|
-
* With no instances configured, every loop id is bare, so `baseRoleOf` is the identity.
|
|
6
|
-
*
|
|
7
|
-
* Only `feature` and `bugfix` may gain instances; any other id is returned unchanged, including a
|
|
8
|
-
* custom loop whose name merely resembles an instance. */
|
|
9
|
-
/** The base roles that may run more than one instance. */
|
|
10
|
-
export const INSTANCE_ROLES = new Set(["feature", "bugfix"]);
|
|
11
|
-
/** The instance-id shape: a base role, a dash, and an index of 2 or more (`-1` is not an
|
|
12
|
-
* instance — the bare id already means instance 1). The source string is also interpolated into
|
|
13
|
-
* the dashboard script (gui-client-labels.ts). */
|
|
14
|
-
export const INSTANCE_ID_SOURCE = "^(feature|bugfix)-([2-9]|[1-9][0-9]+)$";
|
|
15
|
-
const INSTANCE_RE = new RegExp(INSTANCE_ID_SOURCE);
|
|
16
|
-
/** The base role a loop id belongs to: `feature-2` → `feature`, `bugfix-10` → `bugfix`; any
|
|
17
|
-
* other id — a bare role, a custom loop, `feature-1` — is returned unchanged. */
|
|
18
|
-
export function baseRoleOf(id) {
|
|
19
|
-
const m = INSTANCE_RE.exec(id);
|
|
20
|
-
return m ? m[1] : id;
|
|
21
|
-
}
|
|
22
|
-
/** The 1-based instance number of a loop id: 1 for a bare role or any non-instance id, N for
|
|
23
|
-
* `<role>-N`. */
|
|
24
|
-
export function instanceIndex(id) {
|
|
25
|
-
const m = INSTANCE_RE.exec(id);
|
|
26
|
-
return m ? Number(m[2]) : 1;
|
|
27
|
-
}
|
|
28
|
-
/** The instance count configured for a loop id's base role (`roles.<base>.instances`, default
|
|
29
|
-
* 1). A caller tells a multi-instance base (whose unassigned ticks stage claims for their
|
|
30
|
-
* siblings) from a single-runner one by this. */
|
|
31
|
-
export function configuredInstances(config, id) {
|
|
32
|
-
const role = config.roles[baseRoleOf(id)];
|
|
33
|
-
return role?.instances ?? 1;
|
|
34
|
-
}
|
|
35
|
-
/** Is this loop id enabled? True when its base role is enabled in config and the id's instance
|
|
36
|
-
* index is within that role's configured instance count (`roles.<id>.instances`, default 1). */
|
|
37
|
-
export function loopEnabled(config, id) {
|
|
38
|
-
const role = config.roles[baseRoleOf(id)];
|
|
39
|
-
if (role?.enabled !== true)
|
|
40
|
-
return false;
|
|
41
|
-
return instanceIndex(id) <= configuredInstances(config, id);
|
|
42
|
-
}
|
|
43
|
-
/** Every loop id the config says should exist, in `config.roles` order: each enabled role's
|
|
44
|
-
* bare id, then `<id>-2`…`<id>-N` for the `INSTANCE_ROLES` at their configured instance
|
|
45
|
-
* count. Other roles and custom loops stay bare — only feature and bugfix may split. The one
|
|
46
|
-
* answer to "which runners should exist", so the orchestrator (part 5b/7) and live reload
|
|
47
|
-
* enumerate the same set. At the default instance count this returns exactly the bare ids
|
|
48
|
-
* `enabledRoleIds` returns. */
|
|
49
|
-
export function loopIds(config) {
|
|
50
|
-
const ids = [];
|
|
51
|
-
for (const [id, role] of Object.entries(config.roles)) {
|
|
52
|
-
if (!role.enabled)
|
|
53
|
-
continue;
|
|
54
|
-
ids.push(id);
|
|
55
|
-
if (!INSTANCE_ROLES.has(id))
|
|
56
|
-
continue;
|
|
57
|
-
const count = role.instances ?? 1;
|
|
58
|
-
for (let i = 2; i <= count; i++)
|
|
59
|
-
ids.push(`${id}-${i}`);
|
|
60
|
-
}
|
|
61
|
-
return ids;
|
|
62
|
-
}
|
|
63
|
-
/** The pause-marker key for a loop id. Instance 1's loop id is the bare role id, which as a pause
|
|
64
|
-
* key would mean the whole role, so while the role runs several instances instance 1 pauses under
|
|
65
|
-
* `<role>-1`. `-1` is a pause key only, never a loop id. Every other id is its own key. */
|
|
66
|
-
export function instancePauseKey(config, loopId) {
|
|
67
|
-
return INSTANCE_ROLES.has(loopId) && configuredInstances(config, loopId) > 1
|
|
68
|
-
? `${loopId}-1`
|
|
69
|
-
: loopId;
|
|
70
|
-
}
|
|
71
|
-
/** Does the per-role pause marker stop this loop id? True for its own id, its base role's id (a
|
|
72
|
-
* whole-role pause), or — for the bare id of an instance role — the `<role>-1` pause key. */
|
|
73
|
-
export function loopPaused(paused, loopId) {
|
|
74
|
-
return (paused.has(loopId) ||
|
|
75
|
-
paused.has(baseRoleOf(loopId)) ||
|
|
76
|
-
(INSTANCE_ROLES.has(loopId) && paused.has(`${loopId}-1`)));
|
|
77
|
-
}
|