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,7 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
// vocabulary: plain
|
|
2
|
+
/** The merge gate's deterministic checks, run BEFORE a changed tick commits.
|
|
2
3
|
*
|
|
3
|
-
* The gate runs `backlogStructureReason` and `falseFixReason` at
|
|
4
|
-
* the author's session is over: a finding costs a queue slot, a
|
|
4
|
+
* The gate runs `backlogStructureReason` and `falseFixReason` at merge time, by which point
|
|
5
|
+
* the author's session is over: a finding costs a queue slot, a verification, and a rejection that
|
|
5
6
|
* discards the work (13 phantom-fix rejections between 2026-09-23 and 2026-10-02). Both checks
|
|
6
7
|
* read the worktree and the change's merge-base, so the same questions can be asked at staging
|
|
7
8
|
* time, while the authoring session can still be continued for one fix-up turn. This module
|
|
@@ -17,15 +18,32 @@
|
|
|
17
18
|
* files whose final newline the change removed. These stage the change (`git add -A`) to read
|
|
18
19
|
* the index with git only, then restore the index to HEAD before returning: staging is a read
|
|
19
20
|
* aid for the checks, not a change to the tick's state. A git hiccup yields no finding rather
|
|
20
|
-
* than failing the tick.
|
|
21
|
+
* than failing the tick. Unlike the backlog checks above, the gate does not run these: they are
|
|
22
|
+
* heuristics for what a reviewer tends to reject, so neither path check looks inside record
|
|
23
|
+
* prose (isRecordProse), where an old or not-yet-created path is correct as written.
|
|
24
|
+
*
|
|
25
|
+
* `planDependencyFinding` is staging-only: it is not part of the review gate or the in-lock
|
|
26
|
+
* merge re-check, because `tumwater doctor`'s plan-dependency warning is the backstop for
|
|
27
|
+
* anything that gets past the fix-up turn. `waitingNoteFinding` is staging-only and bugfix-only
|
|
28
|
+
* for the same reason: it nudges a bugfix run that left a Progress note without the hold. */
|
|
29
|
+
import path from "node:path";
|
|
21
30
|
import { changedFiles } from "../git/git-diff.js";
|
|
22
|
-
import { conflictMarkerReason } from "../
|
|
31
|
+
import { conflictMarkerReason } from "../merge/merge-git.js";
|
|
23
32
|
import { isExemptDiff } from "../review/exemptions.js";
|
|
24
33
|
import { backlogStructureReason } from "../backlog/backlog-structure.js";
|
|
34
|
+
import { protectedEditReason } from "../review/protected-edits.js";
|
|
35
|
+
import { secretLeakReason } from "../review/secret-leaks.js";
|
|
25
36
|
import { falseFixReason } from "../verdict/fix-claim.js";
|
|
26
|
-
import { changeBaseRev } from "../git/git.js";
|
|
37
|
+
import { changeBaseRev, fileContentAt } from "../git/git.js";
|
|
38
|
+
import { readTextOrNull } from "../files/files.js";
|
|
39
|
+
import { BACKLOG_FILES, finishedArchiveOf, parseEntryDetails } from "../backlog/backlog-md.js";
|
|
40
|
+
import { entryKey, planDependencyProblems, refusalNote, waitingNote } from "../backlog/backlog-eligibility.js";
|
|
41
|
+
import { baseRoleOf } from "../roles/loop-ids.js";
|
|
42
|
+
import { WAITING_FOR_FAILURE_NOTE } from "../roles/role-guidance.js";
|
|
27
43
|
import { gitTry } from "../git/git-run.js";
|
|
28
44
|
import { moreSuffix } from "../text/phrases.js";
|
|
45
|
+
/** Where finished plans are archived (plans/backlog-archive.md). */
|
|
46
|
+
const PLANS_ARCHIVE = finishedArchiveOf("PLANS.md").archive;
|
|
29
47
|
/** How many `file:line` hits a single stale-reference finding lists before it stops. */
|
|
30
48
|
const MAX_STALE_HITS = 10;
|
|
31
49
|
/** How many renamed/deleted paths each stale-reference finding reports before stopping — the
|
|
@@ -41,6 +59,17 @@ const MAX_NEWLINE_FILES = 10;
|
|
|
41
59
|
function diffPlusPath(line) {
|
|
42
60
|
return line.slice(4).replace(/^b\//, "");
|
|
43
61
|
}
|
|
62
|
+
/** True for record prose: a backlog file (BACKLOG_FILES: PLANS.md, BUGS.md, QUESTIONS.md and the
|
|
63
|
+
* finished-entry archives) or a markdown design doc under `plans/`. They record history and
|
|
64
|
+
* describe planned work, so a path there is correct as written even when it is absent from the
|
|
65
|
+
* tree: a finished entry names a file as it was, and a plan names the file it will create or
|
|
66
|
+
* both sides of the move it describes. Asking a rename to sweep them rewrote "old → new" into
|
|
67
|
+
* "new → new" and history into the present; that failure took five rejections on
|
|
68
|
+
* one rename on 2026-10-18 and -19. */
|
|
69
|
+
function isRecordProse(file) {
|
|
70
|
+
const base = file.slice(file.lastIndexOf("/") + 1);
|
|
71
|
+
return BACKLOG_FILES.has(base) || (file.startsWith("plans/") && file.endsWith(".md"));
|
|
72
|
+
}
|
|
44
73
|
/** The capped list a finding line appends: at most `max` entries joined by ", ", then
|
|
45
74
|
* `moreSuffix` for the rest. Shared by the missing-path and lost-final-newline findings so both
|
|
46
75
|
* cap and join identically. */
|
|
@@ -48,13 +77,78 @@ function cappedList(items, max) {
|
|
|
48
77
|
const shown = items.slice(0, max);
|
|
49
78
|
return `${shown.join(", ")}${moreSuffix(items.length - shown.length)}`;
|
|
50
79
|
}
|
|
51
|
-
/** The
|
|
52
|
-
*
|
|
53
|
-
*
|
|
80
|
+
/** The finding for the heading `requires` clause problems that `head` (the edited PLANS.md)
|
|
81
|
+
* adds over `base` (its text at the merge-base), or undefined when it adds none; `archivedTitles`
|
|
82
|
+
* (PLANS-DONE.md's headings) count as existing headings on both sides. Only new
|
|
83
|
+
* problems count: one already on main is not this run's fault and stays doctor's to report, so
|
|
84
|
+
* the author is never asked to fix another run's heading. The most serious new problem is named
|
|
85
|
+
* — cycle, then dangling, unparsed, then misworded — with a count of the rest. */
|
|
86
|
+
export function planDependencyFinding(head, base, archivedTitles = []) {
|
|
87
|
+
const problems = (text) => planDependencyProblems(parseEntryDetails(text, "Planned"), [...parseEntryDetails(text, "Done").map((e) => e.title), ...archivedTitles]);
|
|
88
|
+
const was = problems(base);
|
|
89
|
+
const now = problems(head);
|
|
90
|
+
const newCycle = now.cycle !== null && was.cycle === null ? now.cycle : null;
|
|
91
|
+
const newDangling = now.dangling.filter((d) => !was.dangling.some((b) => b.title === d.title && b.ref === d.ref));
|
|
92
|
+
const newUnparsed = now.unparsed.filter((t) => !was.unparsed.includes(t));
|
|
93
|
+
const newMisworded = now.misworded.filter((m) => !was.misworded.some((b) => b.title === m.title && b.ref === m.ref));
|
|
94
|
+
const total = (newCycle !== null ? 1 : 0) + newDangling.length + newUnparsed.length + newMisworded.length;
|
|
95
|
+
if (total === 0)
|
|
96
|
+
return undefined;
|
|
97
|
+
let first;
|
|
98
|
+
if (newCycle !== null) {
|
|
99
|
+
first = `PLANS.md's requires clauses now form a cycle: ${newCycle.join(" → ")} — every part on it stays blocked forever; ` + // vocabulary-ok: PLANS.md heading syntax
|
|
100
|
+
"edit one heading's `requires … landed` clause to break it"; // vocabulary-ok: PLANS.md heading syntax
|
|
101
|
+
}
|
|
102
|
+
else if (newDangling.length > 0) {
|
|
103
|
+
const d = newDangling[0];
|
|
104
|
+
first = `PLANS.md plan "${d.title}" requires ${d.ref}, which no PLANS.md heading carries — the claim allocator ` +
|
|
105
|
+
"ignores the clause and hands the plan out now; fix the series name or part number to match the prerequisite's heading";
|
|
106
|
+
}
|
|
107
|
+
else if (newUnparsed.length > 0) {
|
|
108
|
+
first = `PLANS.md plan "${newUnparsed[0]}" has a \`requires … landed\` clause that names no part the claim allocator can read — ` + // vocabulary-ok: PLANS.md heading syntax
|
|
109
|
+
"it holds nothing; write each ref as `<Series> i/n` or `part i/n` after a `<Series>, part i/n:` heading prefix";
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const w = newMisworded[0];
|
|
113
|
+
first = `PLANS.md plan "${w.title}" names ${w.ref} as a prerequisite without a \`requires … landed\` clause — ` + // vocabulary-ok: PLANS.md heading syntax
|
|
114
|
+
"the claim allocator ignores that wording and hands the part out now; write it as `requires part i/n landed` — fix it before this change commits"; // vocabulary-ok: PLANS.md heading syntax
|
|
115
|
+
}
|
|
116
|
+
return `${first}${moreSuffix(total - 1)}`;
|
|
117
|
+
}
|
|
118
|
+
/** The number of `- **Progress …` lines in an entry body. */
|
|
119
|
+
function progressCount(body) {
|
|
120
|
+
return body.split("\n").filter((l) => /^-\s+\*\*Progress\b/.test(l.trim())).length;
|
|
121
|
+
}
|
|
122
|
+
/** The finding for a BUGS.md bug that `head` (the edited file) shows with more Progress notes
|
|
123
|
+
* than `base` (its text at the merge-base) while it stays under `## Open` with neither a Waiting
|
|
124
|
+
* note nor a Refused note — bugfix's step 2 asks a run that cannot reproduce a bug to write the
|
|
125
|
+
* Waiting note, and runs that skip it leave the bug open to re-investigation. A newly filed bug
|
|
126
|
+
* is not a re-investigation. Names the first such bug in file order, with a count of the rest. */
|
|
127
|
+
export function waitingNoteFinding(head, base) {
|
|
128
|
+
const was = new Map(parseEntryDetails(base, "Open").map((e) => [entryKey(e.title), e.body]));
|
|
129
|
+
const counting = parseEntryDetails(head, "Open").filter((e) => {
|
|
130
|
+
const before = was.get(entryKey(e.title));
|
|
131
|
+
return (before !== undefined &&
|
|
132
|
+
progressCount(e.body) > progressCount(before) &&
|
|
133
|
+
waitingNote(e.body) === null &&
|
|
134
|
+
refusalNote(e.body) === null);
|
|
135
|
+
});
|
|
136
|
+
if (counting.length === 0)
|
|
137
|
+
return undefined;
|
|
138
|
+
return (`BUGS.md bug "${counting[0].title}" gained a Progress note but stays open with no Waiting for the next ` +
|
|
139
|
+
`failure note — if this run could not reproduce it, add ${WAITING_FOR_FAILURE_NOTE} as its own line under ` +
|
|
140
|
+
`the heading so later runs wait for new evidence (bugfix's step 2); if it did reproduce, leave the entry ` +
|
|
141
|
+
`as it is${moreSuffix(counting.length - 1)}`);
|
|
142
|
+
}
|
|
143
|
+
/** The findings likely to get this uncommitted change rejected at merge, or an empty list when
|
|
144
|
+
* it is clean: the merge gate's own deterministic checks plus the staging-only heuristics (plan
|
|
145
|
+
* dependencies, the bugfix Waiting note, path drift). Checks the worktree at `wt` against
|
|
146
|
+
* `mainBranch`'s merge-base; `exemptPaths` are the config's review-exemption globs that scope
|
|
147
|
+
* the false-fix check.
|
|
54
148
|
*
|
|
55
149
|
* Never throws: a check that fails yields no finding, so a git hiccup at staging time defers to
|
|
56
150
|
* the gate instead of failing the tick. */
|
|
57
|
-
export async function stageCheckFindings(wt, mainBranch, exemptPaths) {
|
|
151
|
+
export async function stageCheckFindings(wt, mainBranch, exemptPaths, role) {
|
|
58
152
|
const files = await changedFiles(wt).catch(() => null);
|
|
59
153
|
if (files === null || files.length === 0)
|
|
60
154
|
return [];
|
|
@@ -62,6 +156,29 @@ export async function stageCheckFindings(wt, mainBranch, exemptPaths) {
|
|
|
62
156
|
const structure = await backlogStructureReason(wt, mainBranch, files).catch(() => undefined);
|
|
63
157
|
if (structure)
|
|
64
158
|
findings.push(structure);
|
|
159
|
+
if (files.includes("PLANS.md")) {
|
|
160
|
+
const head = readTextOrNull(path.join(wt, "PLANS.md")) ?? "";
|
|
161
|
+
const base = await fileContentAt(wt, await changeBaseRev(wt, mainBranch), "PLANS.md").catch(() => undefined);
|
|
162
|
+
// Finished plans are archived in PLANS-DONE.md (plans/backlog-archive.md): a requires clause
|
|
163
|
+
// naming a merged part resolves there, on both sides of the comparison alike.
|
|
164
|
+
const archived = parseEntryDetails(readTextOrNull(path.join(wt, PLANS_ARCHIVE)) ?? "", "Done").map((e) => e.title);
|
|
165
|
+
const dependency = base === undefined ? undefined : planDependencyFinding(head, base, archived);
|
|
166
|
+
if (dependency !== undefined)
|
|
167
|
+
findings.push(dependency);
|
|
168
|
+
}
|
|
169
|
+
if (baseRoleOf(role) === "bugfix" && files.includes("BUGS.md")) {
|
|
170
|
+
const head = readTextOrNull(path.join(wt, "BUGS.md")) ?? "";
|
|
171
|
+
const base = await fileContentAt(wt, await changeBaseRev(wt, mainBranch), "BUGS.md").catch(() => undefined);
|
|
172
|
+
const waiting = base === undefined ? undefined : waitingNoteFinding(head, base);
|
|
173
|
+
if (waiting !== undefined)
|
|
174
|
+
findings.push(waiting);
|
|
175
|
+
}
|
|
176
|
+
const protectedReason = await protectedEditReason(wt, mainBranch, files, role).catch(() => undefined);
|
|
177
|
+
if (protectedReason)
|
|
178
|
+
findings.push(protectedReason);
|
|
179
|
+
const secretReason = await secretLeakReason(wt, mainBranch, files).catch(() => undefined);
|
|
180
|
+
if (secretReason)
|
|
181
|
+
findings.push(secretReason);
|
|
65
182
|
if (isExemptDiff(files, exemptPaths)) {
|
|
66
183
|
const falseFix = await falseFixReason(wt, mainBranch, files).catch(() => undefined);
|
|
67
184
|
if (falseFix)
|
|
@@ -77,11 +194,11 @@ export async function stageCheckFindings(wt, mainBranch, exemptPaths) {
|
|
|
77
194
|
* (plans/parallel-work-instances.md "Claims", part 4/7): the assigned entry is the instance's
|
|
78
195
|
* one task, so moving another is the same class of mistake `backlogStructureReason` catches
|
|
79
196
|
* and costs one fix-up turn before the change commits. undefined when nothing moved or the
|
|
80
|
-
* claimed entry itself moved — a
|
|
197
|
+
* claimed entry itself moved — a merged entry is the expected way to release a claim. */
|
|
81
198
|
export function assignedMovedFinding(claimKey, moved) {
|
|
82
199
|
if (moved.length === 0 || moved.some((m) => m.key === claimKey))
|
|
83
200
|
return undefined;
|
|
84
|
-
return `this
|
|
201
|
+
return `this run is assigned one backlog entry but moved a different one: "${moved[0].title}"`;
|
|
85
202
|
}
|
|
86
203
|
/** The staging finding for an unassigned tick that moved a backlog entry a sibling instance
|
|
87
204
|
* holds — one the poll listed in the tick's `<held-entries>` note (LoopState.heldElsewhere).
|
|
@@ -89,13 +206,13 @@ export function assignedMovedFinding(claimKey, moved) {
|
|
|
89
206
|
* point as assignedMovedFinding (BUGS.md 2026-10-12: 24 of 29 staged claims duplicated a
|
|
90
207
|
* sibling's held entry, and each duplicate cost a gate check, a review, and a conflict
|
|
91
208
|
* resolver run holding the merge slot). The fix-up turn is asked to drop the duplicate work.
|
|
92
|
-
* A change that held nothing else then ends with no change to
|
|
209
|
+
* A change that held nothing else then ends with no change to merge. undefined when no moved
|
|
93
210
|
* entry is held. */
|
|
94
211
|
export function heldMovedFinding(heldKeys, moved) {
|
|
95
212
|
const dup = moved.find((m) => heldKeys.has(m.key));
|
|
96
213
|
if (dup === undefined)
|
|
97
214
|
return undefined;
|
|
98
|
-
return (`this
|
|
215
|
+
return (`this run moved a backlog entry another instance of this role is working on: "${dup.title}" — it is that ` +
|
|
99
216
|
`instance's task (see <held-entries>), so revert every edit made for it, including the ` +
|
|
100
217
|
`entry's move, and leave nothing behind if that was the whole change`);
|
|
101
218
|
}
|
|
@@ -138,7 +255,7 @@ async function gitLevelFindings(wt, mainBranch) {
|
|
|
138
255
|
}
|
|
139
256
|
finally {
|
|
140
257
|
// Restore the index to HEAD. Without this, the tick's change detection (changedFiles and
|
|
141
|
-
//
|
|
258
|
+
// stageTickMerge's "nothing left to merge" guard) reads the stale index and reports a
|
|
142
259
|
// fix-up that reverted the whole change as still changed, so an empty tree would reach
|
|
143
260
|
// commitAll instead of the tick ending no_change.
|
|
144
261
|
await gitTry(wt, "reset");
|
|
@@ -146,7 +263,8 @@ async function gitLevelFindings(wt, mainBranch) {
|
|
|
146
263
|
}
|
|
147
264
|
/** (a) References left behind by a rename or delete: for each `D` path and each `R` origin,
|
|
148
265
|
* `git grep --cached` the old path across the staged tree and report up to MAX_STALE_HITS
|
|
149
|
-
* `file:line` hits. Renames report the path they moved to;
|
|
266
|
+
* `file:line` hits outside record prose (isRecordProse). Renames report the path they moved to;
|
|
267
|
+
* deletes report the deletion. */
|
|
150
268
|
async function staleReferenceFindings(wt, entries) {
|
|
151
269
|
const findings = [];
|
|
152
270
|
let reported = 0;
|
|
@@ -164,7 +282,7 @@ async function staleReferenceFindings(wt, entries) {
|
|
|
164
282
|
const hits = [];
|
|
165
283
|
for (const line of out.split("\n")) {
|
|
166
284
|
const m = /^([^:]+):(\d+):(.*)$/.exec(line);
|
|
167
|
-
if (!m || !hasStandalonePathOccurrence(m[3] ?? "", old))
|
|
285
|
+
if (!m || isRecordProse(m[1]) || !hasStandalonePathOccurrence(m[3] ?? "", old))
|
|
168
286
|
continue;
|
|
169
287
|
hits.push(`${m[1]}:${m[2]}`);
|
|
170
288
|
if (hits.length >= MAX_STALE_HITS)
|
|
@@ -198,6 +316,13 @@ function hasStandalonePathOccurrence(line, needle) {
|
|
|
198
316
|
}
|
|
199
317
|
return false;
|
|
200
318
|
}
|
|
319
|
+
/** Each distinct path-shaped token in `text`, in first-appearance order: at least one slash and
|
|
320
|
+
* a final `.<ext>` segment. Scanned inside the text (not whitespace-split) so surrounding
|
|
321
|
+
* backticks, brackets, and sentence punctuation fall away with the match. */
|
|
322
|
+
export function pathTokens(text) {
|
|
323
|
+
const tokenRe = /[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+\.[A-Za-z0-9]+/g;
|
|
324
|
+
return [...new Set([...text.matchAll(tokenRe)].map((m) => m[0]))];
|
|
325
|
+
}
|
|
201
326
|
/** (b) Nonexistent paths: every path-shaped token on an added diff line whose first segment is
|
|
202
327
|
* a tracked top-level directory but which is absent from the staged tree. Rename origins and
|
|
203
328
|
* deleted paths are excluded — "moved from X" prose names them legitimately. */
|
|
@@ -222,10 +347,6 @@ async function missingPathFindings(wt, diff, entries) {
|
|
|
222
347
|
}
|
|
223
348
|
const seen = new Set();
|
|
224
349
|
const missing = [];
|
|
225
|
-
// A path-shaped token: at least one slash and a final `.<ext>` segment. Scanned inside the
|
|
226
|
-
// line (not whitespace-split) so surrounding backticks, brackets, and sentence punctuation
|
|
227
|
-
// fall away with the match.
|
|
228
|
-
const tokenRe = /[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+\.[A-Za-z0-9]+/g;
|
|
229
350
|
let file = null;
|
|
230
351
|
for (const line of diff.split("\n")) {
|
|
231
352
|
if (line.startsWith("+++ ")) {
|
|
@@ -235,12 +356,12 @@ async function missingPathFindings(wt, diff, entries) {
|
|
|
235
356
|
if (!line.startsWith("+") || line.startsWith("+++"))
|
|
236
357
|
continue;
|
|
237
358
|
// A test file's fixtures routinely name paths from the software under test, which need
|
|
238
|
-
// not exist here
|
|
239
|
-
//
|
|
240
|
-
|
|
359
|
+
// not exist here, and record prose names the paths a plan proposes to create; the
|
|
360
|
+
// added-nonexistent-path heuristic targets real references, so it spares added lines in
|
|
361
|
+
// test/ and in record prose (isRecordProse).
|
|
362
|
+
if (file !== null && (file.startsWith("test/") || isRecordProse(file)))
|
|
241
363
|
continue;
|
|
242
|
-
for (const
|
|
243
|
-
const p = m[0];
|
|
364
|
+
for (const p of pathTokens(line.slice(1))) {
|
|
244
365
|
const top = p.slice(0, p.indexOf("/"));
|
|
245
366
|
if (!topDirs.has(top) || oldPaths.has(p) || staged.has(p) || seen.has(p))
|
|
246
367
|
continue;
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
/** The `telemetry` role's tick-time evidence: the failure digest rendered over the role's own
|
|
2
|
-
*
|
|
2
|
+
* trailing 24 h window. Split out of src/failure/failure-render.ts — which stays the pure
|
|
3
3
|
* renderer — so every observer role's evidence builder has its own module the way
|
|
4
4
|
* qa-coverage.ts and backlog-structure.ts do, and the tick lifecycle (tick-prompt.ts) injects
|
|
5
5
|
* it from there instead of reaching into a report module. */
|
|
6
|
-
import {
|
|
6
|
+
import { collectFailureReportSince } from "../failure/failure-data.js";
|
|
7
7
|
import { renderFailureMarkdown } from "../failure/failure-render.js";
|
|
8
|
-
/** The `telemetry` role's own digest window
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
8
|
+
/** The `telemetry` role's own digest window: the trailing 24 h ending now, not the local
|
|
9
|
+
* calendar day. A calendar day starts at midnight, so a run at 06:32 would never see the
|
|
10
|
+
* previous evening. The CLI keeps the usage report's 14-day default; the role reads 24 h so a
|
|
11
|
+
* cluster re-surfaces only while it is live. The collector's equal-length prior window is what
|
|
12
|
+
* makes the delta line meaningful. */
|
|
13
|
+
export const TELEMETRY_DIGEST_MS = 24 * 60 * 60 * 1000;
|
|
13
14
|
/** The `telemetry` role's tick-time evidence: the failure digest rendered over its own
|
|
14
|
-
*
|
|
15
|
+
* trailing 24 h window. A missing or corrupt log omits the block (undefined) and never fails the
|
|
15
16
|
* tick — an observer must not break on bookkeeping (plans/telemetry-role.md). The window and
|
|
16
17
|
* the swallow-errors policy live with the digest, not in the tick lifecycle that injects it. */
|
|
17
18
|
export function telemetryDigest(root) {
|
|
18
19
|
try {
|
|
19
|
-
return renderFailureMarkdown(
|
|
20
|
+
return renderFailureMarkdown(collectFailureReportSince(root, TELEMETRY_DIGEST_MS));
|
|
20
21
|
}
|
|
21
22
|
catch {
|
|
22
23
|
return undefined;
|