tumwater 0.4.2 → 0.4.4
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 +6 -6
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-backend.js +6 -6
- package/dist/src/agent/{run-agent.js → agent-run.js} +27 -13
- package/dist/src/agent/{run-settle.js → agent-settle.js} +48 -24
- package/dist/src/agent/{run-spawn.js → agent-spawn.js} +21 -12
- package/dist/src/agent/{run-stream.js → agent-stream.js} +4 -4
- package/dist/src/agent/backend-traits.js +2 -2
- package/dist/src/agent/claude/claude-account.js +6 -27
- package/dist/src/agent/claude/claude-backend.js +3 -3
- package/dist/src/agent/claude/claude-env.js +27 -31
- package/dist/src/agent/claude/claude-stream.js +111 -54
- package/dist/src/agent/codex/codex-account.js +8 -0
- package/dist/src/agent/codex/codex-args.js +5 -5
- package/dist/src/agent/codex/codex-backend.js +2 -2
- package/dist/src/agent/codex/codex-env.js +28 -15
- package/dist/src/agent/codex/codex-hook-trust.js +39 -20
- package/dist/src/agent/codex/codex-rollout.js +93 -56
- package/dist/src/agent/codex/codex-stream.js +1 -1
- package/dist/src/agent/env-scrub.js +18 -0
- package/dist/src/agent/image-input.js +68 -11
- package/dist/src/agent/notes-shim.js +2 -2
- package/dist/src/agent/opencode/opencode-backend.js +26 -24
- package/dist/src/agent/opencode/opencode-catalog.js +85 -36
- package/dist/src/agent/opencode/opencode-config.js +32 -14
- package/dist/src/agent/opencode/opencode-env.js +12 -13
- package/dist/src/agent/opencode/opencode-stream.js +47 -25
- package/dist/src/agent/session-ledger.js +23 -27
- package/dist/src/agent/subscription-account.js +2 -83
- package/dist/src/backlog/backlog-archive.js +3 -4
- package/dist/src/backlog/backlog-cli-view.js +81 -41
- package/dist/src/backlog/backlog-cli.js +57 -26
- package/dist/src/backlog/backlog-eligibility.js +56 -122
- package/dist/src/backlog/backlog-md.js +5 -5
- package/dist/src/backlog/backlog-parts.js +65 -27
- package/dist/src/backlog/{question-answer.js → backlog-question-answer.js} +1 -1
- package/dist/src/backlog/backlog-release.js +12 -12
- package/dist/src/backlog/backlog-structure.js +114 -61
- package/dist/src/backlog/backlog.js +3 -2
- package/dist/src/baseline/main-baseline.js +148 -84
- package/dist/src/baseline/main-guardrails.js +2 -2
- package/dist/src/baseline/main-watch.js +84 -50
- package/dist/src/brief/brief.js +26 -13
- package/dist/src/budget/budget-fallback-view.js +100 -243
- package/dist/src/budget/budget-wiring.js +29 -65
- package/dist/src/budget/budget.js +4 -5
- package/dist/src/budget/fallback-breaker.js +2 -2
- package/dist/src/build/build-check-detect.js +43 -14
- package/dist/src/build/build-check-events.js +2 -2
- package/dist/src/build/build-check-report.js +10 -0
- package/dist/src/build/build-check-scoped.js +40 -18
- package/dist/src/build/build-check.js +13 -5
- package/dist/src/build/build-stage.js +6 -4
- package/dist/src/change/change-data.js +1 -4
- package/dist/src/cli/cli-command-args.js +89 -52
- package/dist/src/cli/cli-completion-command.js +75 -0
- package/dist/src/cli/{config-commands.js → cli-config-commands.js} +82 -71
- package/dist/src/cli/cli-doctor-command.js +103 -0
- package/dist/src/cli/cli-flag-specs.js +42 -22
- package/dist/src/cli/cli-init.js +36 -14
- package/dist/src/cli/{log-commands.js → cli-log-commands.js} +43 -23
- package/dist/src/cli/{notify-command.js → cli-notify-command.js} +2 -2
- package/dist/src/cli/cli-query-commands.js +2 -2
- package/dist/src/cli/{question-commands.js → cli-question-commands.js} +86 -60
- package/dist/src/cli/cli-repo-gate.js +2 -2
- package/dist/src/cli/cli-role-command.js +2 -41
- package/dist/src/cli/cli-start-summary.js +4 -3
- package/dist/src/cli/cli-start-supervisor.js +190 -0
- package/dist/src/cli/cli-start.js +94 -244
- package/dist/src/cli/completion.js +85 -49
- package/dist/src/cli/follow-view.js +20 -2
- package/dist/src/cli/help.js +6 -13
- package/dist/src/cli.js +70 -176
- package/dist/src/collections.js +39 -27
- package/dist/src/concurrency/check-slots.js +70 -0
- package/dist/src/concurrency/lock.js +23 -1
- package/dist/src/concurrency/run-permit.js +49 -0
- package/dist/src/concurrency/semaphore.js +46 -250
- package/dist/src/config/config-editable-keys.js +4 -7
- package/dist/src/config/config-fallbacks.js +81 -101
- package/dist/src/config/config-field-checks.js +66 -37
- package/dist/src/config/config-keys.js +66 -40
- package/dist/src/config/config-live.js +4 -28
- package/dist/src/config/{model-selector.js → config-model-selector.js} +4 -4
- package/dist/src/config/config-request.js +163 -102
- package/dist/src/config/config-schema.js +10 -12
- package/dist/src/config/config-selector-checks.js +1 -1
- package/dist/src/config/config-validation-agents.js +2 -2
- package/dist/src/config/config-validation-roles.js +8 -72
- package/dist/src/config/config-validation.js +4 -39
- package/dist/src/config/config-views.js +26 -50
- package/dist/src/config/config-write.js +128 -102
- package/dist/src/config/config.js +5 -14
- package/dist/src/doctor/doctor-backlog-bugs.js +7 -6
- package/dist/src/doctor/doctor-backlog-plans.js +53 -120
- package/dist/src/doctor/doctor-backlog.js +16 -14
- package/dist/src/doctor/doctor-check-names.js +3 -6
- package/dist/src/doctor/doctor-checks.js +8 -8
- package/dist/src/doctor/doctor-claude-checks.js +70 -36
- package/dist/src/doctor/doctor-codex-checks.js +6 -6
- package/dist/src/doctor/doctor-contention-checks.js +57 -117
- package/dist/src/doctor/doctor-fallback-checks.js +48 -26
- package/dist/src/doctor/doctor-holds.js +8 -47
- package/dist/src/doctor/doctor-install.js +1 -1
- package/dist/src/doctor/doctor-launch-services.js +1 -1
- package/dist/src/doctor/doctor-model-checks.js +178 -94
- package/dist/src/doctor/doctor-notify.js +2 -2
- package/dist/src/doctor/doctor-opencode-checks.js +5 -4
- package/dist/src/doctor/doctor-orphans.js +122 -21
- package/dist/src/doctor/{outcome.js → doctor-outcome.js} +7 -0
- package/dist/src/doctor/doctor-pi-checks.js +2 -2
- package/dist/src/doctor/doctor-principles.js +3 -3
- package/dist/src/doctor/doctor-project-checks.js +24 -11
- package/dist/src/doctor/doctor-role-notes.js +1 -1
- package/dist/src/doctor/doctor.js +8 -11
- package/dist/src/events/event-format.js +44 -64
- package/dist/src/events/event-labels.js +1 -5
- package/dist/src/events/event-legacy.js +3 -5
- package/dist/src/events/{notify.js → event-notify.js} +6 -6
- package/dist/src/events/event-read.js +1 -1
- package/dist/src/events/event-window.js +1 -1
- package/dist/src/events/events.js +8 -0
- package/dist/src/failure/failure-cluster.js +9 -18
- package/dist/src/failure/failure-data.js +12 -8
- package/dist/src/failure/failure-render.js +46 -36
- package/dist/src/failure/failure-state-change.js +54 -120
- package/dist/src/failure/{time-spend.js → failure-time-spend.js} +117 -84
- package/dist/src/files/files.js +30 -0
- package/dist/src/files/json-object.js +8 -8
- package/dist/src/files/stat-cache.js +12 -8
- package/dist/src/gates/budget-gates.js +85 -84
- package/dist/src/gates/gate-polls.js +32 -74
- package/dist/src/gates/pause-gates.js +16 -2
- package/dist/src/gates/readiness.js +1 -1
- package/dist/src/gates/role-cap-gates.js +44 -35
- package/dist/src/gates/{startup-gate.js → startup-gates.js} +9 -8
- package/dist/src/gates/{streak-gate.js → streak-gates.js} +75 -52
- package/dist/src/git/git-diff.js +6 -2
- package/dist/src/git/git.js +75 -45
- package/dist/src/git/worktree-pool.js +3 -6
- package/dist/src/gui/gui-admin-endpoints.js +3 -3
- package/dist/src/gui/gui-args.js +12 -0
- package/dist/src/gui/gui-backlog-commands.js +4 -4
- package/dist/src/gui/gui-backlog-endpoints.js +61 -41
- package/dist/src/gui/gui-command.js +43 -24
- package/dist/src/gui/gui-config-commands.js +2 -64
- package/dist/src/gui/gui-endpoint-commands.js +1 -1
- package/dist/src/gui/gui-endpoint-common.js +19 -1
- package/dist/src/gui/gui-history-endpoints.js +100 -52
- package/dist/src/gui/gui-operator-commands.js +43 -48
- package/dist/src/gui/gui-prompt-commands.js +25 -25
- package/dist/src/gui/gui-report-endpoints.js +1 -1
- package/dist/src/gui/gui-server.js +1 -2
- package/dist/src/gui/gui-team-commands.js +2 -1
- package/dist/src/history/history-data.js +71 -44
- package/dist/src/history/history-queued.js +49 -27
- package/dist/src/history/history.js +54 -36
- package/dist/src/inbox/prompt-commands.js +59 -42
- package/dist/src/init/init.js +88 -40
- package/dist/src/merge/merge-backlog-conflicts.js +1 -1
- package/dist/src/merge/merge-check-failures.js +12 -9
- package/dist/src/merge/merge-core.js +7 -32
- package/dist/src/merge/merge-drain.js +11 -13
- package/dist/src/merge/merge-pipeline.js +6 -7
- package/dist/src/merge/merge-post-review-drain.js +11 -14
- package/dist/src/merge/merge-queue-render.js +3 -3
- package/dist/src/merge/merge-requeue.js +1 -1
- package/dist/src/merge/merge-slot.js +0 -8
- package/dist/src/merge/merge-stack.js +17 -18
- package/dist/src/merge/merge-to-main.js +13 -58
- package/dist/src/merge/merge-verify.js +12 -21
- package/dist/src/notes-cli.js +52 -42
- package/dist/src/operator/operator-commands.js +80 -47
- package/dist/src/operator/operator-intent.js +3 -3
- package/dist/src/orchestrator/orchestrator-launch.js +31 -63
- package/dist/src/orchestrator/orchestrator-poll.js +44 -69
- package/dist/src/orchestrator/orchestrator-scheduling.js +5 -9
- package/dist/src/orchestrator/orchestrator.js +8 -21
- package/dist/src/orchestrator/scheduling-admit.js +101 -68
- package/dist/src/orchestrator/scheduling-scope.js +18 -27
- package/dist/src/orchestrator/scheduling-skip.js +10 -14
- package/dist/src/orchestrator/startup-migrations.js +1 -1
- package/dist/src/paths.js +10 -9
- package/dist/src/pi/pi-args.js +5 -2
- package/dist/src/pi/pi-backend.js +2 -2
- package/dist/src/pi/pi-bin.js +3 -3
- package/dist/src/pi/pi-event-line.js +28 -1
- package/dist/src/pi/pi-listed-models.js +13 -0
- package/dist/src/pi/pi-models.js +43 -29
- package/dist/src/pi/pi-settings.js +43 -20
- package/dist/src/pi/pi-stream.js +41 -8
- package/dist/src/pi/pi-watchdogs.js +43 -15
- package/dist/src/pi/pi.js +2 -2
- package/dist/src/pi-extension/context-shake.js +32 -20
- package/dist/src/pi-extension/kill-guard.js +69 -0
- package/dist/src/process/process-group.js +51 -26
- package/dist/src/process/process.js +20 -12
- package/dist/src/process/run-marker.js +33 -15
- package/dist/src/process/supervisor.js +40 -28
- package/dist/src/prompt/dialect.js +1 -1
- package/dist/src/prompt/gate-notes.js +2 -2
- package/dist/src/prompt/gate-prompts.js +1 -44
- package/dist/src/prompt/notes-backlog.js +66 -37
- package/dist/src/prompt/principles.js +6 -4
- package/dist/src/prompt/prompt-followup.js +12 -4
- package/dist/src/prompt/prompt.js +88 -38
- package/dist/src/redeploy/redeployer.js +21 -1
- package/dist/src/report/report-data.js +53 -37
- package/dist/src/report/report-fold.js +16 -9
- package/dist/src/report/report-render.js +21 -194
- package/dist/src/report/report-since-data.js +4 -3
- package/dist/src/report/report-since-render.js +173 -0
- package/dist/src/report/report-usage-fold.js +8 -8
- package/dist/src/report/report.js +46 -22
- package/dist/src/review/diff-rules.js +35 -21
- package/dist/src/review/post-merge-review-render.js +9 -3
- package/dist/src/review/post-merge-review.js +48 -41
- package/dist/src/review/review-precheck.js +14 -4
- package/dist/src/review/review.js +48 -170
- package/dist/src/review/secret-leaks.js +26 -16
- package/dist/src/roles/legacy-role-migration.js +6 -3
- package/dist/src/roles/role-catalog.js +11 -3
- package/dist/src/roles/role-guidance.js +16 -12
- package/dist/src/roles/role-render.js +7 -46
- package/dist/src/roles/role-view.js +10 -33
- package/dist/src/run/acceptance-greps.js +88 -45
- package/dist/src/run/ci-parity.js +19 -8
- package/dist/src/run/run-apply.js +12 -15
- package/dist/src/run/run-detail-data.js +121 -68
- package/dist/src/run/run-detail-render.js +92 -35
- package/dist/src/run/run-detail.js +72 -41
- package/dist/src/run/run-prompt.js +63 -50
- package/dist/src/run/run-resume.js +76 -29
- package/dist/src/run/run-stage.js +141 -90
- package/dist/src/run/run-timing.js +8 -2
- package/dist/src/run/run-usage.js +31 -1
- package/dist/src/run/run-verdict.js +128 -50
- package/dist/src/run/stage-check.js +60 -50
- package/dist/src/run/stage-checks-shared.js +28 -0
- package/dist/src/run/stage-name-checks.js +111 -92
- package/dist/src/run/stage-path-checks.js +69 -42
- package/dist/src/run/stage-prose-checks.js +55 -40
- package/dist/src/runner/{leftover.js → runner-leftover.js} +9 -26
- package/dist/src/runner/runner-outcome.js +7 -2
- package/dist/src/runner/runner-pi.js +9 -22
- package/dist/src/runner/runner-recovery.js +5 -13
- package/dist/src/runner/{revision.js → runner-revision.js} +1 -2
- package/dist/src/runner/runner-run.js +103 -50
- package/dist/src/runner/runner-state-load.js +18 -28
- package/dist/src/runner/runner-state.js +0 -14
- package/dist/src/runner/runner.js +41 -25
- package/dist/src/scheduling/backoff.js +8 -3
- package/dist/src/scheduling/claim-entries.js +2 -1
- package/dist/src/scheduling/claims.js +0 -4
- package/dist/src/scheduling/scheduling.js +53 -66
- package/dist/src/status/status-budget.js +4 -43
- package/dist/src/status/status-data.js +35 -39
- package/dist/src/status/status-role-notes.js +2 -2
- package/dist/src/status/status-roles.js +9 -18
- package/dist/src/team/{error-storm.js → team-error-storm.js} +1 -1
- package/dist/src/team/{failure-spread.js → team-failure-spread.js} +5 -5
- package/dist/src/team/team-hold.js +105 -70
- package/dist/src/team/{orchestrator-info.js → team-orchestrator-info.js} +55 -36
- package/dist/src/team/team-polls.js +53 -201
- package/dist/src/team/{reclaim.js → team-reclaim.js} +61 -31
- package/dist/src/team/team-state.js +4 -13
- package/dist/src/text/datetime.js +16 -7
- package/dist/src/text/markdown.js +1 -1
- package/dist/src/text/phrases.js +29 -15
- package/dist/src/text/suggest.js +1 -1
- package/dist/src/text/text.js +6 -5
- package/dist/src/ui/alert-feeds.js +46 -111
- package/dist/src/ui/badges.js +17 -88
- package/dist/src/ui/gui/gui-client-boot.js +2 -8
- package/dist/src/ui/gui/gui-client-composer.js +1 -1
- package/dist/src/ui/gui/gui-client-drawer-config.js +8 -33
- package/dist/src/ui/gui/gui-client-drawer.js +1 -1
- package/dist/src/ui/gui/gui-client-feed.js +1 -1
- package/dist/src/ui/gui/gui-client-history.js +28 -14
- package/dist/src/ui/gui/gui-client-model.js +0 -12
- package/dist/src/ui/gui/gui-client-operator.js +7 -5
- package/dist/src/ui/gui/gui-client-pending.js +7 -6
- package/dist/src/ui/gui/gui-client-report-chart.js +5 -5
- package/dist/src/ui/gui/gui-client-report.js +1 -1
- package/dist/src/ui/gui/gui-client-roles.js +0 -1
- package/dist/src/ui/gui/gui-client-settings.js +43 -84
- package/dist/src/ui/gui/gui-client-team.js +12 -6
- package/dist/src/ui/gui/gui-client.js +7 -1
- package/dist/src/ui/progress-data.js +107 -44
- package/dist/src/ui/run-progress-model.js +20 -4
- package/dist/src/ui/status-model.js +2 -24
- package/dist/src/ui/status-payload.js +11 -30
- package/dist/src/ui/status-render.js +13 -24
- package/dist/src/ui/team-alert-backlog.js +14 -204
- package/dist/src/ui/team-alert-shared.js +6 -29
- package/dist/src/ui/team-alert-status.js +9 -170
- package/dist/src/ui/team-alert-tail.js +19 -215
- package/dist/src/ui/team-alerts.js +8 -15
- package/dist/src/ui/transcript-run.js +27 -16
- package/dist/src/ui/tui/tui-backlog.js +2 -2
- package/dist/src/ui/tui/tui-editor-answer.js +12 -10
- package/dist/src/ui/tui/tui-editor-attach.js +4 -3
- package/dist/src/ui/tui/tui-editor-backends.js +4 -3
- package/dist/src/ui/tui/tui-editor-brief.js +6 -9
- package/dist/src/ui/tui/tui-editor-budget.js +4 -3
- package/dist/src/ui/tui/tui-editor-config.js +80 -68
- package/dist/src/ui/tui/tui-editor-entry.js +12 -20
- package/dist/src/ui/tui/tui-editor-pause.js +51 -57
- package/dist/src/ui/tui/tui-editor-prompt.js +9 -13
- package/dist/src/ui/tui/tui-editor-queued.js +11 -14
- package/dist/src/ui/tui/tui-editor-quiet.js +4 -3
- package/dist/src/ui/tui/tui-editor-release.js +4 -3
- package/dist/src/ui/tui/tui-editor-retire.js +7 -7
- package/dist/src/ui/tui/tui-editor-role-enabled.js +4 -3
- package/dist/src/ui/tui/tui-editor-role-file.js +4 -4
- package/dist/src/ui/tui/tui-editor-role-key.js +4 -3
- package/dist/src/ui/tui/tui-editor-role-model.js +97 -15
- package/dist/src/ui/tui/tui-editor-search.js +4 -3
- package/dist/src/ui/tui/tui-editor-session.js +17 -4
- package/dist/src/ui/tui/tui-editors.js +5 -10
- package/dist/src/ui/tui/tui-frame.js +182 -134
- package/dist/src/ui/tui/tui-guard.js +20 -0
- package/dist/src/ui/tui/tui-input.js +167 -98
- package/dist/src/ui/tui/tui-keymap.js +51 -29
- package/dist/src/ui/tui/tui-keys.js +1 -1
- package/dist/src/ui/tui/tui-pane-activity.js +5 -4
- package/dist/src/ui/tui/tui-pane-backlog.js +74 -36
- package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
- package/dist/src/ui/tui/tui-pane-health.js +252 -230
- package/dist/src/ui/tui/tui-pane-history.js +67 -74
- package/dist/src/ui/tui/tui-pane-pending.js +288 -247
- package/dist/src/ui/tui/tui-pane-queued.js +25 -27
- package/dist/src/ui/tui/tui-pane-report.js +2 -1
- package/dist/src/ui/tui/tui-pane-session.js +15 -10
- package/dist/src/ui/tui/tui-pane-transcript.js +19 -31
- package/dist/src/ui/tui/tui-pane.js +14 -11
- package/dist/src/ui/tui/tui-panes.js +38 -33
- package/dist/src/ui/tui/tui.js +4 -7
- package/dist/src/verdict/fix-claim.js +107 -56
- package/dist/src/verdict/refusal.js +1 -1
- package/dist/src/verdict/reply-contract.js +3 -4
- package/package.json +5 -5
- package/dist/src/concurrency/provider-lease.js +0 -105
- package/dist/src/review/review-followup.js +0 -87
- package/dist/src/review/review-run.js +0 -395
- package/dist/src/runner/model-fallback.js +0 -76
- package/dist/src/runner/runner-fallback.js +0 -66
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +0 -24
- package/dist/src/ui/tui/tui-editor-provider-caps.js +0 -27
- /package/dist/src/cli/{brief-command.js → cli-brief-command.js} +0 -0
- /package/dist/src/doctor/{probe.js → doctor-probe.js} +0 -0
- /package/dist/src/gates/{disk-gate.js → disk-gates.js} +0 -0
- /package/dist/src/operator/{retire.js → operator-retire.js} +0 -0
- /package/dist/src/operator/{revert.js → operator-revert.js} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { imageReroute } from "../agent/image-input.js";
|
|
1
2
|
import { DIRECTOR_ROLE } from "../roles/roles.js";
|
|
2
3
|
import { baseRoleOf } from "../roles/runner-ids.js";
|
|
3
4
|
import { isDirty, deleteRef } from "../git/git.js";
|
|
@@ -9,6 +10,7 @@ import { errorMessage } from "../text/text.js";
|
|
|
9
10
|
import { recordParity } from "./ci-parity.js";
|
|
10
11
|
import { resetWorktreeToMain } from "../git/worktree.js";
|
|
11
12
|
import { recordDismissedFiling, recordDismissedFlow } from "./run-dismiss.js";
|
|
13
|
+
import { readPiListedModels } from "../pi/pi-listed-models.js";
|
|
12
14
|
import { rejectedRefName } from "../paths.js";
|
|
13
15
|
import { logEventBestEffort } from "../events/events.js";
|
|
14
16
|
import { subscriptionWindowRejected } from "../agent/claude/claude-account.js";
|
|
@@ -23,6 +25,17 @@ function abortWithResume(s, ctx, pi, lastError, result, resumeCause) {
|
|
|
23
25
|
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
24
26
|
return { result, resumeCause };
|
|
25
27
|
}
|
|
28
|
+
/** End a run as an unfulfilled error: record `lastError`, re-queue the raw prompt for a later
|
|
29
|
+
* fresh run (requeueUnfulfilled) and return the error outcome — the terminal the timeout and
|
|
30
|
+
* image-blind arms share, filed here so the "record lastError, re-queue unfulfilled, end as
|
|
31
|
+
* error" contract cannot drift between them (the resume-arming twin is abortWithResume above;
|
|
32
|
+
* failedRunArm's error path keeps its own split because it arms a resume for a dirty worktree
|
|
33
|
+
* and sets lastError before that choice). */
|
|
34
|
+
function unfulfilledError(s, ctx, message) {
|
|
35
|
+
s.lastError = message;
|
|
36
|
+
ctx.pending.requeueUnfulfilled(ctx.userPrompt);
|
|
37
|
+
return { result: "error" };
|
|
38
|
+
}
|
|
26
39
|
/** Classify what the finished run left behind; null means the run is fulfillable and staging
|
|
27
40
|
* should take over. Never throws: every branch returns an outcome, recording what went wrong
|
|
28
41
|
* in `state.lastError` or a warning event. */
|
|
@@ -134,11 +147,9 @@ function progressingTimeoutArm(ctx) {
|
|
|
134
147
|
function timeoutArm(ctx) {
|
|
135
148
|
const { pi, state: s } = ctx;
|
|
136
149
|
if (pi.timedOut) {
|
|
137
|
-
s.lastError = pi.errorMessage ?? "timed out";
|
|
138
150
|
// The request never ran to completion and no work merged: put it back so the next
|
|
139
151
|
// run retries it. (A killed run's half-done edits are discarded by the reset.)
|
|
140
|
-
|
|
141
|
-
return { result: "error" };
|
|
152
|
+
return unfulfilledError(s, ctx, pi.errorMessage ?? "timed out");
|
|
142
153
|
}
|
|
143
154
|
return undefined;
|
|
144
155
|
}
|
|
@@ -178,22 +189,73 @@ function budgetCappedArm(ctx) {
|
|
|
178
189
|
}
|
|
179
190
|
return undefined;
|
|
180
191
|
}
|
|
192
|
+
/** The failure message a run that ended on a blind read names when the agent gave none of its
|
|
193
|
+
* own: the image path the model could not see, unless the kill carried a more specific reason
|
|
194
|
+
* (pi.errorMessage). Shared by the two failing arms of imageBlindArm below — the one-hop-already
|
|
195
|
+
* branch and the no-reroute-target branch — so the "could not read the image" wording cannot
|
|
196
|
+
* drift between them. The reroute arm does NOT use it: its own lastError names the target's
|
|
197
|
+
* tier too. */
|
|
198
|
+
function blindReadFailureMessage(pi, path) {
|
|
199
|
+
return pi.errorMessage ?? `read ${path}, but the model cannot read images`;
|
|
200
|
+
}
|
|
181
201
|
/** The image-blind decision: a run stopped the moment its agent read an image its model cannot
|
|
182
|
-
* see
|
|
183
|
-
*
|
|
184
|
-
* the run"). */
|
|
202
|
+
* see. One hop to an image-capable reroute target is taken when one exists; otherwise the
|
|
203
|
+
* run fails and the prompt re-queues (plans/image-input.md "Failing the run"). */
|
|
185
204
|
function imageBlindArm(ctx) {
|
|
186
205
|
const { pi, state: s } = ctx;
|
|
187
206
|
// A run the runner killed the moment its agent read an image its model cannot see
|
|
188
207
|
// (plans/image-input.md "Failing the run"): pi strips such an image from the request, so
|
|
189
208
|
// the agent was about to answer about a picture it never saw. The run was deliberately
|
|
190
209
|
// stopped before it could work on, so its half-done edits are discarded exactly as a
|
|
191
|
-
// timed-out run's are — nothing to resume, and the prompt re-queues to run fresh
|
|
192
|
-
//
|
|
210
|
+
// timed-out run's are — nothing to resume, and the prompt re-queues to run fresh on a
|
|
211
|
+
// model that CAN see, with one reroute hop to an image-capable target (the arm below). The
|
|
212
|
+
// error backoff and streak apply as for any error.
|
|
193
213
|
if (pi.imageBlind) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
214
|
+
// One hop only (plans/image-input.md "Rerunning on a model that can read images"): a
|
|
215
|
+
// run that ALREADY started on the image-capable target and still read blind is not
|
|
216
|
+
// rerouted a second time — it fails, so a misreadable image cannot bounce the role
|
|
217
|
+
// between tiers forever. Set at run start by runPhase when it routes a run onto the
|
|
218
|
+
// target (blind-read handoff or attachment preflight).
|
|
219
|
+
if (s.imageRerouteActive) {
|
|
220
|
+
// No work was done (the run was killed at the first read): nothing to resume, and the
|
|
221
|
+
// prompt re-queues to run fresh later like any other error. The error backoff/streak
|
|
222
|
+
// apply as usual.
|
|
223
|
+
return unfulfilledError(s, ctx, blindReadFailureMessage(pi, pi.imageBlind.path));
|
|
224
|
+
}
|
|
225
|
+
// Otherwise, when the role's tier has an image-capable reroute target, the failed work
|
|
226
|
+
// is not discarded: the next run starts directly on that target over the kept worktree.
|
|
227
|
+
// The one-shot field carries the target seam plus the `from`/`path` facts the handoff
|
|
228
|
+
// note needs; the run ends `aborted` with the `image-reroute` resume cause, and
|
|
229
|
+
// applyRunOutcome schedules it promptly (nextRunAt = now) while planRunStart hands the
|
|
230
|
+
// kept worktree off to a FRESH session on the target.
|
|
231
|
+
const regime = ctx.rerouteRegime ?? "primary";
|
|
232
|
+
const target = ctx.config !== undefined ? imageReroute(ctx.config, ctx.role, regime, undefined, null, null, readPiListedModels(ctx.root))
|
|
233
|
+
: null;
|
|
234
|
+
if (target) {
|
|
235
|
+
const from = ctx.runModel ?? target.config.model ?? "the model";
|
|
236
|
+
s.imageReroute = {
|
|
237
|
+
tier: target.tier,
|
|
238
|
+
backend: target.config.agent ?? "pi",
|
|
239
|
+
provider: target.config.provider,
|
|
240
|
+
model: target.config.model,
|
|
241
|
+
thinking: target.config.thinking,
|
|
242
|
+
from,
|
|
243
|
+
path: pi.imageBlind.path,
|
|
244
|
+
};
|
|
245
|
+
s.lastError = `read ${pi.imageBlind.path}, but ${from} cannot read images — rerouting to ${target.tier}`;
|
|
246
|
+
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
247
|
+
logEventBestEffort(ctx.root, {
|
|
248
|
+
source: ctx.role,
|
|
249
|
+
type: "image_reroute",
|
|
250
|
+
run: s.runs,
|
|
251
|
+
from,
|
|
252
|
+
to: target.config.model,
|
|
253
|
+
tier: target.tier,
|
|
254
|
+
preflight: false,
|
|
255
|
+
});
|
|
256
|
+
return { result: "aborted", resumeCause: "image-reroute" };
|
|
257
|
+
}
|
|
258
|
+
return unfulfilledError(s, ctx, blindReadFailureMessage(pi, pi.imageBlind.path));
|
|
197
259
|
}
|
|
198
260
|
return undefined;
|
|
199
261
|
}
|
|
@@ -300,51 +362,67 @@ function noChangeArm(ctx, changed) {
|
|
|
300
362
|
// diagnoseNoChange (src/verdict/no-change.ts) tells which, so the warning event below is
|
|
301
363
|
// diagnosable on its own.
|
|
302
364
|
const diagnosis = diagnoseNoChange(pi);
|
|
303
|
-
|
|
304
|
-
ctx.warn(`the agent finished without changes and without declaring nothing-to-do` +
|
|
305
|
-
(diagnosis.notes.length ? ` (${diagnosis.notes.join(", ")})` : ""));
|
|
306
|
-
}
|
|
365
|
+
warnUndeclaredNoChange(ctx, pi, diagnosis.notes);
|
|
307
366
|
// A cut-off run did real work and was NOT fulfilled: a director prompt goes back
|
|
308
367
|
// to the inbox to rerun fresh; a role runner resumes the just-compacted session
|
|
309
368
|
// next run (see the cutOff handling in run()).
|
|
310
369
|
if (diagnosis.cutOff)
|
|
311
370
|
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
371
|
+
recordNoChangeBookkeeping(ctx, pi, diagnosis.cutOff);
|
|
372
|
+
return {
|
|
373
|
+
result: "no_change",
|
|
374
|
+
cutOff: diagnosis.cutOff || undefined,
|
|
375
|
+
summary: noChangeSummary(ctx, pi, diagnosis.cutOff),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
/** Warn when a no-change run declared neither a sentinel nor nothing-to-do, so the non-
|
|
381
|
+
* compliance or truncation the diagnosis saw is diagnosable on its own. */
|
|
382
|
+
function warnUndeclaredNoChange(ctx, pi, notes) {
|
|
383
|
+
if (!pi.nothingToDo) {
|
|
384
|
+
ctx.warn(`the agent finished without changes and without declaring nothing-to-do` +
|
|
385
|
+
(notes.length ? ` (${notes.join(", ")})` : ""));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/** A no-change run's bookkeeping: the qa flow, the filed feedback ledger entry and the ci
|
|
389
|
+
* parity coverage, each recorded only when its observer saw it and the run was not cut off. */
|
|
390
|
+
function recordNoChangeBookkeeping(ctx, pi, cutOff) {
|
|
391
|
+
// A cut-off run did real work but was truncated before declaring its outcome: the FLOW
|
|
392
|
+
// line it left mid-stream is not a finished verdict, so recording it would advance the
|
|
393
|
+
// rotation past a check that did not complete. Only a run that was not cut off records.
|
|
394
|
+
if (ctx.flow && !cutOff) {
|
|
395
|
+
// The ledger only orders the qa menu, so a failed write warns rather than failing the run.
|
|
396
|
+
recordDismissedFlow(ctx, ctx.root, ctx.flow.flow, ctx.flow.result, ctx.flow.result === "bug" ? extractSummary(pi.finalText) ?? undefined : undefined);
|
|
397
|
+
}
|
|
398
|
+
// A fulfilled feedback run that filed records its per-install ledger entry and logs the
|
|
399
|
+
// issue_filed event so the filing is visible in the operator's activity feed. Recording is
|
|
400
|
+
// bookkeeping that never fails the run: a failed ledger write warns and the no_change
|
|
401
|
+
// outcome stands. A cut-off run records nothing — truncation is not a filing the run
|
|
402
|
+
// declared (the same guard the FLOW line carries).
|
|
403
|
+
if (ctx.filed && !cutOff) {
|
|
404
|
+
recordDismissedFiling(ctx, ctx.root, ctx.filed);
|
|
405
|
+
}
|
|
406
|
+
// A fulfilled ci run that declared its coverage records it in the parity ledger so the
|
|
407
|
+
// next fresh session's block knows what the last run covered (part 3/3). The ledger is
|
|
408
|
+
// bookkeeping that never fails the run: a failed write warns and the no_change outcome
|
|
409
|
+
// stands. A cut-off run records nothing — truncation is not a coverage the run declared.
|
|
410
|
+
if (ctx.ci && !cutOff) {
|
|
411
|
+
try {
|
|
412
|
+
recordParity(ctx.root, ctx.ci.sha, ctx.ci.result);
|
|
326
413
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
// bookkeeping that never fails the run: a failed write warns and the no_change outcome
|
|
330
|
-
// stands. A cut-off run records nothing — truncation is not a coverage the run declared.
|
|
331
|
-
if (ctx.ci && !diagnosis.cutOff) {
|
|
332
|
-
try {
|
|
333
|
-
recordParity(ctx.root, ctx.ci.sha, ctx.ci.result);
|
|
334
|
-
}
|
|
335
|
-
catch (err) {
|
|
336
|
-
ctx.warn(`ci parity not recorded: ${errorMessage(err)}`);
|
|
337
|
-
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
ctx.warn(`ci parity not recorded: ${errorMessage(err)}`);
|
|
338
416
|
}
|
|
339
|
-
// A prompt-driven (director) run that answers a question in prose ends `no_change` with
|
|
340
|
-
// no SUMMARY block, so its reply would be lost — the dashboard reads a bare "No change"
|
|
341
|
-
// and the feed carries nothing (BUGS.md 2026-10-14). Keep the prose above the sentinel as
|
|
342
|
-
// the run's summary so `lastSummary` and the run_end event show the answer where the
|
|
343
|
-
// operator asked the question. A role runner's bare nothing-to-do stays summary-less.
|
|
344
|
-
const summary = ctx.userPrompt !== null && !diagnosis.cutOff
|
|
345
|
-
? (extractNoChangeAnswer(pi.finalText) ?? undefined)
|
|
346
|
-
: undefined;
|
|
347
|
-
return { result: "no_change", cutOff: diagnosis.cutOff || undefined, summary };
|
|
348
417
|
}
|
|
349
|
-
|
|
418
|
+
}
|
|
419
|
+
/** A prompt-driven (director) run that answers a question in prose ends `no_change` with
|
|
420
|
+
* no SUMMARY block, so its reply would be lost — the dashboard reads a bare "No change"
|
|
421
|
+
* and the feed carries nothing (BUGS.md 2026-10-14). Keep the prose above the sentinel as
|
|
422
|
+
* the run's summary so `lastSummary` and the run_end event show the answer where the
|
|
423
|
+
* operator asked the question. A role runner's bare nothing-to-do stays summary-less. */
|
|
424
|
+
function noChangeSummary(ctx, pi, cutOff) {
|
|
425
|
+
return ctx.userPrompt !== null && !cutOff
|
|
426
|
+
? (extractNoChangeAnswer(pi.finalText) ?? undefined)
|
|
427
|
+
: undefined;
|
|
350
428
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
/** The merge gate's
|
|
1
|
+
/** The merge gate's checks that still refuse a change, plus staging-only heuristics for what a
|
|
2
|
+
* reviewer tends to reject, run BEFORE a changed run commits.
|
|
2
3
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `falseFixReason` runs only for an exempt (doc-only) diff — the condition under which the gate
|
|
12
|
-
* consults it.
|
|
4
|
+
* Since part 3m/3 the gate refuses only a brief-block edit, a protected edit and a leaked
|
|
5
|
+
* credential — the findings a later commit cannot repair. This module runs those same checks
|
|
6
|
+
* over the run's uncommitted edits while the authoring session can still be continued for one
|
|
7
|
+
* fix-up turn (a finding that reaches merge costs a queue slot, a verification, and a rejection
|
|
8
|
+
* that discards the work — 13 phantom-fix rejections between 2026-09-23 and 2026-10-02 priced
|
|
9
|
+
* that turn). The gate no longer refuses a false fix claim or a broken backlog structure —
|
|
10
|
+
* both are repairable by a later commit and reported by the main watch on main's tip — so
|
|
11
|
+
* those findings never spend the run's fix-up turn here.
|
|
13
12
|
*
|
|
14
13
|
* The git-level checks described next live across stage-path-checks.ts (`gitLevelFindings`),
|
|
15
14
|
* stage-name-checks.ts and stage-prose-checks.ts, which this module appends.
|
|
@@ -20,8 +19,9 @@
|
|
|
20
19
|
* files whose final newline the change removed. These stage the change (`git add -A`) to read
|
|
21
20
|
* the index with git only, then restore the index to HEAD before returning: staging is a read
|
|
22
21
|
* aid for the checks, not a change to the run's state. A git hiccup yields no finding rather
|
|
23
|
-
* than failing the run. Unlike the
|
|
24
|
-
*
|
|
22
|
+
* than failing the run. Unlike the gate's checks this module runs (which the gate also runs at
|
|
23
|
+
* merge time), the gate does not run these: they are heuristics for what a reviewer tends to
|
|
24
|
+
* reject, so neither path check looks inside record
|
|
25
25
|
* prose (isRecordProse), where an old or not-yet-created path is correct as written.
|
|
26
26
|
*
|
|
27
27
|
* A fourth check, `staleNameFindings`, reports a compound identifier the change removed from a
|
|
@@ -59,11 +59,8 @@
|
|
|
59
59
|
import path from "node:path";
|
|
60
60
|
import { changedFiles } from "../git/git-diff.js";
|
|
61
61
|
import { conflictMarkerReason } from "../merge/merge-git.js";
|
|
62
|
-
import { isExemptDiff } from "../review/exemptions.js";
|
|
63
|
-
import { backlogStructureReason } from "../backlog/backlog-structure.js";
|
|
64
62
|
import { protectedEditReason } from "../review/protected-edits.js";
|
|
65
63
|
import { secretLeakReason } from "../review/secret-leaks.js";
|
|
66
|
-
import { falseFixReason } from "../verdict/fix-claim.js";
|
|
67
64
|
import { changeBaseRev, fileContentAt } from "../git/git.js";
|
|
68
65
|
import { readTextOrNull } from "../files/files.js";
|
|
69
66
|
import { FINISHED_ARCHIVES, finishedArchiveOf, parseEntryDetails, } from "../backlog/backlog-md.js";
|
|
@@ -93,35 +90,24 @@ const pushReason = async (findings, check) => {
|
|
|
93
90
|
if (reason)
|
|
94
91
|
findings.push(reason);
|
|
95
92
|
};
|
|
96
|
-
/** The findings
|
|
97
|
-
*
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (files.includes("PLANS.md")) {
|
|
111
|
-
const { head, base } = await headAndBase(wt, mainBranch, "PLANS.md");
|
|
112
|
-
// Finished plans are archived in PLANS-DONE.md (plans/backlog-archive.md): a requires clause
|
|
113
|
-
// naming a merged part resolves there, on both sides of the comparison alike.
|
|
114
|
-
const archived = parseEntryDetails(readTextOrNull(path.join(wt, PLANS_ARCHIVE)) ?? "", "Done").map((e) => e.title);
|
|
115
|
-
const dependency = base === undefined ? undefined : planDependencyFinding(head, base, archived);
|
|
116
|
-
if (dependency !== undefined)
|
|
117
|
-
findings.push(dependency);
|
|
118
|
-
const refusal = base === undefined ? undefined : ungroundedRefusalFinding("PLANS.md", head, base);
|
|
119
|
-
if (refusal !== undefined)
|
|
120
|
-
findings.push(refusal);
|
|
121
|
-
}
|
|
93
|
+
/** The PLANS.md record findings: a requires clause naming a part the archive lacks, and an
|
|
94
|
+
* ungrounded refusal that cites another part. */
|
|
95
|
+
async function plansFindings(wt, mainBranch) {
|
|
96
|
+
const { head, base } = await headAndBase(wt, mainBranch, "PLANS.md");
|
|
97
|
+
// Finished plans are archived in PLANS-DONE.md (plans/backlog-archive.md): a requires clause
|
|
98
|
+
// naming a merged part resolves there, on both sides of the comparison alike.
|
|
99
|
+
const archived = parseEntryDetails(readTextOrNull(path.join(wt, PLANS_ARCHIVE)) ?? "", "Done").map((e) => e.title);
|
|
100
|
+
const dependency = base === undefined ? undefined : planDependencyFinding(head, base, archived);
|
|
101
|
+
const refusal = base === undefined ? undefined : ungroundedRefusalFinding("PLANS.md", head, base);
|
|
102
|
+
return [dependency, refusal].filter((f) => f !== undefined);
|
|
103
|
+
}
|
|
104
|
+
/** The finished-archive findings: duplicates of entries the archive already holds, plus the
|
|
105
|
+
* acceptance greps of the plan entries the change finished (for a PLANS.md change). */
|
|
106
|
+
async function archiveFindings(wt, mainBranch, files) {
|
|
122
107
|
const baseRev = FINISHED_ARCHIVES.some((s) => files.includes(s.file) || files.includes(s.archive))
|
|
123
108
|
? await changeBaseRev(wt, mainBranch).catch(() => undefined)
|
|
124
109
|
: undefined;
|
|
110
|
+
const findings = [];
|
|
125
111
|
for (const spec of FINISHED_ARCHIVES) {
|
|
126
112
|
if (baseRev === undefined || !(files.includes(spec.file) || files.includes(spec.archive)))
|
|
127
113
|
continue;
|
|
@@ -144,12 +130,18 @@ export async function stageCheckFindings(wt, mainBranch, exemptPaths, role) {
|
|
|
144
130
|
})().catch(() => []);
|
|
145
131
|
findings.push(...greps);
|
|
146
132
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
133
|
+
return findings;
|
|
134
|
+
}
|
|
135
|
+
/** The BUGS.md record finding: an ungrounded refusal that cites another bug. */
|
|
136
|
+
async function bugsFindings(wt, mainBranch) {
|
|
137
|
+
const { head, base } = await headAndBase(wt, mainBranch, "BUGS.md");
|
|
138
|
+
const refusal = base === undefined ? undefined : ungroundedRefusalFinding("BUGS.md", head, base);
|
|
139
|
+
return refusal === undefined ? [] : [refusal];
|
|
140
|
+
}
|
|
141
|
+
/** The stray-placeholder findings: a `_None yet._` line a changed record still leaves in a
|
|
142
|
+
* section that holds entries. */
|
|
143
|
+
async function placeholderFindings(wt, mainBranch, files) {
|
|
144
|
+
const findings = [];
|
|
153
145
|
for (const file of PLACEHOLDER_FILES) {
|
|
154
146
|
if (!files.includes(file))
|
|
155
147
|
continue;
|
|
@@ -158,10 +150,28 @@ export async function stageCheckFindings(wt, mainBranch, exemptPaths, role) {
|
|
|
158
150
|
if (stray !== undefined)
|
|
159
151
|
findings.push(stray);
|
|
160
152
|
}
|
|
153
|
+
return findings;
|
|
154
|
+
}
|
|
155
|
+
/** The findings likely to get this uncommitted change rejected at merge, or an empty list when
|
|
156
|
+
* it is clean: the merge gate's remaining checks over the change plus the staging-only
|
|
157
|
+
* heuristics (plan dependencies, path drift). Checks the worktree at `wt` against
|
|
158
|
+
* `mainBranch`'s merge-base.
|
|
159
|
+
*
|
|
160
|
+
* Never throws: a check that fails yields no finding, so a git hiccup at staging time defers to
|
|
161
|
+
* the gate instead of failing the run. */
|
|
162
|
+
export async function stageCheckFindings(wt, mainBranch, role) {
|
|
163
|
+
const files = await changedFiles(wt).catch(() => null);
|
|
164
|
+
if (files === null || files.length === 0)
|
|
165
|
+
return [];
|
|
166
|
+
const findings = [];
|
|
167
|
+
if (files.includes("PLANS.md"))
|
|
168
|
+
findings.push(...(await plansFindings(wt, mainBranch)));
|
|
169
|
+
findings.push(...(await archiveFindings(wt, mainBranch, files)));
|
|
170
|
+
if (files.includes("BUGS.md"))
|
|
171
|
+
findings.push(...(await bugsFindings(wt, mainBranch)));
|
|
172
|
+
findings.push(...(await placeholderFindings(wt, mainBranch, files)));
|
|
161
173
|
await pushReason(findings, protectedEditReason(wt, mainBranch, files, role));
|
|
162
174
|
await pushReason(findings, secretLeakReason(wt, mainBranch, files));
|
|
163
|
-
if (isExemptDiff(files, exemptPaths))
|
|
164
|
-
await pushReason(findings, falseFixReason(wt, mainBranch, files));
|
|
165
175
|
findings.push(...(await gitLevelFindings(wt, mainBranch).catch(() => [])));
|
|
166
176
|
const markers = conflictMarkerReason(wt, files);
|
|
167
177
|
if (markers !== undefined)
|
|
@@ -16,6 +16,34 @@ export const MAX_STALE_PATHS = 10;
|
|
|
16
16
|
export function diffPlusPath(line) {
|
|
17
17
|
return line.slice(4).replace(/^b\//, "");
|
|
18
18
|
}
|
|
19
|
+
/** Parse a `-U0` unified diff into each file's removed and added content lines, marker-stripped.
|
|
20
|
+
* The `diff --git` header names the file, and git's `---`/`+++` old/new path headers carry no
|
|
21
|
+
* content; `@@` hunk headers, context lines and the `\ No newline` marker match neither branch,
|
|
22
|
+
* so the walk records only real `-`/`+` content lines. Consumers keep only what they want — a
|
|
23
|
+
* JS-file filter, a trim-and-min-length rule, a compound-name extraction — from the per-file
|
|
24
|
+
* removed/added lists; stage-path-checks's movedSourceFindings and stage-name-checks's
|
|
25
|
+
* declaredNameDeltas each walked this same grammar before the helper. stage-prose-checks's
|
|
26
|
+
* detachedDocFindings and addedDiffLines are deliberately not folded here: both need each hunk's
|
|
27
|
+
* start-line count from the `@@` header, which this shape drops. */
|
|
28
|
+
export function diffRemovedAdded(diff) {
|
|
29
|
+
const files = [];
|
|
30
|
+
let cur;
|
|
31
|
+
for (const line of diff.split("\n")) {
|
|
32
|
+
const header = /^diff --git a\/.* b\/(.*)$/.exec(line);
|
|
33
|
+
if (header) {
|
|
34
|
+
cur = { path: header[1], removed: [], added: [] };
|
|
35
|
+
files.push(cur);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!cur)
|
|
39
|
+
continue;
|
|
40
|
+
if (line.startsWith("-") && !line.startsWith("---"))
|
|
41
|
+
cur.removed.push(line.slice(1));
|
|
42
|
+
else if (line.startsWith("+") && !line.startsWith("+++"))
|
|
43
|
+
cur.added.push(line.slice(1));
|
|
44
|
+
}
|
|
45
|
+
return files;
|
|
46
|
+
}
|
|
19
47
|
/** Parse `git grep -n` stage output into `file:line:text` hits. `git grep -n` prints one
|
|
20
48
|
* `path:line:text` per match, and the four name/path checks (stage-name-checks.ts's
|
|
21
49
|
* staleNameFindings and movedNameFindings, stage-path-checks.ts's movedSourceFindings and
|