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,17 +1,31 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { increment } from "../collections.js";
|
|
4
3
|
import { readTextOrNull } from "../files/files.js";
|
|
5
4
|
import { cachedTextByStat } from "../files/stat-cache.js";
|
|
6
5
|
import { BACKLOG_FILES, FINISHED_ARCHIVES, fenceTracker, headingMetadata, parseEntryDetails, sectionBodyLines, fenceAwareHeadingLines, headingTitle, } from "./backlog-md.js";
|
|
7
6
|
import { changeBaseRev, fileContentAt } from "../git/git.js";
|
|
8
|
-
import {
|
|
7
|
+
import { plural } from "../text/phrases.js";
|
|
9
8
|
import { collapseWhitespace, truncate } from "../text/text.js";
|
|
10
|
-
import {
|
|
11
|
-
import { plannedPlanEntries } from "./backlog.js";
|
|
9
|
+
import { seriesPart } from "./backlog-parts.js";
|
|
12
10
|
/** The archive files of PLANS.md and BUGS.md (FINISHED_ARCHIVES). */
|
|
13
11
|
const PLANS_ARCHIVE = FINISHED_ARCHIVES[0].archive;
|
|
14
12
|
const BUGS_ARCHIVE = FINISHED_ARCHIVES[1].archive;
|
|
13
|
+
/** The `## ` section titles each backlog file is allowed to carry, keyed by file name. A `## `
|
|
14
|
+
* title outside its file's list is an unknown heading — quoted in no reader's section, so every
|
|
15
|
+
* entry under it would be invisible. backlogStructureReason treats known sections as load-bearing
|
|
16
|
+
* (dropping one is rejected) but unknown ones as mistakes: ADDING one stays a legitimate project
|
|
17
|
+
* extension (this repo grew `## Verified`; a doc-only stack can grow `## Notes`), while DROPPING
|
|
18
|
+
* one is allowed — demoting a misplaced `## ` heading to `### ` under a known section is how such
|
|
19
|
+
* a heading is repaired (BUGS.md 2026-10-23). BUGS.md's Verified section is a real section this
|
|
20
|
+
* repo grew beyond its template; the archive and questions files keep the one section their
|
|
21
|
+
* template seeds. */
|
|
22
|
+
const KNOWN_SECTIONS = {
|
|
23
|
+
"PLANS.md": ["Planned", "Done"],
|
|
24
|
+
"PLANS-DONE.md": ["Done"],
|
|
25
|
+
"BUGS.md": ["Open", "Fixed", "Verified"],
|
|
26
|
+
"BUGS-FIXED.md": ["Fixed"],
|
|
27
|
+
"QUESTIONS.md": ["Open", "Answered"],
|
|
28
|
+
};
|
|
15
29
|
/** The stamp verbs a backlog heading's trailing `(planned 2026-09-29)`-style parenthetical
|
|
16
30
|
* opens with — the same list the backlog writers stamp and the dashboards parse. One home
|
|
17
31
|
* for the verb list: the TUI's team alerts strip the suffix (stripEntryStamp) and the
|
|
@@ -24,7 +38,7 @@ export const ENTRY_STAMP_META_SOURCE = String.raw `\s*\(((?:planned|reported|fou
|
|
|
24
38
|
* (case-insensitive, note captured), read through stripEntryStamp below. */
|
|
25
39
|
const ENTRY_STAMP_META_RE = new RegExp(ENTRY_STAMP_META_SOURCE, "i");
|
|
26
40
|
/** A heading or title with its trailing `(planned 2026-09-29)`-style stamp suffix removed,
|
|
27
|
-
* whitespace otherwise untouched — the shared span of
|
|
41
|
+
* whitespace otherwise untouched — the shared span of backlog-index's indexTitle, eligibility's
|
|
28
42
|
* entryKey and seriesPart, and the TUI team alerts, which all read a heading stamp-free. */
|
|
29
43
|
export function stripEntryStamp(title) {
|
|
30
44
|
return title.replace(ENTRY_STAMP_META_RE, "");
|
|
@@ -68,7 +82,7 @@ const DONE_STAMP = /[—,(;]\s*done \d{4}-\d{2}-\d{2}/;
|
|
|
68
82
|
* but no `done YYYY-MM-DD` — written as plans, never stamped done, and invisible to every
|
|
69
83
|
* Planned reader (`plannedPlanEntries` reads only `## Planned`); and (b) entries under
|
|
70
84
|
* `## Planned` whose heading already carries `done YYYY-MM-DD` — finished and never moved,
|
|
71
|
-
* so the feature
|
|
85
|
+
* so the feature role may implement them again. Fence-aware through backlog-md.ts's shared
|
|
72
86
|
* machinery: a `### `/`## ` line inside a fenced code block is quoted content, not structure,
|
|
73
87
|
* and heading dates are matched against the same joined metadata entryDates matches against.
|
|
74
88
|
* BUGS.md and QUESTIONS.md are deliberately out of scope — their Fixed-section headings do
|
|
@@ -144,6 +158,36 @@ function sectionTitleCounts(md) {
|
|
|
144
158
|
export function duplicateHeadings(md) {
|
|
145
159
|
return [...sectionTitleCounts(md)].filter(([, n]) => n > 1).map(([title]) => title);
|
|
146
160
|
}
|
|
161
|
+
/** The `### ` headings `head` adds that `base` already carries, as extra copies: for each
|
|
162
|
+
* heading (whitespace-collapsed, every `## ` section, fence-aware) present in `base` and more
|
|
163
|
+
* often in `head`, one entry per added copy, in the head's first-appearance order. The archive
|
|
164
|
+
* files' re-pasted-history signal: on 2026-10-19 a conflict hand-back re-pasted 383 + 310 archived
|
|
165
|
+
* entries, and the commit-time sweep prepended them to PLANS-DONE.md and BUGS-FIXED.md again. A
|
|
166
|
+
* heading the base already duplicates never counts unless the head adds yet another copy. */
|
|
167
|
+
export function readdedHeadings(base, head) {
|
|
168
|
+
const count = (md) => {
|
|
169
|
+
const out = new Map();
|
|
170
|
+
for (const line of fenceAwareHeadingLines(md, "### ")) {
|
|
171
|
+
const title = headingTitle(line, "### ");
|
|
172
|
+
const key = collapseWhitespace(title);
|
|
173
|
+
const seen = out.get(key);
|
|
174
|
+
if (seen)
|
|
175
|
+
seen.n++;
|
|
176
|
+
else
|
|
177
|
+
out.set(key, { n: 1, title });
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
};
|
|
181
|
+
const baseCounts = count(base);
|
|
182
|
+
const readded = [];
|
|
183
|
+
for (const [key, { n, title }] of count(head)) {
|
|
184
|
+
const had = baseCounts.get(key)?.n ?? 0;
|
|
185
|
+
if (had > 0 && n > had)
|
|
186
|
+
for (let i = had; i < n; i++)
|
|
187
|
+
readded.push(title);
|
|
188
|
+
}
|
|
189
|
+
return readded;
|
|
190
|
+
}
|
|
147
191
|
/** The `## Fixed` entry headings whose body holds more than one `**Symptom:**` or
|
|
148
192
|
* `**Fix:**` block — the shape a record with no `### ` heading leaves when parseEntryDetails
|
|
149
193
|
* absorbs it into the entry above (an entry opens only at a `### ` line, so the headingless
|
|
@@ -177,9 +221,13 @@ export function doubleBlockFixedEntries(md) {
|
|
|
177
221
|
* against, so a stacked batch is judged change by change — and rejects when (a) the head carries
|
|
178
222
|
* MORE of a title than the base carried (a duplicate `## Done` added, or a second one where the
|
|
179
223
|
* base had none), or (b) a title present on the base is gone from the head (a whole section
|
|
180
|
-
* dropped).
|
|
181
|
-
*
|
|
182
|
-
*
|
|
224
|
+
* dropped). Dropping is judged against the file's KNOWN_SECTIONS — PLANS.md Planned and Done,
|
|
225
|
+
* its archive Done, BUGS.md Open, Fixed and Verified, its archive Fixed, QUESTIONS.md Open and
|
|
226
|
+
* Answered: an unknown `## ` heading (a misplaced one) is free to be dropped — demoting it to
|
|
227
|
+
* `### ` under a known section is the repair (BUGS.md 2026-10-23) — while dropping a known
|
|
228
|
+
* section stays rejected. ADDING a `## ` heading is still a legitimate project extension (this
|
|
229
|
+
* repo's `## Verified`, a doc-only stack's `## Notes`), so the rule stays about heading sets, not
|
|
230
|
+
* section names. A base that already carries a duplicate never blocks unrelated edits — rule (a)
|
|
183
231
|
* fires only when the head's count EXCEEDS the base's, so a change that removes a duplicate always
|
|
184
232
|
* passes. For PLANS.md there is a third rule (part 4/4): a head that ADDS a `### ` entry directly
|
|
185
233
|
* under `## Done` whose joined heading metadata carries `(planned YYYY-MM-DD` but no `done
|
|
@@ -187,11 +235,14 @@ export function doubleBlockFixedEntries(md) {
|
|
|
187
235
|
* the wrong section — rejected with a reason naming the entry and the one-line fix. Judged against
|
|
188
236
|
* the merge-base (the same base the heading rules use), so a Planned → Done move of an entry the
|
|
189
237
|
* base already had passes, a stacked batch is measured change by change, and a pre-existing
|
|
190
|
-
* stranded entry (whose key the base has) never blocks unrelated merges — the clean
|
|
238
|
+
* stranded entry (whose key the base has) never blocks unrelated merges — the clean role's repair
|
|
191
239
|
* (part 3/4) owns those. BUGS.md has one more: a head `## Fixed` entry that holds more than one
|
|
192
240
|
* `**Symptom:**` or `**Fix:**` block (doubleBlockFixedEntries) absorbed a headingless record into
|
|
193
241
|
* its body; a base body that already had the shape is never re-flagged, so only a newly merged
|
|
194
|
-
* record is rejected (BUGS.md 2026-10-08).
|
|
242
|
+
* record is rejected (BUGS.md 2026-10-08). The archive files (PLANS-DONE.md, BUGS-FIXED.md) have
|
|
243
|
+
* one more: a head that adds a copy of a `### ` heading the base archive already carries
|
|
244
|
+
* (readdedHeadings) is pasting finished history back, rejected with the count and the first
|
|
245
|
+
* heading. Detection is deterministic markdown reading — no pi — so
|
|
195
246
|
* both the review gate (exempt and code diffs alike) and the in-lock merge re-check can afford it
|
|
196
247
|
* on every merge (plans: "Backlog structure check", part 2/4). */
|
|
197
248
|
export async function backlogStructureReason(wt, mainBranch, files) {
|
|
@@ -209,10 +260,25 @@ export async function backlogStructureReason(wt, mainBranch, files) {
|
|
|
209
260
|
if (count > 1 && count > (baseCounts.get(title) ?? 0))
|
|
210
261
|
return (`${file} adds another "## ${title}" heading — readers take the first section of a ` +
|
|
211
262
|
`name, so anything under the duplicate is invisible; keep one "## ${title}" per file`);
|
|
263
|
+
// A `## ` title that is not one of this file's known sections (KNOWN_SECTIONS) is not
|
|
264
|
+
// load-bearing: ADDING one is a legitimate project extension (this repo grew `## Verified`,
|
|
265
|
+
// a doc-only stack can grow `## Notes`), so a new one passes — but DROPPING one is the repair
|
|
266
|
+
// for a misplaced heading, so rule (b) guards only known sections and leaves an unknown one
|
|
267
|
+
// free to be demoted to `### ` under a known section (BUGS.md 2026-10-23).
|
|
268
|
+
const known = KNOWN_SECTIONS[file] ?? [];
|
|
212
269
|
for (const title of baseCounts.keys())
|
|
213
|
-
if (!headCounts.has(title))
|
|
270
|
+
if (!headCounts.has(title) && known.includes(title))
|
|
214
271
|
return (`${file} drops the "## ${title}" section the base had — entries filed under it would ` +
|
|
215
272
|
`be invisible to every section reader; restore "## ${title}"`);
|
|
273
|
+
// An archive only grows by new headings: a copy of one the base archive already carries is
|
|
274
|
+
// finished history pasted back (readdedHeadings), never a new finished entry.
|
|
275
|
+
if (file === PLANS_ARCHIVE || file === BUGS_ARCHIVE) {
|
|
276
|
+
const readded = readdedHeadings(base, head);
|
|
277
|
+
if (readded.length > 0)
|
|
278
|
+
return (`${file} adds ${plural(readded.length, '"### " heading', '"### " headings')} it already carries ` +
|
|
279
|
+
`(first: "${truncate(readded[0], 160)}") — the archive holds each finished entry once, so the ` +
|
|
280
|
+
`copies duplicate its history; remove them, and edit an archived entry in place instead`);
|
|
281
|
+
}
|
|
216
282
|
// Part 4/4's rule, PLANS.md and its archive (see this function's doc comment): a NEW plan
|
|
217
283
|
// filed directly under ## Done with no done date. strandedPlanEntries already does the
|
|
218
284
|
// fence-aware, joined-metadata reading the first two conditions need; the base's key set —
|
|
@@ -225,9 +291,18 @@ export async function backlogStructureReason(wt, mainBranch, files) {
|
|
|
225
291
|
]);
|
|
226
292
|
const newcomer = strandedPlanEntries(head).find((e) => e.section === "Done" && !baseKeys.has(planHeadingKey(e.title)));
|
|
227
293
|
if (newcomer)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
294
|
+
// The fault and its fix differ by file: in PLANS.md a NEW plan (the base never carried
|
|
295
|
+
// it in any section) was pasted under ## Done unfinished and belongs under ## Planned;
|
|
296
|
+
// in the archive the same heading is a finished plan the commit-time sweep already moved
|
|
297
|
+
// here, whose only fault is a missing `done YYYY-MM-DD` token — telling its author to
|
|
298
|
+
// "file it under ## Planned" is the opposite of the fix and wastes a revision.
|
|
299
|
+
return file === PLANS_ARCHIVE
|
|
300
|
+
? `${file} files "${newcomer.title}" directly under "## Done" with no done date — the ` +
|
|
301
|
+
`entry is finished and archived, so the only fix is to add \`done YYYY-MM-DD\` to its ` +
|
|
302
|
+
`"### " heading; do not move it back under "## Planned"`
|
|
303
|
+
: `${file} files "${newcomer.title}" directly under "## Done" with no done date — a plan ` +
|
|
304
|
+
`written into the done section is invisible to every Planned reader; file it under ` +
|
|
305
|
+
`"## Planned" instead`;
|
|
231
306
|
}
|
|
232
307
|
// A new headingless record cannot hide in a Fixed entry's body: reject a head whose
|
|
233
308
|
// multi-block entries the base did not carry (an existing one never blocks unrelated
|
|
@@ -249,7 +324,7 @@ export async function backlogStructureReason(wt, mainBranch, files) {
|
|
|
249
324
|
const strandedPlanCache = new Map();
|
|
250
325
|
/** `strandedPlanEntries` for the primary checkout's PLANS.md and PLANS-DONE.md (where finished
|
|
251
326
|
* plans are archived, plans/backlog-archive.md), each entry tagged with its file, stat-cached per
|
|
252
|
-
* file path (stat-cache.cachedTextByStat): the clean
|
|
327
|
+
* file path (stat-cache.cachedTextByStat): the clean role asks for it every run, `tumwater role`
|
|
253
328
|
* previews the same block and doctor reports it, and the archive grows without bound, so an
|
|
254
329
|
* unchanged file costs one stat instead of a full read and section walk. A missing or
|
|
255
330
|
* unreadable file reads as no entries. The result is copied out, so each caller owns its
|
|
@@ -260,8 +335,8 @@ export function strandedPlanEntriesFor(root) {
|
|
|
260
335
|
return (cachedTextByStat(strandedPlanCache, full, full, (md) => strandedPlanEntries(md).map((e) => ({ ...e, file })), (entries) => entries.slice()) ?? []);
|
|
261
336
|
});
|
|
262
337
|
}
|
|
263
|
-
/** The clean
|
|
264
|
-
* (the same root the
|
|
338
|
+
/** The clean role's `<backlog-structure>` prompt block for the primary checkout at `root`
|
|
339
|
+
* (the same root the selfcheck digest and qa coverage blocks read): a rendered block listing
|
|
265
340
|
* each stranded heading with its current section, or undefined when PLANS.md is missing,
|
|
266
341
|
* unreadable, or clean — an unreadable or clean file gives no block, so the prompt is
|
|
267
342
|
* unchanged in the common case. The stranded entries are stat-cached (strandedPlanEntriesFor),
|
|
@@ -285,7 +360,7 @@ ${listed}
|
|
|
285
360
|
* order: the fence-aware walk sectionLines and parseEntryDetails share, carrying the line
|
|
286
361
|
* numbers neither returns. An entry starts at a `### ` line and ends at the line before the
|
|
287
362
|
* next `### ` or `## ` line (or EOF) — the same boundary parseEntryDetails closes bodies on —
|
|
288
|
-
* so a
|
|
363
|
+
* so a role reading lines `start`..`end` sees exactly the entry's heading and body. A `## ` or
|
|
289
364
|
* `### ` line inside a fenced code block is body content, never a boundary, through
|
|
290
365
|
* backlog-md.ts's shared fenceTracker. */
|
|
291
366
|
export function actionableEntryRanges(md, sectionTitle) {
|
|
@@ -322,7 +397,7 @@ export function actionableEntryRanges(md, sectionTitle) {
|
|
|
322
397
|
/** Per-file cache of a section's actionable ranges (actionableEntryRangesFor). */
|
|
323
398
|
const actionableRangesCache = new Map();
|
|
324
399
|
/** `actionableEntryRanges` for one backlog file, stat-cached per (file path, section title):
|
|
325
|
-
* the per-poll and per-
|
|
400
|
+
* the per-poll and per-run readers (eligibility's eligibleEntries, claims' listedKeys) ask for
|
|
326
401
|
* the same section of the same file every couple of seconds, and BUGS.md's closed history —
|
|
327
402
|
* which the whole-file walk reads — grows without bound, so an unchanged file costs one stat
|
|
328
403
|
* instead of a full read, split, and section walk. Keyed by file path so distinct roots never
|
|
@@ -332,43 +407,6 @@ export function actionableEntryRangesFor(root, file, sectionTitle) {
|
|
|
332
407
|
const full = path.join(root, file);
|
|
333
408
|
return (cachedTextByStat(actionableRangesCache, `${full}\u0000${sectionTitle}`, full, (md) => actionableEntryRanges(md, sectionTitle), (ranges) => ranges.slice()) ?? []);
|
|
334
409
|
}
|
|
335
|
-
/** Max characters an index title keeps before a trailing ellipsis, so one very long BUGS.md
|
|
336
|
-
* heading cannot dominate the block. */
|
|
337
|
-
const INDEX_TITLE_MAX = 160;
|
|
338
|
-
/** The index title for a `### ` heading: its stamp suffix (`(reported … 2026-…)`) removed via
|
|
339
|
-
* the shared stripEntryStamp and whitespace collapsed, then clipped to INDEX_TITLE_MAX
|
|
340
|
-
* through text.ts's truncate — the one ellipsis rule, so the cut cannot drift from the
|
|
341
|
-
* other trimmed labels and never splits a surrogate pair. */
|
|
342
|
-
function indexTitle(heading) {
|
|
343
|
-
return truncate(collapseWhitespace(stripEntryStamp(heading)), INDEX_TITLE_MAX);
|
|
344
|
-
}
|
|
345
|
-
/** The files and sections the actionable index covers, in the order loops read them: PLANS.md's
|
|
346
|
-
* planned features, BUGS.md's open bugs, QUESTIONS.md's open questions. */
|
|
347
|
-
const INDEX_SECTIONS = [
|
|
348
|
-
{ file: "PLANS.md", section: "Planned" },
|
|
349
|
-
{ file: "BUGS.md", section: "Open" },
|
|
350
|
-
{ file: "QUESTIONS.md", section: "Open" },
|
|
351
|
-
];
|
|
352
|
-
/** How many closed entries per closed section the index lists. */
|
|
353
|
-
const RECENT_CLOSED_MAX = 5;
|
|
354
|
-
/** The closed sections whose newest entries the index also lists, each read from the live file
|
|
355
|
-
* and then its archive (FINISHED_ARCHIVES, plans/backlog-archive.md); every one keeps its entries
|
|
356
|
-
* newest first, so the first entries in file order are the recent ones. */
|
|
357
|
-
const CLOSED_SECTIONS = FINISHED_ARCHIVES.map((a) => ({
|
|
358
|
-
files: [a.file, a.archive],
|
|
359
|
-
section: a.section,
|
|
360
|
-
}));
|
|
361
|
-
/** Per-file cache of the parsed actionable entries (indexSectionCache). */
|
|
362
|
-
const indexSectionCache = new Map();
|
|
363
|
-
/** Per-file cache of the newest closed entries' ranges; separate from indexSectionCache, which is
|
|
364
|
-
* keyed by the file path alone and PLANS.md would collide. */
|
|
365
|
-
const closedSectionCache = new Map();
|
|
366
|
-
/** One `IndexRow` as an ordinary index line: range, truncated stamp-free title, hold mark, and
|
|
367
|
-
* the in-flight suffix when a live claim names it. The collapsed-series line is built by
|
|
368
|
-
* collapseBlockedSeries, returned alongside these. */
|
|
369
|
-
function renderIndexRow(row) {
|
|
370
|
-
return `- ${row.start}-${row.end}: ${indexTitle(row.title)}${row.mark}${row.claimer ? ` [in flight by ${row.claimer}]` : ""}`;
|
|
371
|
-
}
|
|
372
410
|
/** True when `row` is a candidate for series-collapsing: it is a series part held only by an
|
|
373
411
|
* unmerged prerequisite (`{ blockedBy }`, not a Refused/needs-review/needs-replan note) and no live
|
|
374
412
|
* claim names it. Returns the row's series part when it collapses, else null. */
|
|
@@ -415,7 +453,7 @@ export function partsLabel(rows) {
|
|
|
415
453
|
}
|
|
416
454
|
/** Renders `rows` to index lines, collapsing a multi-part series' blocked parts to one line per
|
|
417
455
|
* series (this plan, "Compact backlog index, part 1/1"): a long series' chain of held parts would
|
|
418
|
-
* otherwise fill every
|
|
456
|
+
* otherwise fill every role prompt with lines no role can act on. An entry collapses when it is a
|
|
419
457
|
* series part held only by an unmerged prerequisite and no live claim names it; a series with two
|
|
420
458
|
* or more such parts renders as one line at the position of its first collapsed part, spanning from
|
|
421
459
|
* that part's start to the last's end, with the part list (`2/4–4/4`) and a `[blocked: each waits
|
|
@@ -423,9 +461,9 @@ export function partsLabel(rows) {
|
|
|
423
461
|
* (Refused/needs-review/needs-replan) part and a claimed part keep their ordinary lines, so the
|
|
424
462
|
* eligibility rules the feature role's charter teaches are unchanged; the collapsed mark keeps the
|
|
425
463
|
* `[blocked: …]` prefix it already skips. */
|
|
426
|
-
/** The grouping half of collapseBlockedSeries, on any rows carrying a title,
|
|
427
|
-
* each series with two or more collapsible blocked parts becomes one `series`
|
|
428
|
-
* part's place; every other row stays a `row` group in order. */
|
|
464
|
+
/** The grouping half of backlog-index's collapseBlockedSeries, on any rows carrying a title,
|
|
465
|
+
* hold and claimer: each series with two or more collapsible blocked parts becomes one `series`
|
|
466
|
+
* group at its first part's place; every other row stays a `row` group in order. */
|
|
429
467
|
export function groupBlockedSeries(rows) {
|
|
430
468
|
// Group the collapsible rows by series, keyed on the lowercased series name.
|
|
431
469
|
const bySeries = new Map();
|
|
@@ -461,98 +499,3 @@ export function groupBlockedSeries(rows) {
|
|
|
461
499
|
}
|
|
462
500
|
return groups;
|
|
463
501
|
}
|
|
464
|
-
function collapseBlockedSeries(rows) {
|
|
465
|
-
return groupBlockedSeries(rows).map((g) => {
|
|
466
|
-
if (g.kind === "row")
|
|
467
|
-
return renderIndexRow(g.row);
|
|
468
|
-
const first = g.rows[0];
|
|
469
|
-
const last = g.rows[g.rows.length - 1];
|
|
470
|
-
return (`- ${first.start}-${last.end}: ${indexTitle(g.series)}, ${g.rows.length} blocked parts ` +
|
|
471
|
-
`(${partsLabel(g.rows)}) [blocked: each waits on an earlier part]`);
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
/** The `<backlog-index>` prompt block: each actionable entry (PLANS.md ## Planned, BUGS.md
|
|
475
|
-
* ## Open, QUESTIONS.md ## Open) with its 1-based line range, rendered from the primary
|
|
476
|
-
* checkout `root`. Replaces the per-tick `grep -n '^##'` heading map, whose pattern also matched
|
|
477
|
-
* every `###` title in BUGS.md's closed history and so re-sent ~108 KB on every turn (BUGS.md
|
|
478
|
-
* 2026-10-06). A missing or unreadable file contributes no sections; undefined when no section
|
|
479
|
-
* has entries, so an empty backlog leaves the prompt unchanged. The per-file actionable entries
|
|
480
|
-
* are stat-cached (indexSectionCache), so an unchanged backlog file is not re-read or re-walked. */
|
|
481
|
-
export function renderBacklogIndexBlock(root) {
|
|
482
|
-
// The still-planned set every section's holds are judged against. A prerequisite clause rides
|
|
483
|
-
// in the entry heading, and the eligibility gate (backlog-eligibility.ts's eligibleEntries)
|
|
484
|
-
// and `tumwater backlog`'s holds read it for BUGS.md's Open entries too, so the same set must
|
|
485
|
-
// reach this block's Open section as well — otherwise a held bug reads as eligible here while
|
|
486
|
-
// the gate skips it. plannedPlanEntries is stat-cached by sectionEntries, so an unchanged
|
|
487
|
-
// PLANS.md costs one stat per call.
|
|
488
|
-
const planned = plannedPlanEntries(root);
|
|
489
|
-
const plannedParts = plannedPartKeys(planned);
|
|
490
|
-
// The cross-role in-flight hold (BUGS.md 2026-10-09): every role's prompts share this one
|
|
491
|
-
// section-agnostic index, and the role it is assembled for is not available here, so an entry
|
|
492
|
-
// claimed by ANY role is marked in flight — the owner's own index marks it too, which is harmless
|
|
493
|
-
// (its assignment note is the instruction), and every other role reads it as taken instead of
|
|
494
|
-
// re-implementing a change someone's merge is already carrying to main.
|
|
495
|
-
const claimed = allLiveClaims(root);
|
|
496
|
-
const listed = [];
|
|
497
|
-
for (const { file, section } of INDEX_SECTIONS) {
|
|
498
|
-
const full = path.join(root, file);
|
|
499
|
-
const entries = cachedTextByStat(indexSectionCache, full, full, (md) => {
|
|
500
|
-
const ranges = actionableEntryRanges(md, section);
|
|
501
|
-
if (ranges.length === 0)
|
|
502
|
-
return [];
|
|
503
|
-
const lines = md.split(/\r?\n/);
|
|
504
|
-
// The body slice is the lines after the heading through the last body line, so a note
|
|
505
|
-
// is found whether it sits first or last (the heading is at 1-based line e.start).
|
|
506
|
-
return ranges.map((e) => ({
|
|
507
|
-
start: e.start,
|
|
508
|
-
end: e.end,
|
|
509
|
-
title: e.title,
|
|
510
|
-
body: lines.slice(e.start, e.end).join("\n"),
|
|
511
|
-
}));
|
|
512
|
-
}, (v) => v.slice());
|
|
513
|
-
if (entries === null || entries.length === 0)
|
|
514
|
-
continue;
|
|
515
|
-
listed.push(`${file} ## ${section}`);
|
|
516
|
-
// Build the rows once per section, then collapse a PLANS.md series' blocked parts (this
|
|
517
|
-
// plan) — BUGS.md and QUESTIONS.md sections keep every entry on its own line.
|
|
518
|
-
const rows = entries.map((e) => {
|
|
519
|
-
const hold = entryHoldAgainst({ title: e.title, body: e.body }, plannedParts);
|
|
520
|
-
const key = collapseWhitespace(stripEntryStamp(e.title)).toLowerCase();
|
|
521
|
-
return {
|
|
522
|
-
start: e.start,
|
|
523
|
-
end: e.end,
|
|
524
|
-
title: e.title,
|
|
525
|
-
hold,
|
|
526
|
-
mark: holdMark(hold),
|
|
527
|
-
claimer: claimed.get(key),
|
|
528
|
-
};
|
|
529
|
-
});
|
|
530
|
-
const isPlans = file === "PLANS.md" && section === "Planned";
|
|
531
|
-
listed.push(...(isPlans ? collapseBlockedSeries(rows) : rows.map(renderIndexRow)));
|
|
532
|
-
}
|
|
533
|
-
for (const { files, section } of CLOSED_SECTIONS) {
|
|
534
|
-
// The newest RECENT_CLOSED_MAX across the live section (normally empty) and the archive.
|
|
535
|
-
let room = RECENT_CLOSED_MAX;
|
|
536
|
-
for (const file of files) {
|
|
537
|
-
if (room <= 0)
|
|
538
|
-
break;
|
|
539
|
-
const full = path.join(root, file);
|
|
540
|
-
const entries = cachedTextByStat(closedSectionCache, full, full, (md) => actionableEntryRanges(md, section).slice(0, RECENT_CLOSED_MAX), (v) => v.slice())?.slice(0, room);
|
|
541
|
-
if (entries === undefined || entries.length === 0)
|
|
542
|
-
continue;
|
|
543
|
-
room -= entries.length;
|
|
544
|
-
listed.push(`${file} ## ${section} (newest ${entries.length})`);
|
|
545
|
-
for (const e of entries)
|
|
546
|
-
listed.push(`- ${e.start}-${e.end}: ${indexTitle(e.title)}`);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if (listed.length === 0)
|
|
550
|
-
return undefined;
|
|
551
|
-
return `Backlog index — each actionable entry, then the newest closed ones, with the 1-based line range it occupies in its file.
|
|
552
|
-
Read the entries you need by those ranges; the archive files (PLANS-DONE.md, BUGS-FIXED.md) grow
|
|
553
|
-
with every merge, so search them rather than reading them. Blocked parts of a multi-part series are
|
|
554
|
-
shown as one line per series.
|
|
555
|
-
<backlog-index>
|
|
556
|
-
${listed.join("\n")}
|
|
557
|
-
</backlog-index>`;
|
|
558
|
-
}
|