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,7 +1,7 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import {
|
|
3
|
+
import { errCode } from "../errno.js";
|
|
4
|
+
import { isJsonObject } from "../files/json-object.js";
|
|
5
5
|
import { readJsonFile } from "../files/json-files.js";
|
|
6
6
|
import { changeBaseRev, fileContentAt } from "../git/git.js";
|
|
7
7
|
import { piModelsPath } from "../pi/pi-models.js";
|
|
@@ -32,9 +32,6 @@ export function secretEnvValues(env) {
|
|
|
32
32
|
}
|
|
33
33
|
return out;
|
|
34
34
|
}
|
|
35
|
-
function isRecord(v) {
|
|
36
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
37
|
-
}
|
|
38
35
|
/** The credentials pi stores: literal `apiKey`s and header values in `models.json`, and the
|
|
39
36
|
* `key`/`access`/`refresh` fields (plus secret-named `env` values) of the sibling `auth.json`.
|
|
40
37
|
* `$NAME` interpolations (secretEnvValues covers those) and `!command` values are skipped. A
|
|
@@ -48,12 +45,12 @@ export function piCredentialValues(modelsPath = piModelsPath()) {
|
|
|
48
45
|
out.push({ name, value });
|
|
49
46
|
};
|
|
50
47
|
const models = readJsonFile(modelsPath);
|
|
51
|
-
if (models &&
|
|
48
|
+
if (models && isJsonObject(models.providers)) {
|
|
52
49
|
for (const [p, cfg] of Object.entries(models.providers)) {
|
|
53
|
-
if (!
|
|
50
|
+
if (!isJsonObject(cfg))
|
|
54
51
|
continue;
|
|
55
52
|
add(`models.json ${p}.apiKey`, cfg.apiKey);
|
|
56
|
-
if (
|
|
53
|
+
if (isJsonObject(cfg.headers)) {
|
|
57
54
|
for (const [h, v] of Object.entries(cfg.headers))
|
|
58
55
|
add(`models.json ${p}.headers.${h}`, v);
|
|
59
56
|
}
|
|
@@ -62,11 +59,11 @@ export function piCredentialValues(modelsPath = piModelsPath()) {
|
|
|
62
59
|
const auth = readJsonFile(path.join(path.dirname(modelsPath), "auth.json"));
|
|
63
60
|
if (auth) {
|
|
64
61
|
for (const [p, cred] of Object.entries(auth)) {
|
|
65
|
-
if (!
|
|
62
|
+
if (!isJsonObject(cred))
|
|
66
63
|
continue;
|
|
67
64
|
for (const f of ["key", "access", "refresh"])
|
|
68
65
|
add(`auth.json ${p}.${f}`, cred[f]);
|
|
69
|
-
if (
|
|
66
|
+
if (isJsonObject(cred.env)) {
|
|
70
67
|
for (const [n, v] of Object.entries(cred.env)) {
|
|
71
68
|
if (SECRET_NAME.test(n))
|
|
72
69
|
add(`auth.json ${p}.env.${n}`, v);
|
|
@@ -85,12 +82,21 @@ export function knownSecrets(env = process.env, modelsPath = piModelsPath()) {
|
|
|
85
82
|
];
|
|
86
83
|
return all.filter((s, i) => all.findIndex((o) => o.value === s.value) === i);
|
|
87
84
|
}
|
|
85
|
+
/** Whether `err` says the path holds no file (deleted, or a parent replaced). */
|
|
86
|
+
function noFile(err) {
|
|
87
|
+
const code = errCode(err);
|
|
88
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
89
|
+
}
|
|
90
|
+
/** The finding for a changed file the gate could not read. */
|
|
91
|
+
function unreadable(file, err) {
|
|
92
|
+
return `\`${file}\` could not be read to check for leaked secrets (${errCode(err) ?? "error"})`;
|
|
93
|
+
}
|
|
88
94
|
/** The deterministic gate/stage finding for a change that writes the value of a secret
|
|
89
95
|
* environment variable or stored pi credential into a file (a Node diagnostic report dumps
|
|
90
96
|
* the whole environment). A value already present in the file at the merge-base is not this
|
|
91
97
|
* change's leak. The reason
|
|
92
|
-
* names files and variables, never the value. undefined when nothing leaked
|
|
93
|
-
*
|
|
98
|
+
* names files and variables, never the value. undefined when nothing leaked. A changed file
|
|
99
|
+
* that exists but cannot be read counts as a finding; a deleted file does not. */
|
|
94
100
|
export async function secretLeakReason(wt, mainBranch, files, secrets = knownSecrets()) {
|
|
95
101
|
if (secrets.length === 0)
|
|
96
102
|
return undefined;
|
|
@@ -100,16 +106,30 @@ export async function secretLeakReason(wt, mainBranch, files, secrets = knownSec
|
|
|
100
106
|
const full = path.join(wt, file);
|
|
101
107
|
let stat;
|
|
102
108
|
try {
|
|
103
|
-
stat
|
|
109
|
+
// lstat, not stat: a symlink in the change set is not followed. The gate scans the
|
|
110
|
+
// content each change writes, and a change that adds a symlink writes no content — its
|
|
111
|
+
// target (which lstat's isFile() leaves unreadable, unlike stat's, which follows it out
|
|
112
|
+
// of the worktree) is not what the change put there. Following it would read an
|
|
113
|
+
// arbitrary host path a malicious repo can point a link at, and would flag that outside
|
|
114
|
+
// file's contents as this change's leak.
|
|
115
|
+
stat = fs.lstatSync(full);
|
|
104
116
|
}
|
|
105
|
-
catch {
|
|
117
|
+
catch (err) {
|
|
118
|
+
if (!noFile(err))
|
|
119
|
+
leaks.push(unreadable(file, err));
|
|
106
120
|
continue;
|
|
107
121
|
}
|
|
108
122
|
if (!stat.isFile() || stat.size > MAX_SCAN_BYTES)
|
|
109
123
|
continue;
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
let head;
|
|
125
|
+
try {
|
|
126
|
+
head = fs.readFileSync(full, "utf8");
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (!noFile(err))
|
|
130
|
+
leaks.push(unreadable(file, err));
|
|
112
131
|
continue;
|
|
132
|
+
}
|
|
113
133
|
const hits = secrets.filter((s) => head.includes(s.value));
|
|
114
134
|
if (hits.length === 0)
|
|
115
135
|
continue;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
/** Detecting a reviewer run that re-ran the full suite on a tree the gate's pre-check already
|
|
3
2
|
* verified — pure string logic over the bash command lines a pi run started, no subprocess or
|
|
4
3
|
* file I/O. The review prompt tells such a reviewer not to (src/prompt/gate-prompts.ts), but on
|
|
@@ -1,23 +1,56 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { readJsonFile, writeJsonAtomic } from "../files/json-files.js";
|
|
2
3
|
import { deleteRefIfAt, refSha } from "../git/git.js";
|
|
3
4
|
import { gitTry, runGit } from "../git/git-run.js";
|
|
4
5
|
import { branchName, mergeRefName, rejectedRefName, reviewSessionDir, roleInboxDir, roleNotesPath, sessionDir, statePath } from "../paths.js";
|
|
5
6
|
import { errorMessage } from "../text/text.js";
|
|
6
7
|
import { LEGACY_ROLE_IDS } from "./legacy-role-ids.js";
|
|
8
|
+
/** Rewrite a JSON state file's `role` field to the id its file name now names; a non-JSON file
|
|
9
|
+
* (or one without a `role` field) is left untouched. `role` is the one field that must match
|
|
10
|
+
* the file name — saveRunnerState writes `statePath(root, state.role)` — so a file moved by
|
|
11
|
+
* migration must not keep its old id inside or it would be saved back to the retired file. */
|
|
12
|
+
function rewriteStateRole(file, role) {
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
15
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
16
|
+
return;
|
|
17
|
+
const rec = parsed;
|
|
18
|
+
if (rec.role === role)
|
|
19
|
+
return;
|
|
20
|
+
rec.role = role;
|
|
21
|
+
// writeJsonAtomic, not a plain writeFileSync like every other production state writer
|
|
22
|
+
// (INV-AF4): the rewritten file is a role's runner state, and a tmp+rename keeps a crash
|
|
23
|
+
// mid-write (or a concurrent reader) from ever seeing a torn file. A migration that dies
|
|
24
|
+
// before the rename leaves the target at its pre-rewrite bytes — retried next startup —
|
|
25
|
+
// never a truncated state file the runner would read as no state.
|
|
26
|
+
writeJsonAtomic(file, rec);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* not a JSON state file — leave it alone */
|
|
30
|
+
}
|
|
31
|
+
}
|
|
7
32
|
/** Move each retired role id's branch, refs and files to the new id (plans/terminology.md "Role
|
|
8
|
-
* names"). Returns one line per artifact
|
|
9
|
-
*
|
|
33
|
+
* names"). Returns one line per artifact, sorted into moved, kept (both
|
|
34
|
+
* ids exist) and failed. State files are the one exception to "kept": when both id-named copies
|
|
35
|
+
* exist and the old-id copy carries the newer end-of-run stamp (`lastRunEndedAt`, or the
|
|
36
|
+
* pre-rename `lastTickEndedAt` a frozen copy carries), that copy is folded over the stale
|
|
37
|
+
* new-name one under the new name (the earlier migration renamed only the file list, not the
|
|
38
|
+
* `role` field inside, so the runner kept writing the old-id name and the two copies drifted —
|
|
39
|
+
* BUGS.md 2026-10-23); otherwise both are left in place. Nothing other than a state file that
|
|
40
|
+
* exists under the new id is overwritten, each artifact fails alone, and pi and native logs stay
|
|
41
|
+
* put as history. */
|
|
10
42
|
export async function migrateLegacyRoleIds(root, legacy = LEGACY_ROLE_IDS) {
|
|
11
|
-
const notes = [];
|
|
43
|
+
const notes = { moved: [], kept: [], failed: [] };
|
|
12
44
|
for (const [oldId, newId] of Object.entries(legacy)) {
|
|
13
45
|
const attempt = async (what, move) => {
|
|
14
46
|
try {
|
|
15
47
|
const note = await move();
|
|
16
|
-
if (note
|
|
17
|
-
|
|
48
|
+
if (note === null)
|
|
49
|
+
return;
|
|
50
|
+
(note.startsWith("kept ") ? notes.kept : notes.moved).push(note);
|
|
18
51
|
}
|
|
19
52
|
catch (err) {
|
|
20
|
-
notes.push(`could not move ${what}: ${errorMessage(err)}`);
|
|
53
|
+
notes.failed.push(`could not move ${what}: ${errorMessage(err)}`);
|
|
21
54
|
}
|
|
22
55
|
};
|
|
23
56
|
await attempt("branch", async () => {
|
|
@@ -45,7 +78,47 @@ export async function migrateLegacyRoleIds(root, legacy = LEGACY_ROLE_IDS) {
|
|
|
45
78
|
return `moved ${from} to ${to}`;
|
|
46
79
|
});
|
|
47
80
|
}
|
|
48
|
-
|
|
81
|
+
// State files get a one-time heal, not "kept": when both id-named copies exist (a state
|
|
82
|
+
// object the earlier migration renamed without rewriting `role`, so the runner kept saving
|
|
83
|
+
// under the old-id name and the two files drifted), fold them into one — when the old-id copy
|
|
84
|
+
// is the newer one, keep it under the new name and drop the stale new-name copy. A live copy
|
|
85
|
+
// carries `lastRunEndedAt`; a frozen copy (never saved since the earlier rename) carries only
|
|
86
|
+
// the pre-rename `lastTickEndedAt`, so the stamp reads either. When either copy is not a state
|
|
87
|
+
// object (unreadable/torn, or a non-object), their stamps tie, or the new-name copy is the
|
|
88
|
+
// newer one, keep both untouched, so the heal never fabricates or destroys a file it does not
|
|
89
|
+
// understand.
|
|
90
|
+
await attempt(`state ${statePath(root, oldId)}`, async () => {
|
|
91
|
+
const from = statePath(root, oldId);
|
|
92
|
+
const to = statePath(root, newId);
|
|
93
|
+
if (!fs.existsSync(from))
|
|
94
|
+
return null;
|
|
95
|
+
if (!fs.existsSync(to)) {
|
|
96
|
+
fs.renameSync(from, to);
|
|
97
|
+
rewriteStateRole(to, newId);
|
|
98
|
+
return `moved ${from} to ${to}`;
|
|
99
|
+
}
|
|
100
|
+
const stamp = (p) => {
|
|
101
|
+
const j = readJsonFile(p);
|
|
102
|
+
if (typeof j?.lastRunEndedAt === "number")
|
|
103
|
+
return j.lastRunEndedAt;
|
|
104
|
+
return typeof j?.lastTickEndedAt === "number" ? j.lastTickEndedAt : -2; // retired-name-ok: legacy state field the frozen copy carries
|
|
105
|
+
};
|
|
106
|
+
const oldStamp = stamp(from);
|
|
107
|
+
const newStamp = stamp(to);
|
|
108
|
+
if (oldStamp < 0 || newStamp < 0 || oldStamp === newStamp)
|
|
109
|
+
return `kept ${from} and ${to}: both exist`;
|
|
110
|
+
if (oldStamp < newStamp)
|
|
111
|
+
return `kept ${from} and ${to}: both exist`;
|
|
112
|
+
// The old-id file holds the newer state (the live runner kept saving under the old name
|
|
113
|
+
// after the earlier migration). Fold it into the new-name path atomically: renameSync
|
|
114
|
+
// replaces the destination and removes the source in one step, so a crash between the two
|
|
115
|
+
// cannot leave the new-name file torn over the still-present old copy (which would read as
|
|
116
|
+
// -2 next time and wedge the heal in "kept").
|
|
117
|
+
fs.renameSync(from, to);
|
|
118
|
+
rewriteStateRole(to, newId);
|
|
119
|
+
return `healed ${from} and ${to}: kept ${from}, newer`;
|
|
120
|
+
});
|
|
121
|
+
for (const pathFor of [roleNotesPath, roleInboxDir, sessionDir, reviewSessionDir]) {
|
|
49
122
|
const from = pathFor(root, oldId);
|
|
50
123
|
const to = pathFor(root, newId);
|
|
51
124
|
await attempt(from, async () => {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
/** The role catalog: one built-in loop's identity and its role-specific "find something to do"
|
|
1
|
+
/** The role catalog: one built-in role's identity and its role-specific "find something to do"
|
|
3
2
|
* instruction text. Split out of roles.ts (2026-10-01, organize) so the catalog's embedded
|
|
4
3
|
* prompt prose lives apart from the derived registries and classification helpers, which stayed
|
|
5
4
|
* in roles.ts. Pure catalog data — the shared instruction fragments role prompts embed live in
|
|
6
5
|
* role-guidance.ts (import-only downward, so prompt assembly depends on both without a cycle).
|
|
7
6
|
* roles.ts re-exports ROLES and Role, so every existing importer keeps its import path. */
|
|
8
|
-
import { backlogMoveGuidance, DECOMPOSITION_GUIDANCE, NEEDS_REPLAN_NOTE, NEEDS_REVIEW_NOTE, PLAN_SIZING, searchGuidance, VALIDATION_GAP_GUIDANCE, VALIDATION_GAP_QUERY, VALIDATION_GAP_TALLY, WAITING_FOR_FAILURE_NOTE, WAITING_FOR_FAILURE_PREFIX, } from "./role-guidance.js";
|
|
7
|
+
import { backlogMoveGuidance, DECOMPOSITION_GUIDANCE, HELD_TAG_PREFIX, NEEDS_REPLAN_NOTE, NEEDS_REVIEW_NOTE, PLAN_QUEUE_GUIDANCE, PLAN_SIZING, PRIOR_HELD_PREFIX, searchGuidance, VALIDATION_GAP_GUIDANCE, VALIDATION_GAP_QUERY, VALIDATION_GAP_TALLY, WAITING_FOR_FAILURE_NOTE, WAITING_FOR_FAILURE_PREFIX, } from "./role-guidance.js";
|
|
9
8
|
import { roleCommitGrep } from "./legacy-role-ids.js";
|
|
10
9
|
import { NOTHING_TO_DO } from "../verdict/reply-contract.js";
|
|
11
10
|
/** The trailer the readme role's commits carry and its `git log --grep` reads back; git parses
|
|
@@ -14,10 +13,10 @@ import { NOTHING_TO_DO } from "../verdict/reply-contract.js";
|
|
|
14
13
|
const README_TRAILER = "Run: readme #";
|
|
15
14
|
const README_TRAILER_GREP = "^(Run|Tick): readme #"; // vocabulary-ok: legacy name read from old refs or commits
|
|
16
15
|
const README_TRAILER_LEGACY = "Tick: readme #"; // vocabulary-ok: legacy name read from old refs or commits
|
|
17
|
-
/** The opinionated role catalog. Every
|
|
18
|
-
* the role-specific "find something to do" half of the
|
|
16
|
+
/** The opinionated role catalog. Every role has one job; a role's `find` text is
|
|
17
|
+
* the role-specific "find something to do" half of the run prompt.
|
|
19
18
|
*
|
|
20
|
-
* Order matters: it is the scheduling priority when
|
|
19
|
+
* Order matters: it is the scheduling priority when roles are otherwise tied
|
|
21
20
|
* (e.g. the startup burst), so shipping work (feature, bugfix) outranks hygiene. */
|
|
22
21
|
export const ROLES = [
|
|
23
22
|
{
|
|
@@ -37,12 +36,18 @@ export const ROLES = [
|
|
|
37
36
|
4. A plan too large to finish in this run is not split by you: append the note
|
|
38
37
|
${NEEDS_REVIEW_NOTE}
|
|
39
38
|
under its heading, skip it, and implement the next available plan that fits. You may mark
|
|
40
|
-
several oversized entries while scanning, but land exactly one plan.
|
|
39
|
+
several oversized entries while scanning, but land exactly one plan. The held tag you write
|
|
40
|
+
— \`${HELD_TAG_PREFIX}<N>)\` on a Needs-review note, \`${HELD_TAG_PREFIX}<H>)\` on a
|
|
41
|
+
Needs-replan note — is ONE past the count already on the entry: read your own or an earlier
|
|
42
|
+
mark's \`${HELD_TAG_PREFIX}N)\` tag, else the \`${PRIOR_HELD_PREFIX}<N>**\` line a lift left
|
|
43
|
+
behind, else start at 1 — never reset it while the same plan ping-pongs, so three holds
|
|
44
|
+
without a merge reach \`${HELD_TAG_PREFIX}3)\`. A plan that merged and left \`## Planned\`
|
|
45
|
+
carries no marker — the convergence backstop starts fresh there.
|
|
41
46
|
5. Implement it completely: code, tests, and any docs. The reviewer checks your diff against
|
|
42
47
|
the entry's files-touched list and acceptance criteria, so land everything the entry
|
|
43
48
|
promises, or update the entry to say what changed and why. When a plan's anchors no longer
|
|
44
49
|
match the code, correct the entry in the same change instead of refusing.
|
|
45
|
-
6. Mark the plan done in PLANS.md — ${backlogMoveGuidance("PLANS.md", "## Planned", "## Done", "PLANS-DONE.md")}
|
|
50
|
+
6. Mark the plan done in PLANS.md — ${backlogMoveGuidance("PLANS.md", "## Planned", "## Done", "PLANS-DONE.md", "done")}
|
|
46
51
|
7. Verify last, after the PLANS.md edit, so one run of the project's check covers everything
|
|
47
52
|
you changed (see "Leave the project working" below).
|
|
48
53
|
A plan that resists implementation is a finding: refuse it with the objection recorded rather
|
|
@@ -63,7 +68,7 @@ than forcing it.`,
|
|
|
63
68
|
heading, and end the run. Do not keep investigating without new evidence.
|
|
64
69
|
3. Fix it, and add a regression test that fails without the fix — run that test to watch it
|
|
65
70
|
fail before the fix and pass after.
|
|
66
|
-
4. Mark it fixed in BUGS.md — ${backlogMoveGuidance("BUGS.md", "## Open", "## Fixed", "BUGS-FIXED.md")}
|
|
71
|
+
4. Mark it fixed in BUGS.md — ${backlogMoveGuidance("BUGS.md", "## Open", "## Fixed", "BUGS-FIXED.md", "fixed")}
|
|
67
72
|
5. In the moved entry, write the required validation-gap trace line (format below).
|
|
68
73
|
6. Verify last, after the BUGS.md edits, so one run of the project's check covers everything
|
|
69
74
|
you changed (see "Leave the project working" below).
|
|
@@ -95,6 +100,13 @@ within that budget, there is nothing to do.`,
|
|
|
95
100
|
following it would answer them — correcting the approach or the anchors, tightening the
|
|
96
101
|
acceptance criteria, splitting it per PLAN_SIZING, or refusing it with the objection
|
|
97
102
|
recorded when it should not be done — and remove both the note and the quoted objections.
|
|
103
|
+
- When you lift a Needs-review or Needs-replan note (a split or a rewrite) and the change
|
|
104
|
+
has NOT merged, keep the held count instead of letting it reset: remove any existing
|
|
105
|
+
\`**Previously held N**\` line on the surviving parent and every child part the split
|
|
106
|
+
produces, then write \`${PRIOR_HELD_PREFIX}<N>**\` at the lifted note's count on the
|
|
107
|
+
parent and copy it to each child, so exactly one marker survives and heldCount reads the
|
|
108
|
+
lifted count everywhere — a merged parent (one whose plan left \`## Planned\`) leaves no
|
|
109
|
+
marker.
|
|
98
110
|
- Otherwise, when PLANS.md \`## Planned\` already holds one more eligible plan than there
|
|
99
111
|
are feature instances (one waiting per instance, plus one for the next planner run),
|
|
100
112
|
end with ${NOTHING_TO_DO} — feature has work, and a waiting plan is refined by the
|
|
@@ -113,6 +125,7 @@ within that budget, there is nothing to do.`,
|
|
|
113
125
|
at them in ranges.
|
|
114
126
|
5. Write it as a short markdown section under \`## Planned\` in PLANS.md: goal, approach, files
|
|
115
127
|
touched, acceptance criteria.
|
|
128
|
+
${PLAN_QUEUE_GUIDANCE}
|
|
116
129
|
${PLAN_SIZING}
|
|
117
130
|
${DECOMPOSITION_GUIDANCE}`,
|
|
118
131
|
},
|
|
@@ -154,7 +167,7 @@ edit the brief between the tumwater:prompt markers.`,
|
|
|
154
167
|
title: "code organizer",
|
|
155
168
|
// Written against the 2026-10-05/06 churn: with minor fixes exhausted and the shared
|
|
156
169
|
// small-and-self-contained scope rule in force, organize reorganized the whole tree one
|
|
157
|
-
// directory per
|
|
170
|
+
// directory per run (45 of 86 merges in four hours) with no target structure ever
|
|
158
171
|
// stated. Major refactors stay welcome; they must be considered whole and merge whole.
|
|
159
172
|
find: `Find ONE way the code could be better organized, and restructure it.
|
|
160
173
|
1. First rank the production source files (not the tests) by \`wc -l\`. A production module
|
|
@@ -163,7 +176,8 @@ edit the brief between the tumwater:prompt markers.`,
|
|
|
163
176
|
file of similar size: a test file's shape follows the module it covers. An outlier that is
|
|
164
177
|
mostly one large function or closure cannot be split in one run: do not start the split,
|
|
165
178
|
write it as a plan in PLANS.md under ## Planned that states the target structure (which
|
|
166
|
-
state and handlers move into which module).
|
|
179
|
+
state and handlers move into which module).
|
|
180
|
+
${PLAN_QUEUE_GUIDANCE} Then look for other minor improvements to
|
|
167
181
|
the existing structure: a module in the wrong directory, a missing separation between
|
|
168
182
|
layers, or inconsistent file naming.
|
|
169
183
|
2. When the minor improvements are exhausted, a major refactor — new directories, a
|
|
@@ -233,7 +247,8 @@ scratch analysis scripts — that is a different, much larger task.`,
|
|
|
233
247
|
this run's ONE task: move each listed entry to the section its heading's dates say it belongs in
|
|
234
248
|
(cut and paste, under the existing \`## \` heading — never add, remove, or rename a \`## \`
|
|
235
249
|
heading), leave its text verbatim, and change nothing else. An entry listed under PLANS-DONE.md's
|
|
236
|
-
\`## Done\` with no done date
|
|
250
|
+
\`## Done\` with no done date is already finished and archived there: keep it in place and only add the
|
|
251
|
+
missing \`done YYYY-MM-DD\` to its \`### \` heading — never move it back under PLANS.md's \`## Planned\`.
|
|
237
252
|
|
|
238
253
|
Otherwise, pick ONE kind of uncleanliness — dead code, misleading names, commented-out blocks,
|
|
239
254
|
overly clever constructs, missing or wrong doc comments on public surfaces, inconsistent
|
|
@@ -330,6 +345,9 @@ the last day (per-role outcome counts, each role's time and spend by outcome, no
|
|
|
330
345
|
warning clusters, review rejections, and what merged). It is your entire evidence base — do not go
|
|
331
346
|
looking for the event log or per-role transcripts yourself; tumwater injects the digest because
|
|
332
347
|
the live log lives outside your worktree.
|
|
348
|
+
The digest ends with a ## Fast merge guardrails section when the post-merge machinery is failing
|
|
349
|
+
quietly (main red over 30 minutes, quarantine aged or oversized). Each line there is tumwater's
|
|
350
|
+
own response failing, so it is fileable as it stands under rule 1.
|
|
333
351
|
1. A cluster is a bug ONLY when tumwater's RESPONSE to it is wrong — a failure that raised no
|
|
334
352
|
alarm, drove the wrong state or sleep ladder, latched a stale verdict, or hid itself from
|
|
335
353
|
the user. A mere infrastructure failure (the model server was slow, pi exited non-zero)
|
|
@@ -349,6 +367,44 @@ the live log lives outside your worktree.
|
|
|
349
367
|
entry, so the bugfix role can reproduce it.
|
|
350
368
|
BUGS.md is your only write; never edit source, tests, or docs. If every cluster is ordinary
|
|
351
369
|
infrastructure weather or already filed, there is nothing to do.`,
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "feedback",
|
|
373
|
+
tier: "default",
|
|
374
|
+
title: "feedback: files tumwater bugs upstream on GitHub",
|
|
375
|
+
find: `File ONE bug per run as a GitHub issue on github.com/znation/tumwater, or declare
|
|
376
|
+
nothing-to-do, from the <failure-digest> block in your prompt: a deterministic digest of this
|
|
377
|
+
project's own event log over the last day (per-role outcome counts, each role's time and spend by
|
|
378
|
+
outcome, normalized error and warning clusters, review rejections, and what merged). It is your
|
|
379
|
+
entire evidence base — do not go looking for the event log or per-role transcripts yourself; tumwater
|
|
380
|
+
injects the digest because the live log lives outside your worktree.
|
|
381
|
+
The digest ends with a ## Fast merge guardrails section when the post-merge machinery is failing
|
|
382
|
+
quietly (main red over 30 minutes, quarantine aged or oversized). Each line there is tumwater's
|
|
383
|
+
own response failing, so it is fileable as it stands under rule 1.
|
|
384
|
+
1. A cluster is a bug ONLY when tumwater's RESPONSE to it is wrong — a failure that raised no
|
|
385
|
+
alarm, drove the wrong state or sleep ladder, latched a stale verdict, or hid itself from
|
|
386
|
+
the user. A mere infrastructure failure (the model server was slow, pi exited non-zero)
|
|
387
|
+
is weather, not a bug: filing it produces an issue no maintainer can act on.
|
|
388
|
+
2. Read the digest for that wrong response — 44 identical errors that raised no warning, a run
|
|
389
|
+
that failed in 200 ms then climbed the idle ladder, a cluster that begins on the date a
|
|
390
|
+
specific commit merged.
|
|
391
|
+
3. Rank what you would file by LOSS, not by run count: the digest's time-and-spend table and
|
|
392
|
+
top loss causes price each cause in agent-hours and dollars, and a cluster that burned hours
|
|
393
|
+
outranks one that burned seconds — cite its cost when it is the reason you file.
|
|
394
|
+
4. Before filing, dedupe: check the "Previously filed from this install" block in your prompt
|
|
395
|
+
and run \`gh issue list --repo znation/tumwater --search <cluster terms>\` — never file a
|
|
396
|
+
duplicate. When the same cluster is already filed, there is nothing to do.
|
|
397
|
+
5. File it with \`gh issue create --repo znation/tumwater --title … --body …\` from the shell
|
|
398
|
+
and verify the issue URL in the command's output before you report it. The body cites the
|
|
399
|
+
cluster's normalized key, its count and roles, the correlated \`merged\` commit, and the
|
|
400
|
+
output of \`tumwater version\`; redact absolute paths and secrets — file only what the digest
|
|
401
|
+
itself already shows.
|
|
402
|
+
You never edit files in this repo — \`gh\` is your only write. End every run with the
|
|
403
|
+
${NOTHING_TO_DO} sentinel; when you filed, one \`FILED: #<n> — <title>\` line rides just above it
|
|
404
|
+
(named as qa's FLOW line is). A run that cannot file because \`gh\` is missing, unauthenticated,
|
|
405
|
+
or failed ends WITHOUT the sentinel, naming the failure in your final prose so tumwater's
|
|
406
|
+
finished-without-changes warning surfaces it. If every cluster is ordinary infrastructure weather
|
|
407
|
+
or already filed, there is nothing to do.`,
|
|
352
408
|
},
|
|
353
409
|
{
|
|
354
410
|
id: "security",
|
|
@@ -461,6 +517,7 @@ arguments, config files, environment), and rough edges you meet while running th
|
|
|
461
517
|
- promote a recurring non-\`none\` \`gap:\` tag — three or more Fixed entries carrying it —
|
|
462
518
|
into a PLANS.md entry for the infrastructure that would retire it, citing those entries.
|
|
463
519
|
${VALIDATION_GAP_TALLY}
|
|
520
|
+
${PLAN_QUEUE_GUIDANCE}
|
|
464
521
|
You edit only markdown — never source.
|
|
465
522
|
|
|
466
523
|
Reading the backlog files: PLANS.md and BUGS.md hold open work, so read them whole when your move
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
/** Shared instruction fragments role prompts and prompt assembly embed verbatim. Pure
|
|
3
2
|
* data: this module imports only the dependency-free legacy-role-ids, so prompt assembly
|
|
4
3
|
* can depend on it without a cycle.
|
|
5
4
|
* Split from roles.ts, whose catalog (now role-catalog.ts) find texts embed these fragments
|
|
6
5
|
* while prompt.ts embeds them too — the wording is one concern, the catalog another. */
|
|
7
6
|
import { roleCommitGrep } from "./legacy-role-ids.js";
|
|
8
|
-
/** Shared guidance for any
|
|
7
|
+
/** Shared guidance for any role about to record a plan or bug: split independent parts
|
|
9
8
|
* into their own entries. Defined once so the director and role prompts cannot drift.
|
|
10
9
|
* Lives here (not in prompt.ts) because two role `find` texts embed it — keeping this
|
|
11
10
|
* module import-free, with the dependency running one way: prompt assembly → roles,
|
|
@@ -14,8 +13,8 @@ export const DECOMPOSITION_GUIDANCE = `Before recording a plan or bug, check whe
|
|
|
14
13
|
into independent subparts (separate features, or separate bugs). If it does, record each part as
|
|
15
14
|
its own PLANS.md/BUGS.md entry that cross-references its siblings, so roles can pick them up
|
|
16
15
|
independently; if the parts are not truly independent, keep a single entry.`;
|
|
17
|
-
/** Shared instruction for closing a backlog entry (the feature
|
|
18
|
-
* the bugfix
|
|
16
|
+
/** Shared instruction for closing a backlog entry (the feature role moving a plan to Done,
|
|
17
|
+
* the bugfix role moving a bug to Fixed). Written against the observed failure (PLANS.md
|
|
19
18
|
* 2026-09-30, part 1/3): "move it to a Done section" reads as "create a section", and five
|
|
20
19
|
* commits left PLANS.md with two `## Done` headings — so the wording names the file's EXISTING
|
|
21
20
|
* resolved heading, forbids touching `## ` structure, and gives the verifiable invariant
|
|
@@ -23,11 +22,16 @@ independently; if the parts are not truly independent, keep a single entry.`;
|
|
|
23
22
|
* numbered step of their task. The pasted entry does not stay there: tumwater moves it to the top
|
|
24
23
|
* of `archive` when the change commits (plans/backlog-archive.md), which the last line says so no
|
|
25
24
|
* run opens the archive to do it by hand. Takes the file, section and archive names so the feature
|
|
26
|
-
* and bugfix texts cannot drift apart; lives here with the other shared fragments.
|
|
27
|
-
|
|
25
|
+
* and bugfix texts cannot drift apart; lives here with the other shared fragments. Also takes
|
|
26
|
+
* the closing token ("done" for a finished plan, "fixed" for a BUGS.md record) chosen by the
|
|
27
|
+
* caller, so the verb and the heading-key rule are named instead of left for the role to guess. */
|
|
28
|
+
export function backlogMoveGuidance(file, openSection, resolvedSection, archive, token) {
|
|
28
29
|
return `move the entry, step by step:
|
|
29
30
|
a. cut the entry out of \`${openSection}\`;
|
|
30
|
-
b. add the closing
|
|
31
|
+
b. add the closing stamp to its \`### \` heading, at the end — \`— ${token} YYYY-MM-DD by the <role> role\`
|
|
32
|
+
(a finished plan appends it after its existing \`(planned …)\` parenthetical; the heading
|
|
33
|
+
text before its first \` (\` must not change, or the gate reads it as a brand-new entry
|
|
34
|
+
under \`${resolvedSection}\` rather than a move);
|
|
31
35
|
c. paste it as the first entry under the file's existing \`${resolvedSection}\` heading;
|
|
32
36
|
d. if it was the last entry under \`${openSection}\`, leave a \`_None yet._\` placeholder
|
|
33
37
|
there.
|
|
@@ -39,7 +43,7 @@ export function backlogMoveGuidance(file, openSection, resolvedSection, archive)
|
|
|
39
43
|
* a comment inside the template would become prompt text. */
|
|
40
44
|
const REQUIRES_PART_FORM = "requires part N/M landed"; // vocabulary-ok: requiredParts parses this heading syntax
|
|
41
45
|
/** Shared sizing rule for anyone writing a plan (the plan role and the director): the feature
|
|
42
|
-
*
|
|
46
|
+
* role is one mid-sized local model working alone in one run, and the review gate rejects a
|
|
43
47
|
* change that lands less than its entry promises — so a plan must fit that run. Oversized plans
|
|
44
48
|
* were the pattern behind "half-done against its own plan" rejections. The last sentence is the
|
|
45
49
|
* self-hosting build lag (BUGS.md 2026-09-23): plan 4b bundled "the merge preserves the live
|
|
@@ -58,6 +62,17 @@ ranges, because line anchors go stale with every merge. When tumwater is buildin
|
|
|
58
62
|
every commit merges under the build that predates it, so a
|
|
59
63
|
change to how merges behave and any step that depends on it are separate sub-plans, the second
|
|
60
64
|
merge only once the first is the running build.`;
|
|
65
|
+
/** Shared guidance for anyone writing a plan (the plan role, the organize role, and the
|
|
66
|
+
* director): a new plan joins the BACK of the `## Planned` queue, never the top. Part of the
|
|
67
|
+
* define-once pattern of DECOMPOSITION_GUIDANCE/PLAN_SIZING — file order under `## Planned` IS
|
|
68
|
+
* the work queue: claim assignment takes the first free entry in file order (`assignNext`,
|
|
69
|
+
* src/scheduling/claims.ts), so an entry set at the top jumps ahead of work already waiting.
|
|
70
|
+
* Lives here because three role `find` texts and the director's routing bullet embed it. */
|
|
71
|
+
export const PLAN_QUEUE_GUIDANCE = `A new plan joins the queue at the BACK: append the entry at the
|
|
72
|
+
END of \`## Planned\`, after every existing entry, never at the top. File order under
|
|
73
|
+
\`## Planned\` is the work queue — claim assignment takes the first free entry in that order
|
|
74
|
+
(\`assignNext\`, src/scheduling/claims.ts) — so an entry inserted at the top jumps ahead of work
|
|
75
|
+
already waiting.`;
|
|
61
76
|
/** The closed vocabulary for the validation-gap trace (plans/repair-traces.md): what made a bug
|
|
62
77
|
* hard to *confirm*, which is the only evidence of where this project's test infrastructure is
|
|
63
78
|
* weakest. Closed on purpose so the traces aggregate, and `none` is a written value rather than an
|
|
@@ -109,36 +124,56 @@ export const VALIDATION_GAP_QUERY = `grep -snE '\\*\\*Validation gap:\\*\\* (${V
|
|
|
109
124
|
* parsing the date or the wording out of the full template — the same split as
|
|
110
125
|
* NEEDS_REPLAN_PREFIX. */
|
|
111
126
|
export const NEEDS_REVIEW_PREFIX = `**Needs review `;
|
|
112
|
-
/** The
|
|
113
|
-
*
|
|
127
|
+
/** The tag text before the held count, shared by the note templates and the parser
|
|
128
|
+
* (backlog-eligibility's heldCount) so the two cannot drift: the live count on a Needs-review
|
|
129
|
+
* or Needs-replan note is written inline as `(held N)`, with `N` one past the surviving prior
|
|
130
|
+
* count (the feature role appends it, the plan role's split carries it via PRIOR_HELD_NOTE). */
|
|
131
|
+
export const HELD_TAG_PREFIX = `(held `;
|
|
132
|
+
/** The markdown note the feature role appends to a plan it cannot finish in one run, so the plan
|
|
133
|
+
* role can split it. Mirrors the **Refused …** note convention: a note the next fresh run reads,
|
|
114
134
|
* with no code parsing it. Defined once so the feature and plan texts and the director's routing
|
|
115
|
-
* clause cannot drift.
|
|
116
|
-
|
|
135
|
+
* clause cannot drift. The trailing `(held <N>)` tag (HELD_TAG_PREFIX) is the live held count,
|
|
136
|
+
* written as one past the count surviving on the entry; it sits inside the line, so
|
|
137
|
+
* NEEDS_REVIEW_PREFIX still recognizes the note. */
|
|
138
|
+
export const NEEDS_REVIEW_NOTE = `${NEEDS_REVIEW_PREFIX}<YYYY-MM-DD> by feature: too large for one run ${HELD_TAG_PREFIX}<N>)**`;
|
|
139
|
+
/** The marker a lifted hold leaves behind, so a split or rewrite carries the count instead of
|
|
140
|
+
* letting it reset: `**Previously held N**` on the note's line. The live count is `(held N)` on
|
|
141
|
+
* the note; the surviving marker after a lift is `**Previously held N**` — heldCount reads the
|
|
142
|
+
* first as the priority, then the second. */
|
|
143
|
+
export const PRIOR_HELD_PREFIX = `**Previously held `;
|
|
144
|
+
/** The literal marker a lift leaves on a split or rewrite so the held count survives instead of
|
|
145
|
+
* resetting: a `**Previously held <N>**` line at the same count the lifted note carried (the
|
|
146
|
+
* <N> here is the value the note's `(held N)` tag held). heldCount reads this as the fallback
|
|
147
|
+
* when no live note is present, so the count round-trips a split. */
|
|
148
|
+
export const PRIOR_HELD_NOTE = `${PRIOR_HELD_PREFIX}<N>**`;
|
|
117
149
|
/** The literal prefix every Needs-replan note starts with. Exported so a reader that only has to
|
|
118
150
|
* recognize one (the backlog index's ` [blocked: needs replan]` mark) can match on it instead of
|
|
119
151
|
* parsing the date or the round count out of the full template. */
|
|
120
152
|
export const NEEDS_REPLAN_PREFIX = `**Needs replan `;
|
|
121
|
-
/** The markdown note the feature
|
|
122
|
-
* final revision round, carrying the reviewer's objections back to the strong-tier plan
|
|
153
|
+
/** The markdown note the feature role appends under a plan whose change was rejected after its
|
|
154
|
+
* final revision round, carrying the reviewer's objections back to the strong-tier plan role
|
|
123
155
|
* instead of letting feature re-author the same plan. Same convention as NEEDS_REVIEW_NOTE: a
|
|
124
|
-
* note a later fresh
|
|
125
|
-
|
|
156
|
+
* note a later fresh run reads, with no code parsing it. The trailing `(held <H>)` tag
|
|
157
|
+
* (HELD_TAG_PREFIX) is the live held count, one past the count surviving on the entry; the
|
|
158
|
+
* placeholder is `<H>` (for held) so it reads apart from the `<N>` review-round count earlier
|
|
159
|
+
* in the same line. It sits inside the line, so NEEDS_REPLAN_PREFIX still recognizes the note. */
|
|
160
|
+
export const NEEDS_REPLAN_NOTE = `${NEEDS_REPLAN_PREFIX}<YYYY-MM-DD> by feature: rejected after <N> review rounds ${HELD_TAG_PREFIX}<H>)**`;
|
|
126
161
|
/** The literal prefix every Waiting-for-the-next-failure note starts with. Exported so a reader
|
|
127
162
|
* that only has to recognize one (the backlog index's ` [blocked: waiting for the next failure]`
|
|
128
163
|
* mark) can match on it instead of parsing the date or the wording out of the full template. */
|
|
129
164
|
export const WAITING_FOR_FAILURE_PREFIX = `**Waiting for the next failure `;
|
|
130
|
-
/** The markdown note the bugfix
|
|
165
|
+
/** The markdown note the bugfix role writes under an open bug it cannot reproduce once it has made
|
|
131
166
|
* the failure explain itself, so bugfix stops spending runs without new evidence. Same convention
|
|
132
|
-
* as NEEDS_REPLAN_NOTE: a note a later fresh
|
|
167
|
+
* as NEEDS_REPLAN_NOTE: a note a later fresh run reads. It lifts when whoever records the next
|
|
133
168
|
* occurrence deletes the line, or by `tumwater bug --release <n>`. */
|
|
134
169
|
export const WAITING_FOR_FAILURE_NOTE = `${WAITING_FOR_FAILURE_PREFIX}<YYYY-MM-DD> by bugfix: <what the next failure's message will show>**`;
|
|
135
170
|
/** How a role with no backlog (organize, clean, dedupe, perf, security, robustness, improve) finds
|
|
136
171
|
* its one task, as numbered steps. Written against the observed failure: with nothing to point
|
|
137
172
|
* at, a local model reads the codebase file by file — thirty whole-file reads, ~300 KB of tool
|
|
138
|
-
* output — and then either fills its window or declares nothing-to-do (improve: 41 of 44
|
|
173
|
+
* output — and then either fills its window or declares nothing-to-do (improve: 41 of 44 runs in
|
|
139
174
|
* the first week of September landed nothing). Cheap signals shortlist candidates; a decision
|
|
140
175
|
* deadline ends the search; the harness's own commit subjects (`tumwater(<role>): …`) are the
|
|
141
|
-
* only cross-
|
|
176
|
+
* only cross-run memory of what the role did lately, so the text names them. Takes the role id
|
|
142
177
|
* so the git command is literal, not a placeholder the model has to fill in. */
|
|
143
178
|
export function searchGuidance(roleId) {
|
|
144
179
|
return `How to search — do not read the codebase file by file: a whole-tree survey costs most of
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
/** How a role's id shows up in words: an instance id as `feature #2`. Pure, no I/O, so core
|
|
3
2
|
* modules and the UI may both import it. */
|
|
4
|
-
import { baseRoleOf, instanceIndex } from "./
|
|
3
|
+
import { baseRoleOf, instanceIndex } from "./runner-ids.js";
|
|
5
4
|
/** An instance id as `feature #2`; any other id (a bare role, `director`, a custom role,
|
|
6
5
|
* `feature-1`) unchanged. */
|
|
7
6
|
export function roleLabel(runnerId) {
|