tumwater 0.1.2 → 0.2.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 +74 -108
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-args.js +24 -0
- package/dist/src/agent/agent-backend.js +2 -2
- package/dist/src/agent/backend-traits.js +60 -0
- package/dist/src/agent/claude/claude-account.js +61 -8
- package/dist/src/agent/claude/claude-args.js +12 -15
- package/dist/src/agent/claude/claude-backend.js +51 -41
- package/dist/src/agent/claude/claude-env.js +7 -3
- package/dist/src/agent/claude/claude-stream.js +7 -16
- package/dist/src/agent/image-input.js +107 -0
- package/dist/src/agent/notes-shim.js +41 -0
- package/dist/src/agent/opencode/opencode-args.js +61 -0
- package/dist/src/agent/opencode/opencode-backend.js +255 -0
- package/dist/src/agent/opencode/opencode-catalog.js +184 -0
- package/dist/src/agent/opencode/opencode-config.js +188 -0
- package/dist/src/agent/opencode/opencode-env.js +36 -0
- package/dist/src/agent/opencode/opencode-session-id.js +37 -0
- package/dist/src/agent/opencode/opencode-stream.js +278 -0
- package/dist/src/agent/role-notes-write.js +16 -32
- package/dist/src/agent/run-agent.js +95 -18
- package/dist/src/agent/session-ledger.js +57 -1
- package/dist/src/backlog/backlog-archive.js +296 -0
- package/dist/src/backlog/backlog-cli.js +573 -0
- package/dist/src/backlog/backlog-commit.js +31 -0
- package/dist/src/backlog/backlog-eligibility.js +357 -33
- package/dist/src/backlog/backlog-md.js +98 -18
- package/dist/src/backlog/backlog-render.js +62 -11
- package/dist/src/backlog/backlog-structure.js +261 -58
- package/dist/src/backlog/backlog-write.js +197 -319
- package/dist/src/backlog/backlog.js +113 -16
- package/dist/src/backlog/question-answer.js +339 -0
- package/dist/src/baseline/main-baseline.js +75 -88
- package/dist/src/baseline/main-red.js +38 -30
- package/dist/src/brief/brief-edit.js +43 -0
- package/dist/src/{brief.js → brief/brief.js} +66 -10
- package/dist/src/budget/budget.js +32 -15
- package/dist/src/budget/fallback-breaker.js +8 -7
- package/dist/src/build/build-check-detect.js +8 -7
- package/dist/src/build/build-check-events.js +68 -22
- package/dist/src/build/build-check-report.js +22 -23
- package/dist/src/build/build-check-scoped.js +51 -62
- package/dist/src/build/build-check.js +15 -14
- package/dist/src/build/build-info.js +2 -1
- package/dist/src/build/build-stage.js +23 -22
- package/dist/src/build/dep-install.js +22 -21
- package/dist/src/build/host-sleep.js +1 -1
- package/dist/src/change/change-data.js +25 -14
- package/dist/src/change/change-render.js +11 -10
- package/dist/src/cli/brief-command.js +58 -0
- package/dist/src/cli/cli-args.js +25 -5
- package/dist/src/cli/cli-command-args.js +46 -14
- package/dist/src/cli/cli-editor.js +64 -0
- package/dist/src/cli/cli-flag-specs.js +54 -16
- package/dist/src/cli/cli-marker-commands.js +13 -10
- package/dist/src/cli/cli-output.js +9 -0
- package/dist/src/cli/cli-query-commands.js +458 -49
- package/dist/src/cli/cli-repo-gate.js +1 -1
- package/dist/src/cli/cli-run.js +208 -59
- package/dist/src/cli/completion.js +207 -0
- package/dist/src/cli/config-commands.js +14 -11
- package/dist/src/cli/help.js +305 -110
- package/dist/src/cli/log-commands.js +127 -21
- package/dist/src/cli/notify-command.js +33 -18
- package/dist/src/cli/question-commands.js +29 -224
- package/dist/src/cli.js +121 -40
- package/dist/src/collections.js +66 -16
- package/dist/src/concurrency/check-permit.js +16 -17
- package/dist/src/concurrency/lock.js +6 -5
- package/dist/src/concurrency/provider-lease.js +69 -0
- package/dist/src/concurrency/semaphore.js +258 -36
- package/dist/src/config/config-aliases.js +106 -0
- package/dist/src/config/config-editable-keys.js +3 -3
- package/dist/src/config/config-fallbacks.js +379 -0
- package/dist/src/config/config-field-checks.js +19 -10
- package/dist/src/config/config-keys.js +160 -0
- package/dist/src/config/config-live.js +64 -12
- package/dist/src/config/config-schema.js +34 -22
- package/dist/src/config/config-validation.js +132 -48
- package/dist/src/config/config-views.js +119 -337
- package/dist/src/config/config-write.js +138 -177
- package/dist/src/config/config.js +60 -49
- package/dist/src/config/model-selector.js +28 -2
- package/dist/src/doctor/doctor-backlog.js +546 -49
- package/dist/src/doctor/doctor-checks.js +534 -56
- package/dist/src/doctor/doctor-holds.js +193 -53
- package/dist/src/doctor/doctor-install.js +30 -0
- package/dist/src/doctor/doctor-launch-services.js +2 -2
- package/dist/src/doctor/doctor-model-checks.js +549 -157
- package/dist/src/doctor/doctor-notify.js +27 -0
- package/dist/src/doctor/doctor-orphans.js +10 -9
- package/dist/src/doctor/doctor-render.js +36 -9
- package/dist/src/doctor/doctor.js +80 -22
- package/dist/src/doctor/image-probe.js +244 -0
- package/dist/src/doctor/probe.js +64 -0
- package/dist/src/events/event-format.js +226 -102
- package/dist/src/events/event-labels.js +116 -0
- package/dist/src/events/event-legacy.js +140 -0
- package/dist/src/events/event-read.js +58 -22
- package/dist/src/events/event-window.js +27 -7
- package/dist/src/events/events.js +18 -14
- package/dist/src/events/notify.js +137 -38
- package/dist/src/failure/failure-cluster.js +12 -9
- package/dist/src/failure/failure-data.js +96 -48
- package/dist/src/failure/failure-render.js +69 -58
- package/dist/src/failure/failure-state-change.js +33 -26
- package/dist/src/failure/time-spend.js +79 -59
- package/dist/src/files/file-queue.js +16 -4
- package/dist/src/files/files.js +18 -6
- package/dist/src/files/json-object.js +18 -3
- package/dist/src/files/tail.js +1 -1
- package/dist/src/gates/bootstrap-gates.js +5 -4
- package/dist/src/gates/budget-gates.js +250 -34
- package/dist/src/gates/disk-gate.js +18 -18
- package/dist/src/gates/gate-polls.js +117 -69
- package/dist/src/gates/maintenance-quota.js +29 -29
- package/dist/src/gates/pause-gates.js +11 -11
- package/dist/src/gates/readiness.js +2 -1
- package/dist/src/gates/role-cap-gates.js +16 -21
- package/dist/src/gates/startup-gate.js +75 -31
- package/dist/src/gates/streak-gate.js +18 -16
- package/dist/src/git/commit-message.js +12 -5
- package/dist/src/git/git-diff.js +16 -16
- package/dist/src/git/git-run.js +8 -8
- package/dist/src/git/git.js +86 -55
- package/dist/src/git/slots-state.js +14 -5
- package/dist/src/git/worktree-pool.js +25 -18
- package/dist/src/git/worktree-use.js +15 -14
- package/dist/src/git/worktree.js +18 -18
- package/dist/src/gui/gui-args.js +10 -14
- package/dist/src/gui/gui-backlog-commands.js +273 -0
- package/dist/src/gui/gui-command.js +1 -1
- package/dist/src/gui/gui-config-commands.js +440 -0
- package/dist/src/gui/gui-endpoint-commands.js +16 -669
- package/dist/src/gui/gui-endpoint-common.js +49 -0
- package/dist/src/gui/gui-endpoints.js +116 -56
- package/dist/src/gui/gui-operator-commands.js +145 -0
- package/dist/src/gui/gui-prompt-commands.js +234 -0
- package/dist/src/gui/gui-server.js +28 -8
- package/dist/src/gui/gui-team-commands.js +164 -0
- package/dist/src/gui/http-body.js +17 -1
- package/dist/src/history/history-data.js +194 -98
- package/dist/src/history/history-render.js +76 -0
- package/dist/src/history/history.js +37 -58
- package/dist/src/inbox/inbox-attachments.js +75 -14
- package/dist/src/inbox/inbox-cancel.js +7 -5
- package/dist/src/inbox/inbox-edit.js +1 -1
- package/dist/src/inbox/inbox-submit.js +44 -4
- package/dist/src/inbox/inbox-top.js +83 -0
- package/dist/src/inbox/inbox.js +8 -2
- package/dist/src/inbox/pending-prompt.js +1 -1
- package/dist/src/inbox/prompt-commands.js +170 -80
- package/dist/src/init/init-templates.js +16 -14
- package/dist/src/init/init.js +12 -11
- package/dist/src/loop/leftover.js +28 -27
- package/dist/src/loop/loop-fallback.js +2 -2
- package/dist/src/loop/loop-pi.js +106 -52
- package/dist/src/loop/loop-recovery.js +15 -15
- package/dist/src/loop/loop-state.js +139 -13
- package/dist/src/loop/loop-tick.js +194 -86
- package/dist/src/loop/loop.js +72 -36
- package/dist/src/loop/model-fallback.js +3 -2
- package/dist/src/loop/revision.js +16 -7
- package/dist/src/merge/backlog-conflicts.js +155 -0
- package/dist/src/{landing/landing-batch.js → merge/merge-batch.js} +90 -89
- package/dist/src/{landing/landing-check-failures.js → merge/merge-check-failures.js} +87 -41
- package/dist/src/{landing/landing-core.js → merge/merge-core.js} +119 -95
- package/dist/src/{landing/landing-diff.js → merge/merge-diff.js} +1 -1
- package/dist/src/merge/merge-drain.js +212 -0
- package/dist/src/{landing/landing-git.js → merge/merge-git.js} +94 -21
- package/dist/src/merge/merge-pipeline.js +99 -0
- package/dist/src/{landing/landing-questions.js → merge/merge-questions.js} +9 -6
- package/dist/src/merge/merge-queue-render.js +75 -0
- package/dist/src/{landing/landing-queue.js → merge/merge-queue.js} +95 -36
- package/dist/src/merge/merge-slot.js +340 -0
- package/dist/src/{landing/landing-stack.js → merge/merge-stack.js} +66 -62
- package/dist/src/{landing/landing-merge.js → merge/merge-to-main.js} +72 -62
- package/dist/src/merge/merge-verify.js +212 -0
- package/dist/src/notes-cli.js +97 -0
- package/dist/src/operator/operator-commands.js +154 -47
- package/dist/src/operator/operator-intent.js +87 -49
- package/dist/src/operator/operator-requests.js +52 -52
- package/dist/src/operator/retire.js +62 -27
- package/dist/src/operator/revert.js +71 -0
- package/dist/src/orchestrator/orchestrator-launch.js +74 -14
- package/dist/src/orchestrator/orchestrator-scheduling.js +129 -44
- package/dist/src/orchestrator/orchestrator.js +158 -100
- package/dist/src/orchestrator/retention.js +29 -12
- package/dist/src/orchestrator/startup-migration-steps.js +51 -0
- package/dist/src/orchestrator/startup-migrations.js +40 -0
- package/dist/src/paths.js +128 -37
- package/dist/src/pi/pi-args.js +26 -3
- package/dist/src/pi/pi-backend.js +15 -23
- package/dist/src/pi/pi-bin.js +18 -12
- package/dist/src/pi/pi-event-line.js +17 -1
- package/dist/src/pi/pi-failure.js +87 -0
- package/dist/src/pi/pi-models.js +126 -38
- package/dist/src/pi/pi-run-result.js +1 -1
- package/dist/src/pi/pi-settings.js +86 -0
- package/dist/src/pi/pi-stream.js +131 -107
- package/dist/src/pi/pi-watchdogs.js +47 -43
- package/dist/src/pi-extension/bounded-output.js +20 -5
- package/dist/src/pi-extension/context-budget.js +3 -1
- package/dist/src/pi-extension/context-shake.js +54 -21
- package/dist/src/pi-extension/context-window.js +37 -0
- package/dist/src/pi-extension/full-output.js +2 -2
- package/dist/src/pi-extension/strip-thinking.js +80 -0
- package/dist/src/pi-extension/trim-system-prompt.js +38 -0
- package/dist/src/process/launch-services.js +11 -10
- package/dist/src/process/process-table.js +1 -1
- package/dist/src/process/process.js +7 -7
- package/dist/src/process/run-marker.js +29 -6
- package/dist/src/process/supervisor.js +47 -25
- package/dist/src/prompt/dialect.js +30 -10
- package/dist/src/prompt/gate-prompts.js +170 -67
- package/dist/src/prompt/principles.js +1 -0
- package/dist/src/prompt/prompt-followup.js +35 -20
- package/dist/src/prompt/prompt.js +249 -91
- package/dist/src/redeploy/redeploy-policy.js +10 -10
- package/dist/src/redeploy/redeploy.js +6 -4
- package/dist/src/redeploy/redeployer.js +63 -55
- package/dist/src/redeploy/self-reload.js +1 -1
- package/dist/src/report/report-data.js +264 -255
- package/dist/src/report/report-fold.js +353 -0
- package/dist/src/report/report-render.js +270 -33
- package/dist/src/request-timeouts.js +1 -1
- package/dist/src/review/known-flakes.js +34 -14
- package/dist/src/review/protected-edits.js +33 -0
- package/dist/src/review/review-followup.js +16 -4
- package/dist/src/review/review-precheck.js +14 -13
- package/dist/src/review/review-verdict.js +14 -0
- package/dist/src/review/review.js +81 -39
- package/dist/src/review/secret-leaks.js +127 -0
- package/dist/src/review/suite-rerun.js +4 -3
- package/dist/src/roles/legacy-role-ids.js +21 -0
- package/dist/src/roles/legacy-role-migration.js +62 -0
- package/dist/src/roles/loop-ids.js +19 -2
- package/dist/src/roles/role-catalog.js +125 -135
- package/dist/src/roles/role-guidance.js +64 -31
- package/dist/src/roles/role-labels.js +10 -0
- package/dist/src/roles/role-render.js +194 -18
- package/dist/src/roles/role-view.js +216 -17
- package/dist/src/roles/roles.js +18 -15
- package/dist/src/scheduling/backoff.js +29 -19
- package/dist/src/scheduling/claims.js +25 -9
- package/dist/src/scheduling/once-round.js +11 -11
- package/dist/src/scheduling/quiet-hours.js +25 -32
- package/dist/src/scheduling/scheduling.js +43 -24
- package/dist/src/scheduling/{work-landed-cache.js → work-merged-cache.js} +14 -14
- package/dist/src/status/status-budget.js +115 -0
- package/dist/src/status/status-data.js +77 -184
- package/dist/src/status/status-inbox.js +47 -0
- package/dist/src/status/status-install.js +27 -0
- package/dist/src/status/status-polls.js +68 -33
- package/dist/src/status/status-roles.js +105 -0
- package/dist/src/{fleet → team}/error-storm.js +10 -10
- package/dist/src/{fleet → team}/failure-spread.js +8 -8
- package/dist/src/{fleet → team}/orchestrator-info.js +86 -18
- package/dist/src/{fleet → team}/reclaim.js +5 -4
- package/dist/src/{fleet/fleet-hold.js → team/team-hold.js} +55 -21
- package/dist/src/{fleet/fleet-polls.js → team/team-polls.js} +116 -65
- package/dist/src/{fleet/fleet-state.js → team/team-state.js} +62 -37
- package/dist/src/text/datetime.js +42 -2
- package/dist/src/text/format.js +10 -1
- package/dist/src/text/markdown.js +1 -1
- package/dist/src/text/phrases.js +35 -34
- package/dist/src/text/suggest.js +3 -2
- package/dist/src/text/text.js +2 -2
- package/dist/src/tick/qa-coverage.js +2 -1
- package/dist/src/tick/stage-check.js +147 -26
- package/dist/src/tick/telemetry-digest.js +10 -9
- package/dist/src/tick/tick-apply.js +101 -72
- package/dist/src/tick/tick-detail-data.js +32 -18
- package/dist/src/tick/tick-detail.js +62 -21
- package/dist/src/tick/tick-finalize.js +29 -22
- package/dist/src/tick/tick-outcome.js +2 -1
- package/dist/src/tick/tick-prompt.js +94 -26
- package/dist/src/tick/tick-resume.js +29 -13
- package/dist/src/tick/tick-stage.js +91 -47
- package/dist/src/tick/tick-timing.js +60 -59
- package/dist/src/tick/tick-usage.js +53 -23
- package/dist/src/tick/tick-verdict.js +76 -10
- package/dist/src/ui/badges.js +118 -67
- package/dist/src/ui/gui/gui-client-boot.js +209 -30
- package/dist/src/ui/gui/gui-client-composer.js +102 -23
- package/dist/src/ui/gui/gui-client-drawer.js +330 -75
- package/dist/src/ui/gui/gui-client-history.js +96 -38
- package/dist/src/ui/gui/gui-client-labels.js +24 -0
- package/dist/src/ui/gui/gui-client-loops.js +94 -52
- package/dist/src/ui/gui/gui-client-markdown.js +1 -0
- package/dist/src/ui/gui/gui-client-model.js +63 -43
- package/dist/src/ui/gui/gui-client-operator.js +129 -39
- package/dist/src/ui/gui/gui-client-pending.js +36 -32
- package/dist/src/ui/gui/gui-client-report.js +339 -46
- package/dist/src/ui/gui/gui-client-settings.js +148 -14
- package/dist/src/ui/gui/gui-client-sound.js +2 -1
- package/dist/src/ui/gui/{gui-client-fleet.js → gui-client-team.js} +196 -72
- package/dist/src/ui/gui/gui-client.js +26 -20
- package/dist/src/ui/gui/gui-page.js +19 -19
- package/dist/src/ui/gui/gui-styles.js +12 -4
- package/dist/src/ui/progress-data.js +5 -3
- package/dist/src/ui/status-model.js +184 -122
- package/dist/src/ui/status-payload.js +119 -32
- package/dist/src/ui/status-render.js +122 -52
- package/dist/src/ui/team-alerts.js +341 -0
- package/dist/src/ui/tick-progress-model.js +14 -14
- package/dist/src/ui/tone.js +11 -8
- package/dist/src/ui/transcript-tail.js +1 -0
- package/dist/src/ui/transcript.js +23 -16
- package/dist/src/ui/tui/tui-app.js +1 -0
- package/dist/src/ui/tui/tui-backlog.js +85 -11
- package/dist/src/ui/tui/tui-editor-answer.js +97 -0
- package/dist/src/ui/tui/tui-editor-attach.js +107 -0
- package/dist/src/ui/tui/tui-editor-brief.js +107 -0
- package/dist/src/ui/tui/tui-editor-budget.js +155 -0
- package/dist/src/ui/tui/tui-editor-ctx.js +1 -0
- package/dist/src/ui/tui/tui-editor-entry.js +230 -0
- package/dist/src/ui/tui/tui-editor-interval.js +122 -0
- package/dist/src/ui/tui/tui-editor-notes.js +114 -0
- package/dist/src/ui/tui/tui-editor-pause.js +266 -0
- package/dist/src/ui/tui/tui-editor-prompt.js +95 -0
- package/dist/src/ui/tui/tui-editor-queued.js +98 -0
- package/dist/src/ui/tui/tui-editor-quiet.js +106 -0
- package/dist/src/ui/tui/tui-editor-retire.js +83 -0
- package/dist/src/ui/tui/tui-editor-search.js +73 -0
- package/dist/src/ui/tui/tui-editor-session.js +97 -0
- package/dist/src/ui/tui/tui-editors.js +152 -0
- package/dist/src/ui/tui/tui-frame.js +67 -55
- package/dist/src/ui/tui/tui-history.js +12 -7
- package/dist/src/ui/tui/tui-input.js +49 -44
- package/dist/src/ui/tui/tui-keymap.js +1 -0
- package/dist/src/ui/tui/tui-keys.js +91 -1704
- package/dist/src/ui/tui/tui-pane-activity.js +99 -0
- package/dist/src/ui/tui/tui-pane-backlog.js +224 -0
- package/dist/src/ui/tui/tui-pane-brief.js +30 -0
- package/dist/src/ui/tui/tui-pane-ctx.js +11 -0
- package/dist/src/ui/tui/tui-pane-health.js +124 -0
- package/dist/src/ui/tui/tui-pane-history.js +316 -0
- package/dist/src/ui/tui/tui-pane-pending.js +74 -0
- package/dist/src/ui/tui/tui-pane-queued.js +143 -0
- package/dist/src/ui/tui/tui-pane-report.js +122 -0
- package/dist/src/ui/tui/tui-pane-session.js +101 -0
- package/dist/src/ui/tui/tui-pane-transcript.js +150 -0
- package/dist/src/ui/tui/tui-pane.js +66 -28
- package/dist/src/ui/tui/tui-panes.js +294 -0
- package/dist/src/ui/tui/tui.js +45 -25
- package/dist/src/verdict/fix-claim.js +141 -38
- package/dist/src/verdict/no-change.js +19 -0
- package/dist/src/verdict/refusal.js +92 -8
- package/dist/src/verdict/reply-contract.js +17 -5
- package/dist/src/version.js +3 -2
- package/package.json +4 -2
- package/dist/src/agent/mcp/role-notes-server.js +0 -160
- package/dist/src/landing/backlog-conflicts.js +0 -116
- package/dist/src/landing/landing-drain.js +0 -204
- package/dist/src/landing/landing-pipeline.js +0 -96
- package/dist/src/landing/landing-queue-render.js +0 -31
- package/dist/src/landing/landing-slot.js +0 -246
- package/dist/src/landing/landing-vetting.js +0 -196
- package/dist/src/pi-extension/role-notes.js +0 -40
- package/dist/src/ui/fleet-alerts.js +0 -174
|
@@ -1,31 +1,15 @@
|
|
|
1
|
+
// vocabulary: plain
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { describeCheck } from "../build/build-check-report.js";
|
|
3
4
|
import { DECOMPOSITION_GUIDANCE, NEEDS_REPLAN_NOTE, NEEDS_REVIEW_NOTE, PLAN_SIZING } from "../roles/role-guidance.js";
|
|
4
|
-
import { CLAIMS_RULE, REPLY_ENDINGS } from "../verdict/reply-contract.js";
|
|
5
|
+
import { CLAIMS_RULE, CLAIMS_RULE_COMPACT, REPLY_ENDINGS } from "../verdict/reply-contract.js";
|
|
5
6
|
import { todayStamp } from "../budget/budget.js";
|
|
7
|
+
import { daysBetween } from "../text/datetime.js";
|
|
8
|
+
import { plural } from "../text/phrases.js";
|
|
6
9
|
import { worktreePath } from "../paths.js";
|
|
7
10
|
import { PI_DIALECT, dialectFor } from "./dialect.js";
|
|
8
|
-
/** Prompt construction for the role loops' pi runs (tick and director). The reply contract those
|
|
9
|
-
* runs must follow — the TUMWATER_NOTHING_TO_DO sentinel, the TUMWATER_REFUSED line, the
|
|
10
|
-
* SUMMARY/WHY/RISK/VERIFIED block format, the three endings, and the claims rules — lives in
|
|
11
|
-
* reply-contract.ts; this module weaves it into each prompt. The follow-up prompts that pick a
|
|
12
|
-
* session back up — the resume bridge, the missing-summary recovery, and the fresh-tick cut-off
|
|
13
|
-
* note — live in prompt-followup.ts. The landing gate's pi runs (conflict resolution, build fix,
|
|
14
|
-
* review) build their prompts in gate-prompts.ts; assembling a reply into the tick's commit message
|
|
15
|
-
* lives in git/commit-message.ts.
|
|
16
|
-
*
|
|
17
|
-
* The prose is tuned for the fleet's models (2026-10-01): the primary GLM-5.3-Flash (an 18B-active
|
|
18
|
-
* MoE behind a ~1M window) and the budget fallback's local Qwen3.8-27B (~127k window). Both follow
|
|
19
|
-
* short grouped rules with concrete numbers and literal commands far better than long dense
|
|
20
|
-
* paragraphs, over-read when told merely to "work economically", make unchecked claims the reviewer
|
|
21
|
-
* then rejects (the primary's leading rejection cause), and need to be told how a reply must end
|
|
22
|
-
* — so the rules are grouped with one rule per bullet, budgets are numeric, role tasks are
|
|
23
|
-
* numbered steps, claims carry the check that proves them, the three possible endings are a
|
|
24
|
-
* mutually exclusive list, and that closing contract comes last, where a model attends to it most.
|
|
25
|
-
* Nothing here is model-conditional. The PRINCIPLES.md reader the principles block is fed from
|
|
26
|
-
* lives in principles.ts (readPrinciples, with its injection cap). */
|
|
27
11
|
/** The context-budget rule every run carries. Under the old 87k window half of all ticks ended
|
|
28
|
-
* at the ceiling
|
|
12
|
+
* at the ceiling merging nothing (308 of 733 in the autonomous fortnight; 216 of 245 no-change
|
|
29
13
|
* ticks in the first week of September), almost all of it whole-file reads: the model, told only
|
|
30
14
|
* to "work economically", read the codebase file by file (277 whole-file reads against 6 ranged
|
|
31
15
|
* ones in the bugfix/improve/clean logs). Every token read is prefill and dilutes the model's
|
|
@@ -54,12 +38,36 @@ export function contextBudgetRule(dialect = PI_DIALECT) {
|
|
|
54
38
|
/** The pi-dialect reading-budget rule, byte-identical to the rule before the dialect split, for
|
|
55
39
|
* the callers and tests that want the default text. */
|
|
56
40
|
export const CONTEXT_BUDGET_RULE = contextBudgetRule(PI_DIALECT);
|
|
41
|
+
/** contextBudgetRule for the compact prompt: the same habits, with the range-read threshold at
|
|
42
|
+
* ~150 lines, which is what a 64k window's read cap holds (bounded-output.ts outputLimits). Its
|
|
43
|
+
* first bullet names the cost rather than the window's size, since every model gets it. */
|
|
44
|
+
export function compactContextBudgetRule(dialect = PI_DIALECT) {
|
|
45
|
+
return `- Everything you read stays in your context until the run ends and is re-sent on every turn;
|
|
46
|
+
a run that fills the window merges nothing. A whole read of a 500-line file costs ~5k tokens.
|
|
47
|
+
- Check size first (\`wc -l\`) and read files over ~150 lines in ranges: find the lines with
|
|
48
|
+
\`grep -n\`, then read them with ${dialect.readRange}.
|
|
49
|
+
- Cap command output with \`head\`/\`tail\`; never dump a file, a log, or a test run. Do not
|
|
50
|
+
re-read what you already saw.
|
|
51
|
+
- Issue lookups that do not depend on each other as separate tool calls in the same turn; keep
|
|
52
|
+
dependent steps sequential.
|
|
53
|
+
- ${dialect.oversizedOutput}`;
|
|
54
|
+
}
|
|
55
|
+
/** The process-kill rule every run that may run commands carries, word for word: the tick and
|
|
56
|
+
* director rules (full and compact, each adding its own reason), the conflict resolver, and the
|
|
57
|
+
* reviewer. The team's runs and tumwater's own checks share one host, so a kill by pattern
|
|
58
|
+
* reaches them all. On 2026-10-18 a feature run's `pkill -f "dist/test/test-runner.js"; pkill -f
|
|
59
|
+
* "npm run test"` killed two sibling review runs, and a plan run's `pkill -f "npm run test"`
|
|
60
|
+
* killed the coverage run and four lab pi runs (BUGS.md 2026-10-18). Moving pi's prompt from argv
|
|
61
|
+
* to stdin keeps prompt text out of pi's command line; check runs and test runners still match. */
|
|
62
|
+
export const KILL_BY_PID_RULE = `Never kill processes by name or pattern (\`pkill\`, \`killall\`,
|
|
63
|
+
\`pgrep … | xargs kill\`): stop only a process you started, by its PID (\`kill $!\` right after
|
|
64
|
+
backgrounding it).`;
|
|
57
65
|
/** The test-runner rule every run that may run tests carries: the tick and director rules, the
|
|
58
|
-
* conflict resolver, and the reviewer. Project-neutral on purpose — a
|
|
66
|
+
* conflict resolver, and the reviewer. Project-neutral on purpose — a team works on any codebase
|
|
59
67
|
* (npm, cargo, pytest, a configured check.command), so the rule names no ecosystem's runner as THE
|
|
60
68
|
* right one: it points at the declared check or the framework already in use, and its one example
|
|
61
69
|
* is labeled as a mismatch. A run that does not know the suite's runner guesses one: in the week
|
|
62
|
-
* to 2026-10-05 the
|
|
70
|
+
* to 2026-10-05 the team's agents ran `npx vitest` 19 times in tumwater, a node:test repo, and
|
|
63
71
|
* vitest killed the compiled tests mid-run, so no cleanup hook ran (BUGS.md 2026-10-05: ~725 temp
|
|
64
72
|
* run roots abandoned and test children orphaned by one conflict resolver's five runs). Even where
|
|
65
73
|
* a guess cannot do that damage, it wastes turns on a runner that finds no tests. tumwater's own
|
|
@@ -68,12 +76,21 @@ export const CONTEXT_BUDGET_RULE = contextBudgetRule(PI_DIALECT);
|
|
|
68
76
|
export const TEST_RUNNER_RULE = `- Run tests only through the project's declared check or the test framework it already uses —
|
|
69
77
|
never a runner you guessed (say, \`npx vitest\` in a suite written for node:test): a mismatched
|
|
70
78
|
runner finds no tests at best, and can kill them mid-run, leaking processes and temp files.`;
|
|
79
|
+
/** Render a dialect's `backendRules` as one bullet each, or nothing when the dialect has none
|
|
80
|
+
* (pi and claude) — so a backend's extra shell/reasoning guidance rides the reading-budget
|
|
81
|
+
* section right after the oversized-output rule and is present in both the full and compact
|
|
82
|
+
* prompts. */
|
|
83
|
+
function backendRuleLines(rules) {
|
|
84
|
+
if (!rules || rules.length === 0)
|
|
85
|
+
return "";
|
|
86
|
+
return "\n" + rules.map((rule) => `- ${rule}`).join("\n");
|
|
87
|
+
}
|
|
71
88
|
/** The date line every pi prompt carries — tick and director (via sharedPreamble) and the gate's
|
|
72
89
|
* conflict and review runs — naming the local calendar day as YYYY-MM-DD. No prompt
|
|
73
90
|
* used to say what day it is, so a run that had to write one (a BUGS.md heading's "(found by …
|
|
74
91
|
* YYYY-MM-DD)", a Fixed or Refused date, NEEDS_REVIEW_NOTE's and NEEDS_REPLAN_NOTE's
|
|
75
92
|
* <YYYY-MM-DD>, a plan deadline)
|
|
76
|
-
* inferred it from the newest dates in the repo, which were themselves drifting: the
|
|
93
|
+
* inferred it from the newest dates in the repo, which were themselves drifting: the team
|
|
77
94
|
* stamped entries days into the future and each wrong date seeded the next. The day is
|
|
78
95
|
* todayStamp's — the same local-day computation as the daily cost budget window — and every
|
|
79
96
|
* builder takes it as an optional trailing `today` that falls through to this default, so tests
|
|
@@ -85,7 +102,7 @@ export function dateLine(today = todayStamp()) {
|
|
|
85
102
|
* derived from worktreePath so the prompt cannot drift from the layout: the hand-written "two
|
|
86
103
|
* levels up (`../../node_modules`)" it replaced pointed at .tumwater/ itself, and on 2026-09-29
|
|
87
104
|
* a bugfix run that followed it (`cd ../..`, git output empty there) fell back to the primary
|
|
88
|
-
* checkout's absolute path and ran the suite in the live
|
|
105
|
+
* checkout's absolute path and ran the suite in the live team's own checkout (BUGS.md). */
|
|
89
106
|
export const ROOT_FROM_WORKTREE = path.relative(worktreePath("/", "role"), "/");
|
|
90
107
|
/** The Scope rules every run carries unless its role states its own (Role.scope): one small,
|
|
91
108
|
* self-contained task, chosen fast, with a numeric size ceiling. The director always carries
|
|
@@ -101,8 +118,11 @@ export const DEFAULT_SCOPE = `- Do exactly ONE focused task, then stop. Small, c
|
|
|
101
118
|
* in a repo with no node_modules anywhere (a Python, Rust, or Go repo, or one with no check
|
|
102
119
|
* at all). Undefined keeps the generic wording, exactly as prompts read before a check could
|
|
103
120
|
* be configured. `scope` is the Scope section's rules: DEFAULT_SCOPE unless the tick's role
|
|
104
|
-
* states its own (Role.scope).
|
|
105
|
-
|
|
121
|
+
* states its own (Role.scope). A rule added here goes in compactRules too, and its directive in
|
|
122
|
+
* test/compact-prompt.test.ts's DIRECTIVES list, which checks that both forms state it. */
|
|
123
|
+
function commonRules(check, briefFile = "README.md", scope = DEFAULT_SCOPE, dialect = PI_DIALECT, compact) {
|
|
124
|
+
if (compact)
|
|
125
|
+
return compactRules(check, briefFile, scope, dialect, compact.assigned);
|
|
106
126
|
const verify = check
|
|
107
127
|
? `verify with ${describeCheck(check)} (the project's declared check) — run it after your
|
|
108
128
|
change and fix what you broke.`
|
|
@@ -118,14 +138,13 @@ Rules for this run:
|
|
|
118
138
|
|
|
119
139
|
Orientation — read this much before choosing your task, and no more:
|
|
120
140
|
- First read the project brief (${briefFile}) in full, plus QUESTIONS.md when present.
|
|
121
|
-
- PLANS.md and BUGS.md
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- Your worktree starts clean at main, and the harness checks main's build before code roles
|
|
141
|
+
- PLANS.md and BUGS.md hold open work: the prompt's <backlog-index> block lists their actionable
|
|
142
|
+
entries (PLANS.md ## Planned, BUGS.md ## Open, QUESTIONS.md ## Open), each with its 1-based line
|
|
143
|
+
range, so read only the entries you need by those ranges. Finished entries live in
|
|
144
|
+
PLANS-DONE.md and BUGS-FIXED.md, newest first; the index lists the newest few with their
|
|
145
|
+
ranges. Those archive files only grow — never read them whole: search them with grep, or read
|
|
146
|
+
one entry by its range, and only when a specific finished entry matters to your task.
|
|
147
|
+
- Your worktree starts clean at main, and tumwater checks main's build before code roles
|
|
129
148
|
start: do not run the build or test suite just to establish a baseline. Run it after your
|
|
130
149
|
change, or when the task itself needs its output (reproducing a failure, counting the suite).
|
|
131
150
|
|
|
@@ -133,7 +152,7 @@ Scope:
|
|
|
133
152
|
${scope}
|
|
134
153
|
|
|
135
154
|
Reading budget:
|
|
136
|
-
${contextBudgetRule(dialect)}
|
|
155
|
+
${contextBudgetRule(dialect)}${backendRuleLines(dialect.backendRules)}
|
|
137
156
|
|
|
138
157
|
Where you work:
|
|
139
158
|
- Stay inside your worktree: run commands from it — the directory you start in, where
|
|
@@ -142,14 +161,16 @@ Where you work:
|
|
|
142
161
|
- Other agents run concurrently on this machine: put scratch files under \`$TMPDIR\` (made with
|
|
143
162
|
\`mktemp\`), never at a fixed \`/tmp\` path such as \`/tmp/check.log\`, so no other run overwrites
|
|
144
163
|
or reads yours.
|
|
145
|
-
- Never cd to the repo root (\`${ROOT_FROM_WORKTREE}\`): it is the primary checkout, which
|
|
146
|
-
|
|
147
|
-
code the running
|
|
164
|
+
- Never cd to the repo root (\`${ROOT_FROM_WORKTREE}\`): it is the primary checkout, which tumwater
|
|
165
|
+
merges main into while you work, and when this project is tumwater itself its dist/ is the
|
|
166
|
+
code the running team executes — never build, test, or edit files in that checkout.${modules}
|
|
148
167
|
- Never run an unbounded scan (\`find /\`, \`grep -r /\`, any recursive search rooted outside the
|
|
149
|
-
repo): it runs for tens of minutes with no output until
|
|
168
|
+
repo): it runs for tens of minutes with no output until tumwater kills your run as hung.
|
|
150
169
|
- Never run a command that can wait forever — interactive programs (TUIs, REPLs, editors,
|
|
151
170
|
anything reading stdin), servers, or watch modes. To test such a program, background it with
|
|
152
171
|
a hard time limit (kill it after at most 30 minutes) and never give it a real TTY.
|
|
172
|
+
- ${KILL_BY_PID_RULE} A pattern naming your check or test runner also matches other
|
|
173
|
+
agents' runs and tumwater's own checks on this machine, and kills them too.
|
|
153
174
|
- Leave the project working: ${verify} One run answers every question about the check: write
|
|
154
175
|
its full output to a file in a scratch dir once (\`<check> > <scratch-dir>/check.log 2>&1\`),
|
|
155
176
|
then pipe that file through \`tail\` — only the failures matter. To see any other part of the
|
|
@@ -157,15 +178,15 @@ Where you work:
|
|
|
157
178
|
${TEST_RUNNER_RULE}
|
|
158
179
|
|
|
159
180
|
Boundaries:
|
|
160
|
-
- Never create, amend, or revert git commits, branches, or merges —
|
|
181
|
+
- Never create, amend, or revert git commits, branches, or merges — tumwater handles all git
|
|
161
182
|
operations. Reading git history is fine.
|
|
162
183
|
- Never touch the .tumwater directory or tumwater.json.
|
|
163
|
-
- Never edit the
|
|
184
|
+
- Never edit the brief block in ${briefFile} (between the tumwater:prompt markers).
|
|
164
185
|
- PRINCIPLES.md holds this project's design principles; only the director and steward roles may
|
|
165
186
|
edit it. Treat it as read-only — to object to a principle, record the objection in PLANS.md.
|
|
166
187
|
- docs/invariants.md states each subsystem's guarantees, pinned to the test that enforces them.
|
|
167
188
|
Read the matching section before you reconstruct what the code you are changing is supposed
|
|
168
|
-
to guarantee; when a
|
|
189
|
+
to guarantee; when a merge adds or changes a guarantee, update the register in the same
|
|
169
190
|
commit.
|
|
170
191
|
|
|
171
192
|
When to ask, when to refuse:
|
|
@@ -173,7 +194,7 @@ When to ask, when to refuse:
|
|
|
173
194
|
choice, taste), do not guess: append a question to QUESTIONS.md under ## Open with context,
|
|
174
195
|
the options, and your own recommendation. Then continue with the parts that don't depend on
|
|
175
196
|
it, or end the run. Never block on an unanswered question; check QUESTIONS.md for answers at
|
|
176
|
-
the start of each
|
|
197
|
+
the start of each run and act on one that unblocks your work. Do not re-ask an open question.
|
|
177
198
|
- If partway in you conclude the task would harm the project — it violates PRINCIPLES.md, grows
|
|
178
199
|
complexity without justification, or keeps fighting back — refuse it. Revert nothing; append
|
|
179
200
|
this note directly under the entry's heading in PLANS.md (a planned feature) or BUGS.md:
|
|
@@ -185,12 +206,109 @@ When to ask, when to refuse:
|
|
|
185
206
|
|
|
186
207
|
${CLAIMS_RULE}
|
|
187
208
|
|
|
188
|
-
How to end your reply —
|
|
209
|
+
How to end your reply — tumwater parses it, so the form matters:
|
|
189
210
|
- Your last message is plain text: never a tool call, and never an announcement of what you would
|
|
190
211
|
do next ("Let me check…") — either make the call or finish. If a tool result only repeats what
|
|
191
212
|
you already have, do not call it again.
|
|
192
213
|
${REPLY_ENDINGS}`;
|
|
193
214
|
}
|
|
215
|
+
/** commonRules for the compact prompt (CompactPrompt): every directive of the full rules, in
|
|
216
|
+
* fewer words. Orientation differs for an assigned run, which reads its entry first and the
|
|
217
|
+
* brief only when the entry needs it — reading the brief in full cost a median ~2.5k tokens of
|
|
218
|
+
* every run before its first edit (2026-10-17 audit). Scope, Boundaries' first three bullets,
|
|
219
|
+
* TEST_RUNNER_RULE and the reply contract are the full prompt's own text. Keep it in step with
|
|
220
|
+
* commonRules: test/compact-prompt.test.ts checks every directive both must state. */
|
|
221
|
+
function compactRules(check, briefFile, scope, dialect, assigned) {
|
|
222
|
+
const verify = check
|
|
223
|
+
? `verify with ${describeCheck(check)} (the project's declared check) — run it after your
|
|
224
|
+
change and fix what you broke.`
|
|
225
|
+
: `if it has a build or test command, run it after your change and fix what you broke.`;
|
|
226
|
+
const modules = check?.kind === "npm"
|
|
227
|
+
? `
|
|
228
|
+
- Your worktree has no node_modules; it borrows \`${ROOT_FROM_WORKTREE}/node_modules\`. Read a
|
|
229
|
+
dependency's types or source there.`
|
|
230
|
+
: "";
|
|
231
|
+
const orientation = assigned
|
|
232
|
+
? `Orientation — read this much before you start, and no more:
|
|
233
|
+
- Read your assigned entry (at the end of this prompt) by its line range first. It replaces your
|
|
234
|
+
task's choosing steps, so this prompt carries no backlog index. Read the project brief
|
|
235
|
+
(${briefFile}) or QUESTIONS.md only when the entry needs them.
|
|
236
|
+
- PLANS.md and BUGS.md grow without bound — never read them wholesale. Read another entry only
|
|
237
|
+
by a range this prompt gives, or find it with \`grep -n\` first.`
|
|
238
|
+
: `Orientation — read this much before choosing your task, and no more:
|
|
239
|
+
- First read the project brief (${briefFile}) in full, plus QUESTIONS.md when present.
|
|
240
|
+
- PLANS.md and BUGS.md grow without bound — never read them wholesale. The <backlog-index> block
|
|
241
|
+
gives each actionable entry and the newest closed ones with its 1-based line range: read only
|
|
242
|
+
the entries you need, by range. The steward role is the exception: it curates those files and
|
|
243
|
+
must see them whole.`;
|
|
244
|
+
return `
|
|
245
|
+
Rules for this run:
|
|
246
|
+
|
|
247
|
+
${orientation}
|
|
248
|
+
- Your worktree starts clean at main and tumwater checks main's build first: do not run the
|
|
249
|
+
build or test suite for a baseline. Run it after your change, or when the task needs its output.
|
|
250
|
+
|
|
251
|
+
Scope:
|
|
252
|
+
${scope}
|
|
253
|
+
|
|
254
|
+
Reading budget:
|
|
255
|
+
${compactContextBudgetRule(dialect)}${backendRuleLines(dialect.backendRules)}
|
|
256
|
+
|
|
257
|
+
Where you work:
|
|
258
|
+
- Run commands from your worktree (where you start; \`git log\` and \`git show\` work there) or a
|
|
259
|
+
scratch directory. Put scratch files under \`$TMPDIR\` (made with \`mktemp\`), never at a fixed
|
|
260
|
+
\`/tmp\` path: other agents run concurrently on this machine.
|
|
261
|
+
- Never cd to the repo root (\`${ROOT_FROM_WORKTREE}\`): it is the primary checkout tumwater merges
|
|
262
|
+
into — never build, test, or edit files there.${modules}
|
|
263
|
+
- Never run an unbounded scan (\`find /\`, \`grep -r /\`, any recursive search rooted outside the
|
|
264
|
+
repo) or a command that can wait forever (TUIs, REPLs, editors, anything reading stdin,
|
|
265
|
+
servers, watch modes). To test such a program, background it with a hard time limit (at most
|
|
266
|
+
30 minutes) and never give it a real TTY.
|
|
267
|
+
- ${KILL_BY_PID_RULE} A pattern also kills other agents' runs and checks.
|
|
268
|
+
- Leave the project working: ${verify} Write the check's full output to a scratch file once
|
|
269
|
+
(\`<check> > <scratch-dir>/check.log 2>&1\`) and \`tail\` it; grep that file for anything else, and
|
|
270
|
+
re-run the check only after an edit changes the tree.
|
|
271
|
+
${TEST_RUNNER_RULE}
|
|
272
|
+
|
|
273
|
+
Boundaries:
|
|
274
|
+
- Never create, amend, or revert git commits, branches, or merges — tumwater handles all git
|
|
275
|
+
operations. Reading git history is fine.
|
|
276
|
+
- Never touch the .tumwater directory or tumwater.json.
|
|
277
|
+
- Never edit the brief block in ${briefFile} (between the tumwater:prompt markers).
|
|
278
|
+
- PRINCIPLES.md is read-only to every role but the director and steward; record an objection to
|
|
279
|
+
a principle in PLANS.md.
|
|
280
|
+
- docs/invariants.md pins each subsystem's guarantees to tests: read the matching section before
|
|
281
|
+
changing that code, and update it in the same commit when a merge changes a guarantee.
|
|
282
|
+
|
|
283
|
+
When to ask, when to refuse:
|
|
284
|
+
- A fork that is genuinely the user's call (product direction, an irreversible choice, taste):
|
|
285
|
+
do not guess. Append a question to QUESTIONS.md under ## Open with context, the options, and
|
|
286
|
+
your recommendation, then continue with what does not depend on it, or end the run. Never
|
|
287
|
+
block on a question, act on an answer that unblocks your work, and do not re-ask an open one.
|
|
288
|
+
- A task that would harm the project (it violates PRINCIPLES.md, grows complexity without
|
|
289
|
+
justification, or keeps fighting back): refuse it. Revert nothing; append this note directly
|
|
290
|
+
under the entry's heading in PLANS.md (a planned feature) or BUGS.md:
|
|
291
|
+
**Refused <YYYY-MM-DD> by <role>: <one-line reason>**
|
|
292
|
+
That note is the only change a refusing run leaves.
|
|
293
|
+
- Skip entries carrying a Refused note: never pick or re-refuse them. A fully blocked backlog is
|
|
294
|
+
a legitimate nothing-to-do state.
|
|
295
|
+
|
|
296
|
+
${CLAIMS_RULE_COMPACT}
|
|
297
|
+
|
|
298
|
+
How to end your reply — tumwater parses it, so the form matters:
|
|
299
|
+
- Your last message is plain text: never a tool call, and never an announcement of what you would
|
|
300
|
+
do next ("Let me check…") — either make the call or finish. If a tool result only repeats what
|
|
301
|
+
you already have, do not call it again.
|
|
302
|
+
${REPLY_ENDINGS}`;
|
|
303
|
+
}
|
|
304
|
+
/** The principles block's compact form: PRINCIPLES.md from its first list item on, dropping the
|
|
305
|
+
* file's own introduction (which restates the block's heading). A file with no list item is
|
|
306
|
+
* kept whole. */
|
|
307
|
+
export function principleBullets(principles) {
|
|
308
|
+
const lines = principles.split("\n");
|
|
309
|
+
const first = lines.findIndex((line) => /^[-*] /.test(line));
|
|
310
|
+
return first <= 0 ? principles : lines.slice(first).join("\n");
|
|
311
|
+
}
|
|
194
312
|
/** The <principles> block injected into every tick and director prompt: the project's codified
|
|
195
313
|
* taste, phrased positively so loops follow it rather than merely avoid violations. The file
|
|
196
314
|
* itself is read by principles.ts's readPrinciples. */
|
|
@@ -201,54 +319,83 @@ function principlesBlock(principles) {
|
|
|
201
319
|
* the harness renders the digest from its own event log at the project root and hands it over, so
|
|
202
320
|
* the role spends no tool calls acquiring evidence and cannot read a stale or foreign log. */
|
|
203
321
|
function digestBlock(digest) {
|
|
204
|
-
return `Runtime failure digest of
|
|
322
|
+
return `Runtime failure digest of tumwater's own event log — your evidence base:\n<failure-digest>\n${digest}\n</failure-digest>`;
|
|
323
|
+
}
|
|
324
|
+
/** The age clause opening the notes heading: `last saved today; `, `last saved <day>, N days
|
|
325
|
+
* ago; `, or `last saved <day>; ` for a day after today (clock skew). Empty without both days. */
|
|
326
|
+
function savedClause(savedDay, today) {
|
|
327
|
+
if (!savedDay || !today)
|
|
328
|
+
return "";
|
|
329
|
+
const age = daysBetween(savedDay, today);
|
|
330
|
+
if (age < 0)
|
|
331
|
+
return `last saved ${savedDay}; `;
|
|
332
|
+
if (age === 0)
|
|
333
|
+
return "last saved today; ";
|
|
334
|
+
return `last saved ${savedDay}, ${plural(age, "day")} ago; `;
|
|
205
335
|
}
|
|
206
336
|
/** The role's notebook as shown to it: its own earlier ticks' note, labeled as unverified.
|
|
207
337
|
* Rides only when there is a non-empty note; the standing instruction to write one is always
|
|
208
338
|
* present (roleNotesInstruction), so a role's FIRST tick — with no note yet — still learns the
|
|
209
|
-
*
|
|
210
|
-
function roleNotesBlock(notes) {
|
|
211
|
-
return `Notes your role wrote in earlier
|
|
339
|
+
* command exists and can seed the notebook. */
|
|
340
|
+
function roleNotesBlock(notes, savedDay, today) {
|
|
341
|
+
return `Notes your role wrote in earlier runs (${savedClause(savedDay, today)}yours, unverified — check against the code before
|
|
212
342
|
relying on them):\n<role-notes>\n${notes.trim()}\n</role-notes>`;
|
|
213
343
|
}
|
|
214
344
|
/** The standing instruction that makes the notebook seedable: every role tick is told it may
|
|
215
345
|
* write one, so the first tick (when no note exists yet) can create it. The note content block
|
|
216
|
-
* (roleNotesBlock) is what is omitted when there is nothing to show.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
346
|
+
* (roleNotesBlock) is what is omitted when there is nothing to show. Every backend speaks the
|
|
347
|
+
* same shell command (plans/notes-command.md §3), and the director never reaches
|
|
348
|
+
* buildTickPrompt, so it never carries one. */
|
|
349
|
+
export function roleNotesInstruction() {
|
|
350
|
+
return `Before you end, if you learned something the next run of your role should know (where
|
|
351
|
+
things live, what you ruled out and why, what you would look at next), save the full replacement
|
|
352
|
+
note (at most 4 KB) by running:
|
|
353
|
+
|
|
354
|
+
tumwater-notes write <<'TUMWATER_NOTES'
|
|
355
|
+
<the note>
|
|
356
|
+
TUMWATER_NOTES
|
|
357
|
+
|
|
358
|
+
Do not copy backlog entries into it — PLANS.md and BUGS.md hold the work itself.`;
|
|
222
359
|
}
|
|
360
|
+
/** The retired names a model still meets in code, git history and logs, mapped to today's terms
|
|
361
|
+
* (plans/terminology.md "Glossary"). The one place these words may appear in a prompt. */
|
|
362
|
+
const OLDER_NAMES_MAP = "loop = role, tick = run, fleet = team, landing/landed = merge/merged, vetted = approved, notebook = notes, initial prompt = brief"; // vocabulary-ok: maps retired names
|
|
363
|
+
/** The sentence every assembled prompt carries once: the older names and where all of them are
|
|
364
|
+
* listed. Interpolated into the shared preamble and the reviewer and resolver openers. */
|
|
365
|
+
export const OLDER_NAMES_SENTENCE = `Older tumwater names still appear in code, git history and logs: ${OLDER_NAMES_MAP}; docs/glossary.md in tumwater's repository lists them all.`;
|
|
223
366
|
/** Shared opening of every loop prompt: where the run happens, what day it is, and why the
|
|
224
367
|
* project exists. Defined once so the tick and director prompts cannot drift — and so the one
|
|
225
368
|
* sentence telling every role to date its records from dateLine, not from the repo, reaches
|
|
226
369
|
* each of them without editing any role text. */
|
|
227
370
|
function sharedPreamble(initialPrompt, today) {
|
|
228
371
|
const parts = [
|
|
229
|
-
`You work in a dedicated git worktree of this project; your changes will be committed and merged to main by
|
|
230
|
-
`${dateLine(today)} Stamp it on anything you record now — a new BUGS.md
|
|
231
|
-
"(found by
|
|
232
|
-
|
|
372
|
+
`You work in a dedicated git worktree of this project; your changes will be committed and merged to main by tumwater after you finish.`,
|
|
373
|
+
`${dateLine(today)} Stamp it on anything you record now — a new BUGS.md heading ends
|
|
374
|
+
"(found by the <role> role YYYY-MM-DD)", a closing date reads "— fixed YYYY-MM-DD by the <role> role",
|
|
375
|
+
a new PLANS.md heading reads "(planned YYYY-MM-DD by the <role> role …)", and a Refused or
|
|
376
|
+
Needs-review note carries the date too — and count plan deadlines from it; never infer the date
|
|
377
|
+
from the repo.`,
|
|
378
|
+
OLDER_NAMES_SENTENCE,
|
|
233
379
|
];
|
|
234
380
|
if (initialPrompt) {
|
|
235
|
-
parts.push(`The project's
|
|
381
|
+
parts.push(`The project's brief — its reason to exist — is:\n<project-prompt>\n${initialPrompt}\n</project-prompt>`);
|
|
236
382
|
}
|
|
237
383
|
return parts;
|
|
238
384
|
}
|
|
239
|
-
/** The full prompt for one role
|
|
385
|
+
/** The full prompt for one role run. */
|
|
240
386
|
export function buildTickPrompt(input) {
|
|
241
|
-
const { role, initialPrompt, principles, digest, coverage, backlogStructure, backlogIndex, notes, extraInstructions, planTargetNote, check, briefFile, today, userRequest, agent } = input;
|
|
387
|
+
const { role, initialPrompt, principles, digest, coverage, backlogStructure, backlogIndex, notes, notesSavedDay, extraInstructions, planTargetNote, flakyChecks, check, briefFile, today, userRequest, agent, compact } = input;
|
|
242
388
|
const dialect = dialectFor(agent);
|
|
389
|
+
const day = today ?? todayStamp();
|
|
243
390
|
const parts = [
|
|
244
|
-
`You are the "${role.id}"
|
|
245
|
-
...sharedPreamble(initialPrompt,
|
|
391
|
+
`You are the "${role.id}" role (${role.title}) on this project's tumwater team — tumwater runs a team of AI roles that develop the project.`,
|
|
392
|
+
...sharedPreamble(initialPrompt, day),
|
|
246
393
|
];
|
|
247
394
|
if (principles)
|
|
248
|
-
parts.push(principlesBlock(principles));
|
|
395
|
+
parts.push(principlesBlock(compact ? principleBullets(principles) : principles));
|
|
249
396
|
if (userRequest)
|
|
250
|
-
parts.push(`An explicit request from the user, aimed at this
|
|
251
|
-
your role's rules and the
|
|
397
|
+
parts.push(`An explicit request from the user, aimed at this role. Treat it as this run's steering;
|
|
398
|
+
your role's rules and the merge gate still apply unchanged. If the request is out of this
|
|
252
399
|
role's scope, say so in your reply instead of doing it anyway.\n<user-request>\n${userRequest.trim()}\n</user-request>`);
|
|
253
400
|
if (coverage)
|
|
254
401
|
parts.push(coverage);
|
|
@@ -259,14 +406,16 @@ role's scope, say so in your reply instead of doing it anyway.\n<user-request>\n
|
|
|
259
406
|
if (backlogIndex)
|
|
260
407
|
parts.push(backlogIndex);
|
|
261
408
|
if (notes && notes.trim() !== "")
|
|
262
|
-
parts.push(roleNotesBlock(notes));
|
|
409
|
+
parts.push(roleNotesBlock(notes, notesSavedDay, day));
|
|
263
410
|
parts.push(`Your task this run:\n${role.find.trim()}`);
|
|
264
411
|
if (planTargetNote)
|
|
265
412
|
parts.push(planTargetNote.trim());
|
|
413
|
+
if (flakyChecks)
|
|
414
|
+
parts.push(flakyChecks.trim());
|
|
266
415
|
if (extraInstructions)
|
|
267
416
|
parts.push(`Additional standing instructions from the user:\n${extraInstructions.trim()}`);
|
|
268
|
-
parts.push(roleNotesInstruction(
|
|
269
|
-
parts.push(commonRules(check, briefFile, role.scope, dialect).trim());
|
|
417
|
+
parts.push(roleNotesInstruction());
|
|
418
|
+
parts.push(commonRules(check, briefFile, role.scope, dialect, compact).trim());
|
|
270
419
|
return parts.join("\n\n");
|
|
271
420
|
}
|
|
272
421
|
/** The prompt for a director tick, which routes a user request into the project. */
|
|
@@ -283,27 +432,34 @@ today,
|
|
|
283
432
|
backlogIndex,
|
|
284
433
|
/** The agent backend the director resolves to, selecting the prompt dialect (./dialect.ts).
|
|
285
434
|
* Omitted: pi's dialect. */
|
|
286
|
-
agent
|
|
435
|
+
agent,
|
|
436
|
+
/** The compact variant every run gets; a director run is never assigned an entry. Omitted:
|
|
437
|
+
* the full prompt. */
|
|
438
|
+
compact,
|
|
439
|
+
/** The director's `roles.director.instructions` override — its run prompt is otherwise the
|
|
440
|
+
* user's request. Omitted: no `Additional standing instructions` block, exactly as today. */
|
|
441
|
+
extraInstructions) {
|
|
442
|
+
const variant = compact ? { assigned: false } : undefined;
|
|
287
443
|
const parts = [
|
|
288
|
-
`You are the "director"
|
|
289
|
-
the project by sending it requests; one has just arrived. Other specialist
|
|
444
|
+
`You are the "director" role on this project's tumwater team — tumwater runs a team of AI roles that develop the project. The user steers
|
|
445
|
+
the project by sending it requests; one has just arrived. Other specialist roles continuously
|
|
290
446
|
implement planned features from PLANS.md and fix bugs from BUGS.md.`,
|
|
291
447
|
...sharedPreamble(initialPrompt, today),
|
|
292
448
|
];
|
|
293
449
|
if (principles)
|
|
294
|
-
parts.push(principlesBlock(principles));
|
|
450
|
+
parts.push(principlesBlock(variant ? principleBullets(principles) : principles));
|
|
295
451
|
if (backlogIndex)
|
|
296
452
|
parts.push(backlogIndex);
|
|
297
453
|
parts.push(`The user's request:\n<user-request>\n${userPrompt.trim()}\n</user-request>`);
|
|
298
454
|
parts.push(`Interpret the request as a project-level command and route it — do NOT implement substantial
|
|
299
455
|
work yourself:
|
|
300
456
|
- A feature request or substantial change: write a concrete plan for it in PLANS.md (goal,
|
|
301
|
-
approach, files touched, acceptance criteria) so the feature
|
|
457
|
+
approach, files touched, acceptance criteria) so the feature role implements it. Do not build
|
|
302
458
|
it now. ${PLAN_SIZING}
|
|
303
459
|
- A bug report: record it in BUGS.md (symptom, how to reproduce, suspected cause — investigate
|
|
304
|
-
briefly to sharpen the report) so the bugfix
|
|
460
|
+
briefly to sharpen the report) so the bugfix role fixes it. Do not fix it now.
|
|
305
461
|
- Guidance, a decision, or a constraint (e.g. "prefer X", "drop feature Y"): record it durably
|
|
306
|
-
where future
|
|
462
|
+
where future roles will see it — PRINCIPLES.md first for standing design guidance and taste;
|
|
307
463
|
README.md, PLANS.md, or BUGS.md otherwise — and remove anything it supersedes.
|
|
308
464
|
- A question: answer it in your final reply, and record anything durable it surfaced.
|
|
309
465
|
- An answer to an open question (e.g. "answer Q3: choose SQLite"): move that entry from
|
|
@@ -311,39 +467,41 @@ work yourself:
|
|
|
311
467
|
or route any follow-on work it implies.
|
|
312
468
|
- A decision about a refused entry (e.g. "clear the refusal on plan X", "reconsider plan Y"):
|
|
313
469
|
clear its **Refused …** note from PLANS.md/BUGS.md — or revise the entry per the user's
|
|
314
|
-
direction — so
|
|
470
|
+
direction — so roles can pick it up again.
|
|
315
471
|
- A decision about a marked plan (e.g. "split plan X", "keep plan X whole", "replan X"):
|
|
316
472
|
split it into independently landable sub-plans per PLAN_SIZING, or clear the
|
|
317
473
|
${NEEDS_REVIEW_NOTE} note, or clear a ${NEEDS_REPLAN_NOTE} note and rewrite the entry — per
|
|
318
474
|
the user's direction.
|
|
319
|
-
- A request to manage
|
|
320
|
-
"move X before Y"): write the FULL replacement
|
|
475
|
+
- A request to manage custom roles ("add a role named X that does Y", "remove X",
|
|
476
|
+
"move X before Y"): write the FULL replacement customRoles array to
|
|
321
477
|
.tumwater-config-request.json at your worktree root — the shape is
|
|
322
|
-
{ "
|
|
323
|
-
[a-z0-9_-] no built-in role uses and a task written as the
|
|
478
|
+
{ "customRoles": [ { "name": "…", "task": "…" } ] }. Add appends an entry with a name in
|
|
479
|
+
[a-z0-9_-] no built-in role uses and a task written as the role's standing per-run
|
|
324
480
|
instruction (one clear paragraph); remove deletes the entry; move reorders entries (array
|
|
325
|
-
order is display/scheduling order). The array REPLACES the current one — include every
|
|
326
|
-
that should exist, not only the changed entries. Worked example — to add a
|
|
481
|
+
order is display/scheduling order). The array REPLACES the current one — include every role
|
|
482
|
+
that should exist, not only the changed entries. Worked example — to add a role named docs
|
|
327
483
|
that keeps the examples current and drop one named scrape, write the file containing exactly
|
|
328
484
|
the surviving entries:
|
|
329
|
-
{ "
|
|
330
|
-
Write the file and stop:
|
|
331
|
-
it; the
|
|
485
|
+
{ "customRoles": [ { "name": "docs", "task": "Keep the examples/ directory current with the latest API changes." } ] }.
|
|
486
|
+
Write the file and stop: tumwater validates it, applies it to the live config, and deletes
|
|
487
|
+
it; the roles start on their own and the request file is never committed.
|
|
332
488
|
- Only a trivially small direct edit (fix a typo, tweak a doc line, adjust a config value the
|
|
333
489
|
user explicitly stated) may be done immediately instead of routed.
|
|
334
490
|
- Investigate only as much as routing precisely needs — grep and ranged reads to name the right
|
|
335
491
|
files, functions, and suspected cause — never a survey of the codebase, and never the
|
|
336
492
|
implementation itself.
|
|
337
493
|
- ${DECOMPOSITION_GUIDANCE}`);
|
|
494
|
+
if (extraInstructions)
|
|
495
|
+
parts.push(`Additional standing instructions from the user:\n${extraInstructions.trim()}`);
|
|
338
496
|
// Before the shared rules, not after them: the closing reply contract stays the last thing
|
|
339
497
|
// in the prompt, where a model attends to it most (the tick prompt's invariant too).
|
|
340
|
-
parts.push(`Note on one boundary in the rules below, director only: user-defined-
|
|
498
|
+
parts.push(`Note on one boundary in the rules below, director only: user-defined-role requests are
|
|
341
499
|
executed by writing .tumwater-config-request.json in your worktree (shape and worked example
|
|
342
|
-
above) — never by editing tumwater.json, which stays off-limits to you as to every role.
|
|
343
|
-
|
|
500
|
+
above) — never by editing tumwater.json, which stays off-limits to you as to every role. Tumwater
|
|
501
|
+
validates the request and applies only its customRoles array; a request that also names
|
|
344
502
|
any other setting (timeouts, budgets, role enablement, review settings) has that key discarded
|
|
345
503
|
with a warning, so such a request is guidance to record per the routing rules, not an edit you
|
|
346
504
|
can make.`);
|
|
347
|
-
parts.push(commonRules(check, briefFile, undefined, dialectFor(agent)).trim());
|
|
505
|
+
parts.push(commonRules(check, briefFile, undefined, dialectFor(agent), variant).trim());
|
|
348
506
|
return parts.join("\n\n");
|
|
349
507
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
/** Self-redeploy for a self-hosting
|
|
1
|
+
/** Self-redeploy for a self-hosting team (see build/build-info.ts for why): when main's build
|
|
2
2
|
* inputs have moved past the running build, verify that main is green, compile it into a staging
|
|
3
|
-
* dir, drain the
|
|
3
|
+
* dir, drain the team (no new ticks; in-flight ones finish or are aborted resumably after
|
|
4
4
|
* RESTART_DRAIN_MAX_MS), swap the compiled tree into dist/, and ask the supervisor
|
|
5
5
|
* (src/process/supervisor.ts) to respawn the harness onto it by exiting RESTART_EXIT_CODE. Every
|
|
6
6
|
* step is non-blocking from the orchestrator's poll: the green check and the compile run in the
|
|
7
7
|
* background and are consulted on later polls, so a slow `npm test` never stalls scheduling.
|
|
8
8
|
* Completed restarts are rate-limited to one per RESTART_COOLDOWN_MS (BUGS.md 2026-09-11), so
|
|
9
|
-
* sustained main churn cannot halt the
|
|
9
|
+
* sustained main churn cannot halt the team for a drain over and over.
|
|
10
10
|
*
|
|
11
11
|
* This module is the POLICY — the injectable effects interface (RedeployDeps), the
|
|
12
12
|
* cooldown/escalation knobs the state machine decides with, and the RESTART_EXIT_CODE handshake
|
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
* contract both halves serve:
|
|
18
18
|
*
|
|
19
19
|
* Nothing here is fail-open: a red main, a failed compile, or a swap error blocks the restart for
|
|
20
|
-
* that head and the
|
|
20
|
+
* that head and the team keeps running the old build until main moves again. A green check that
|
|
21
21
|
* REJECTS is the one non-verdict — it could not run, so it says nothing about the tree: the
|
|
22
22
|
* pending head is dropped, one warning is logged per episode, and the next poll re-runs the
|
|
23
23
|
* check (BUGS.md 2026-09-16). A block says so out loud — one warning event, and the reason in
|
|
24
24
|
* BuildStatus.restartBlocked, which the dashboards and doctor render — because a stale build that
|
|
25
25
|
* is about to be replaced and one that never will be look identical otherwise (BUGS.md
|
|
26
26
|
* 2026-09-08). A green, compiled build can still be unable to START here — the environment, not
|
|
27
|
-
* the code, fails `tumwater run`'s startup gate — so that gate is asked before the
|
|
27
|
+
* the code, fails `tumwater run`'s startup gate — so that gate is asked before the team is held
|
|
28
28
|
* and again right before the swap, and a failure refuses the restart with a `restart_refused`
|
|
29
|
-
* event instead of trading a running
|
|
29
|
+
* event instead of trading a running team for a child that exits on its first line (BUGS.md
|
|
30
30
|
* 2026-09-23). Every tick a restart interrupts resumes on the new build through the same resume
|
|
31
31
|
* machinery a Ctrl+C uses, so a restart loses no work. */
|
|
32
32
|
/** The exit code a supervised `tumwater run` child uses to say "rebuilt; respawn me" —
|
|
@@ -38,7 +38,7 @@ export const RESTART_EXIT_CODE = 75;
|
|
|
38
38
|
* Redeployer.drainSince. Director ticks are exempt: an in-flight human prompt extends the hold
|
|
39
39
|
* without any cap (see poll).
|
|
40
40
|
*
|
|
41
|
-
* The live window tracks the
|
|
41
|
+
* The live window tracks the team's real tick duration: the orchestrator passes the p75 of recent
|
|
42
42
|
* completed work-bearing role ticks (InFlightCounts.roleTickP75Ms) and poll uses it in place of
|
|
43
43
|
* this constant once it has enough samples (BUGS.md 2026-09-18). The hand-set 30 minutes had not
|
|
44
44
|
* been re-derived since an earlier backend; measured p50 was 46 min and p75 82 min, so the drain
|
|
@@ -47,15 +47,15 @@ export const RESTART_EXIT_CODE = 75;
|
|
|
47
47
|
export const RESTART_DRAIN_MAX_MS = 30 * 60_000;
|
|
48
48
|
/** How often a COMPLETED auto-restart may land — at most once per this window (BUGS.md
|
|
49
49
|
* 2026-09-11): under sustained main churn every stale head would otherwise drive a full
|
|
50
|
-
* hold+drain+swap episode back to back, halting the
|
|
50
|
+
* hold+drain+swap episode back to back, halting the team for a drain over and over. Bounds
|
|
51
51
|
* frequency across episodes; RESTART_DRAIN_MAX_MS bounds the drain within one. */
|
|
52
52
|
export const RESTART_COOLDOWN_MS = 12 * 60 * 60_000;
|
|
53
53
|
/** The cooldown's urgency carve-out (BUGS.md 2026-09-30): when the RUNNING build's own commit
|
|
54
54
|
* carries a red baseline verdict, the deferral is cut to this window instead of the full 12 h —
|
|
55
55
|
* a red running build is not churn but the exact predicament the self-redeploy exists to end,
|
|
56
|
-
* and the rate limit protects against storms, not against a
|
|
56
|
+
* and the rate limit protects against storms, not against a team knowingly executing code its
|
|
57
57
|
* own tree fails. Still a real delay, so consecutive reds cannot storm: one episode per this
|
|
58
|
-
* window at most, each swap giving the
|
|
58
|
+
* window at most, each swap giving the team a chance to merge on green. */
|
|
59
59
|
export const RESTART_URGENT_COOLDOWN_MS = 15 * 60_000;
|
|
60
60
|
/** The urgent carve-out in whole minutes — the one home of that ms→min division, beside the
|
|
61
61
|
* constant it derives from, so the three redeployer.ts warnings that restate the length in
|