tumwater 0.4.2 → 0.4.3
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} +25 -12
- 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 +1 -8
- 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 +9 -0
- 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 +2 -2
- package/dist/src/agent/opencode/opencode-catalog.js +50 -27
- 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 +1 -1
- package/dist/src/agent/subscription-account.js +3 -3
- 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 +58 -102
- package/dist/src/backlog/backlog-md.js +5 -5
- package/dist/src/backlog/backlog-parts.js +65 -27
- package/dist/src/backlog/backlog-release.js +12 -12
- package/dist/src/backlog/backlog-structure.js +114 -61
- package/dist/src/backlog/backlog.js +1 -1
- package/dist/src/baseline/main-baseline.js +148 -84
- package/dist/src/budget/budget-fallback-view.js +100 -243
- package/dist/src/budget/budget-wiring.js +29 -65
- 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-scoped.js +40 -16
- package/dist/src/build/build-check.js +13 -5
- package/dist/src/cli/cli-command-args.js +89 -52
- package/dist/src/cli/cli-flag-specs.js +42 -22
- package/dist/src/cli/cli-init.js +34 -12
- package/dist/src/cli/cli-query-commands.js +1 -1
- package/dist/src/cli/cli-repo-gate.js +2 -2
- package/dist/src/cli/cli-start.js +91 -58
- package/dist/src/cli/completion.js +82 -46
- package/dist/src/cli/config-commands.js +82 -71
- package/dist/src/cli/help.js +4 -6
- package/dist/src/cli/log-commands.js +43 -23
- package/dist/src/cli/notify-command.js +2 -2
- package/dist/src/cli/question-commands.js +86 -60
- package/dist/src/cli.js +181 -135
- package/dist/src/collections.js +31 -16
- package/dist/src/concurrency/check-slots.js +70 -0
- package/dist/src/concurrency/lock.js +22 -0
- 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 +5 -20
- 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 +153 -102
- package/dist/src/config/config-schema.js +9 -11
- 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 +4 -52
- package/dist/src/config/config-validation.js +4 -38
- package/dist/src/config/config-views.js +9 -34
- package/dist/src/config/config-write.js +6 -23
- package/dist/src/config/config.js +4 -5
- package/dist/src/doctor/doctor-backlog-bugs.js +7 -6
- package/dist/src/doctor/doctor-backlog-plans.js +24 -24
- package/dist/src/doctor/doctor-backlog.js +7 -5
- package/dist/src/doctor/doctor-check-names.js +3 -6
- package/dist/src/doctor/doctor-checks.js +5 -5
- package/dist/src/doctor/doctor-claude-checks.js +44 -28
- package/dist/src/doctor/doctor-codex-checks.js +6 -6
- package/dist/src/doctor/doctor-contention-checks.js +44 -112
- package/dist/src/doctor/doctor-fallback-checks.js +2 -2
- package/dist/src/doctor/doctor-holds.js +6 -45
- 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 +120 -20
- 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 +3 -3
- 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 +30 -54
- package/dist/src/events/event-labels.js +1 -5
- package/dist/src/events/event-legacy.js +1 -3
- package/dist/src/events/{notify.js → event-notify.js} +2 -2
- package/dist/src/events/event-read.js +1 -1
- package/dist/src/failure/failure-cluster.js +6 -6
- package/dist/src/failure/failure-data.js +8 -4
- package/dist/src/failure/failure-render.js +44 -34
- package/dist/src/failure/failure-state-change.js +4 -34
- 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 +27 -68
- 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} +7 -7
- package/dist/src/gates/{streak-gate.js → streak-gates.js} +75 -52
- 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-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-history-endpoints.js +100 -52
- package/dist/src/gui/gui-operator-commands.js +22 -23
- package/dist/src/gui/gui-prompt-commands.js +23 -23
- package/dist/src/gui/gui-report-endpoints.js +1 -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 +51 -33
- 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-core.js +1 -1
- package/dist/src/merge/merge-drain.js +8 -11
- package/dist/src/merge/merge-pipeline.js +6 -7
- package/dist/src/merge/merge-post-review-drain.js +10 -13
- package/dist/src/merge/merge-requeue.js +1 -1
- package/dist/src/merge/merge-slot.js +0 -8
- package/dist/src/merge/merge-verify.js +8 -18
- package/dist/src/notes-cli.js +52 -42
- package/dist/src/operator/operator-commands.js +76 -44
- 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 +27 -32
- package/dist/src/orchestrator/orchestrator.js +8 -21
- package/dist/src/orchestrator/scheduling-admit.js +101 -68
- package/dist/src/orchestrator/scheduling-scope.js +1 -1
- package/dist/src/orchestrator/scheduling-skip.js +1 -1
- package/dist/src/orchestrator/startup-migrations.js +1 -1
- package/dist/src/paths.js +8 -7
- 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 +1 -1
- 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 +32 -6
- package/dist/src/pi/pi-watchdogs.js +12 -5
- 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 +49 -5
- package/dist/src/process/process.js +8 -8
- package/dist/src/process/run-marker.js +31 -13
- 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/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/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/post-merge-review-render.js +9 -3
- package/dist/src/review/post-merge-review.js +1 -1
- package/dist/src/review/review-precheck.js +11 -1
- package/dist/src/review/review.js +40 -153
- package/dist/src/review/secret-leaks.js +26 -16
- 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 +1 -14
- package/dist/src/roles/role-view.js +9 -21
- package/dist/src/run/acceptance-greps.js +88 -45
- package/dist/src/run/run-apply.js +12 -6
- 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 +135 -84
- 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 +119 -50
- package/dist/src/run/stage-check.js +49 -32
- 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/runner-outcome.js +6 -1
- package/dist/src/runner/runner-pi.js +3 -10
- package/dist/src/runner/runner-recovery.js +1 -1
- package/dist/src/runner/{revision.js → runner-revision.js} +1 -1
- package/dist/src/runner/runner-run.js +97 -35
- package/dist/src/runner/runner-state-load.js +12 -9
- package/dist/src/runner/runner.js +41 -25
- package/dist/src/scheduling/claims.js +0 -4
- package/dist/src/scheduling/scheduling.js +53 -66
- package/dist/src/status/status-data.js +2 -2
- package/dist/src/status/status-role-notes.js +2 -2
- package/dist/src/status/status-roles.js +9 -17
- 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 +98 -65
- package/dist/src/team/{orchestrator-info.js → team-orchestrator-info.js} +2 -15
- package/dist/src/team/team-polls.js +28 -126
- package/dist/src/team/{reclaim.js → team-reclaim.js} +61 -31
- package/dist/src/team/team-state.js +4 -13
- package/dist/src/text/markdown.js +1 -1
- package/dist/src/text/phrases.js +20 -15
- package/dist/src/text/text.js +3 -3
- package/dist/src/ui/alert-feeds.js +49 -73
- package/dist/src/ui/badges.js +3 -13
- package/dist/src/ui/gui/gui-client-boot.js +2 -2
- package/dist/src/ui/gui/gui-client-composer.js +1 -1
- package/dist/src/ui/gui/gui-client-drawer-config.js +7 -17
- package/dist/src/ui/gui/gui-client-drawer.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-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 +4 -4
- package/dist/src/ui/gui/gui-client.js +7 -1
- package/dist/src/ui/progress-data.js +53 -40
- package/dist/src/ui/run-progress-model.js +9 -2
- package/dist/src/ui/status-model.js +2 -24
- package/dist/src/ui/status-payload.js +3 -7
- package/dist/src/ui/status-render.js +5 -9
- package/dist/src/ui/team-alert-backlog.js +6 -6
- 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 +7 -8
- package/dist/src/ui/transcript-run.js +27 -16
- 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 +1 -7
- 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 +159 -92
- 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 +3 -3
- 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 -228
- package/dist/src/ui/tui/tui-pane-history.js +61 -70
- package/dist/src/ui/tui/tui-pane-pending.js +284 -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 +11 -7
- 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 +29 -32
- package/dist/src/ui/tui/tui.js +3 -4
- package/dist/src/verdict/fix-claim.js +76 -54
- package/dist/src/verdict/refusal.js +1 -1
- package/package.json +5 -5
- package/dist/src/concurrency/provider-lease.js +0 -105
- 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/backlog/{question-answer.js → backlog-question-answer.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
- /package/dist/src/runner/{leftover.js → runner-leftover.js} +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { compactTokens, usd } from "../text/format.js";
|
|
2
|
+
import { durationLabel } from "../cli/cli-args.js";
|
|
3
|
+
import { boundedWindowLine, sinceWindowLine } from "../text/datetime.js";
|
|
4
|
+
import { SPARSE_WINDOW_NOTE } from "../events/event-window.js";
|
|
5
|
+
import { foldBaseRoles } from "./report-fold.js";
|
|
6
|
+
import { rankedRoleMap, rankedRoleLine, promptTokensLine, pushRankedRoleLine, pushPresentRankedLine, pushProviderCostLines, pushReportSummary, costPerCommitPairsFrom, costPerCommitLine, } from "./report-render.js";
|
|
7
|
+
/** Render a trailing-window totals report as Markdown — the aggregate sibling of the
|
|
8
|
+
* `logs --since` raw stream ("how much" here, "what exactly" there). Pure function of
|
|
9
|
+
* SinceReport: no I/O, no clock reads (the window line's local time is rendered from the
|
|
10
|
+
* collector's captured cutoff, so the printed window matches the aggregated one). */
|
|
11
|
+
export function renderSinceReportMarkdown(data) {
|
|
12
|
+
const lines = [];
|
|
13
|
+
pushSinceWindowHeader(lines, data);
|
|
14
|
+
pushSinceSummary(lines, data);
|
|
15
|
+
pushSinceRoleLines(lines, data);
|
|
16
|
+
pushSinceProviderModelLines(lines, data);
|
|
17
|
+
pushSinceMainRedLines(lines, data);
|
|
18
|
+
pushSinceFrictionLines(lines, data);
|
|
19
|
+
pushSinceDeferralLines(lines, data);
|
|
20
|
+
pushSinceCommitLines(lines, data);
|
|
21
|
+
pushSinceCostLines(lines, data);
|
|
22
|
+
pushSinceCoverageLines(lines, data);
|
|
23
|
+
return lines.join("\n");
|
|
24
|
+
}
|
|
25
|
+
/** The window header: the title, the bounded-or-since window line and the role scope line. */
|
|
26
|
+
function pushSinceWindowHeader(lines, data) {
|
|
27
|
+
lines.push("# tumwater usage report");
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push(data.untilMs !== undefined && data.toIso !== undefined
|
|
30
|
+
? boundedWindowLine(durationLabel(data.sinceMs), durationLabel(data.untilMs), data.fromIso, data.toIso)
|
|
31
|
+
: sinceWindowLine(durationLabel(data.sinceMs), data.fromIso));
|
|
32
|
+
if (data.role !== undefined)
|
|
33
|
+
lines.push(`role: ${data.role}`);
|
|
34
|
+
lines.push("");
|
|
35
|
+
}
|
|
36
|
+
/** The shared totals voice (minus features/bugs/filing cells, stated at the tail), gated
|
|
37
|
+
* to the team report like the day sibling's. */
|
|
38
|
+
function pushSinceSummary(lines, data) {
|
|
39
|
+
const t = data.totals;
|
|
40
|
+
// Same totals voice as the day report, minus the features/bugs and filing cells: those tallies
|
|
41
|
+
// come from day-granular backlog-file dates that cannot subdivide a sub-day window, so the
|
|
42
|
+
// final line below states the omission instead of showing a day-rounded number.
|
|
43
|
+
pushReportSummary(lines, t, "", data.role !== undefined);
|
|
44
|
+
lines.push("");
|
|
45
|
+
}
|
|
46
|
+
/** Runs / tokens / prompt-tokens / cost breakdowns by role. */
|
|
47
|
+
function pushSinceRoleLines(lines, data) {
|
|
48
|
+
lines.push(rankedRoleLine("Runs by role", rankedRoleMap(data.runsByRole), String));
|
|
49
|
+
// Same breakdown for token spend: ranked by tokens desc then name asc, zero-token roles
|
|
50
|
+
// omitted — an all-zero window renders "-" like the other lines. Output tokens are the
|
|
51
|
+
// resource the budget charges, so this is the line an operator reads to find which role's
|
|
52
|
+
// prompt loop burned the window's tokens.
|
|
53
|
+
pushRankedRoleLine(lines, "Tokens by role", rankedRoleMap(data.tokensByRole), compactTokens);
|
|
54
|
+
// The prompt side of the same breakdown, straight after the output-token line: how many
|
|
55
|
+
// prompt tokens each role sent and the share of them its backend served from cache — the
|
|
56
|
+
// input side of the bill an operator cannot see from Tokens by role. Zero-prompt roles are
|
|
57
|
+
// omitted, and an all-zero window renders "-" like the sibling lines.
|
|
58
|
+
lines.push(promptTokensLine(rankedRoleMap(data.promptTokensByRole ?? {}), rankedRoleMap(data.cacheReadTokensByRole ?? {})));
|
|
59
|
+
// Same breakdown for spend: ranked by spend desc then name asc, zero-spend roles omitted —
|
|
60
|
+
// an all-zero window renders "-" like the runs line.
|
|
61
|
+
pushRankedRoleLine(lines, "Cost by role", rankedRoleMap(data.costByRole), usd);
|
|
62
|
+
}
|
|
63
|
+
/** The provider cost lines and the per-model line, gated to the team report. */
|
|
64
|
+
function pushSinceProviderModelLines(lines, data) {
|
|
65
|
+
// The same breakdown per PROVIDER (plans: per-provider spend), gated to the team report
|
|
66
|
+
// like its day sibling. SinceReport.costByProvider is always present (empty when none), so
|
|
67
|
+
// the zero-means-absent gate prints no line for a window with no provider spend.
|
|
68
|
+
if (data.role === undefined) {
|
|
69
|
+
pushProviderCostLines(lines, () => rankedRoleMap(data.costByProvider), () => rankedRoleMap(data.notionalByProvider));
|
|
70
|
+
// The same breakdown per MODEL (each run_start's agent-qualified selector — the model a
|
|
71
|
+
// team mixing providers, per-role overrides and fallbacks actually ran on), gated to the
|
|
72
|
+
// team report like the provider lines: a model is not a role, so a role-scoped window
|
|
73
|
+
// omits it. SinceReport.runsByModel is always present, so a window with no modelled
|
|
74
|
+
// start prints no line (keeping a model-less day's output unchanged), exactly as the
|
|
75
|
+
// provider lines stay absent when no provider spent.
|
|
76
|
+
pushPresentRankedLine(lines, "Runs by model", rankedRoleMap(data.runsByModel));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Main's red time and the test/merge culprits behind it, gated like the day sibling. */
|
|
80
|
+
function pushSinceMainRedLines(lines, data) {
|
|
81
|
+
// Main's red wall-clock over the window (main's own, not any role's — a role-scoped
|
|
82
|
+
// collector carries 0, so this never prints for a scoped window). Zero-absent: no line for
|
|
83
|
+
// a window main was never red.
|
|
84
|
+
if (data.mainRedMs > 0)
|
|
85
|
+
lines.push(`**Main red:** ${durationLabel(data.mainRedMs)}`);
|
|
86
|
+
// The same "what kept main red" detail named by failing test — which tests main's own red
|
|
87
|
+
// checks named over the window. Team-only like main's red time, so a role-scoped window
|
|
88
|
+
// omits it; zero-absent: no line when the window's red checks named no test.
|
|
89
|
+
if (data.role === undefined) {
|
|
90
|
+
pushPresentRankedLine(lines, "Main red by test", rankedRoleMap(data.mainRedByTest ?? {}));
|
|
91
|
+
// The same "what kept main red" detail named by merge — the landed merges the watch's
|
|
92
|
+
// own main_culprit events blamed, mirroring the failing-test line right above. Team-only
|
|
93
|
+
// like main's red time and zero-absent: no line when the window blamed no merge.
|
|
94
|
+
pushPresentRankedLine(lines, "Main red by merge", rankedRoleMap(data.mainRedByCulprit ?? {}));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** The review-friction and failed-run breakdowns by role. */
|
|
98
|
+
function pushSinceFrictionLines(lines, data) {
|
|
99
|
+
// The same breakdown for review friction: rejections per role from review_rejected events;
|
|
100
|
+
// a window with none renders "-" like the other lines. The post-merge line is the
|
|
101
|
+
// afterMerge: true subset — rejections that reached an already-merged commit.
|
|
102
|
+
pushRankedRoleLine(lines, "Rejections by role", rankedRoleMap(data.rejectionsByRole));
|
|
103
|
+
pushRankedRoleLine(lines, "Post-merge rejections by role", rankedRoleMap(data.postMergeRejectionsByRole));
|
|
104
|
+
pushRankedRoleLine(lines, "Revisions by role", rankedRoleMap(data.revisionsByRole));
|
|
105
|
+
pushRankedRoleLine(lines, "Refusals by role", rankedRoleMap(data.refusalsByRole));
|
|
106
|
+
// The same breakdown for outright failures: error-class run_ends per role under the
|
|
107
|
+
// digest's OUTCOME_CLASS — the runs that burned agent time and merged nothing without
|
|
108
|
+
// going through review, which rejections/revisions would miss entirely. A window with
|
|
109
|
+
// none renders "-" like the other lines.
|
|
110
|
+
pushRankedRoleLine(lines, "Failed runs by role", rankedRoleMap(data.failedRunsByRole));
|
|
111
|
+
pushRankedRoleLine(lines, "Image reroutes by role", rankedRoleMap(data.imageReroutesByRole));
|
|
112
|
+
}
|
|
113
|
+
/** Deferred maintenance runs by reason code, gated to the team report. */
|
|
114
|
+
function pushSinceDeferralLines(lines, data) {
|
|
115
|
+
// Deferred maintenance runs per reason code, the --since twin of the day render's line:
|
|
116
|
+
// how many due maintenance runs stood down and why (backlog / no-work / plans-full).
|
|
117
|
+
// Gated to the team report — a reason is not a role, so a role-scoped window omits it.
|
|
118
|
+
// Zero-means-absent: a window with no deferral prints no line.
|
|
119
|
+
if (data.role === undefined) {
|
|
120
|
+
pushPresentRankedLine(lines, "Deferred runs by reason", rankedRoleMap(data.deferredByReason ?? {}));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** Commits by role and the merged/merge-failure split, gated to the team report. */
|
|
124
|
+
function pushSinceCommitLines(lines, data) {
|
|
125
|
+
// The merged-commits split by base role, matching the day render's unfiltered line: the
|
|
126
|
+
// window's totals.commits carries the count; this line says which role's commits it was.
|
|
127
|
+
lines.push(rankedRoleLine("Commits by role", rankedRoleMap(data.commitsByRole), String));
|
|
128
|
+
// The merged-outcomes split by base role, mirroring the day render's line: which roles'
|
|
129
|
+
// changes reach main and which bounce off the gate (merge_failed). Gated to the team
|
|
130
|
+
// report — a role-scoped window omits merge (ROLE_SCOPE_NOTE), so the per-role
|
|
131
|
+
// attribution would contradict it. Zero-means-absent: the failures line prints only when
|
|
132
|
+
// some role has one; a merge-free window renders `-` like the other lines.
|
|
133
|
+
if (data.role === undefined) {
|
|
134
|
+
lines.push(rankedRoleLine("Merged by role", rankedRoleMap(data.mergedByRole), String));
|
|
135
|
+
pushPresentRankedLine(lines, "Merge failures by role", rankedRoleMap(data.mergeFailedByRole));
|
|
136
|
+
// WHY the window's merges failed, from each merge_failed's result code — the day
|
|
137
|
+
// report's reason twin of the role line above, so an operator can tell a review
|
|
138
|
+
// rejection (fix the change) from a conflict/block (rebase) from a shutdown abort
|
|
139
|
+
// (capacity). Ranked the same way (count desc then code asc), reason-free only.
|
|
140
|
+
pushPresentRankedLine(lines, "Merge failures by reason", rankedRoleMap(data.mergeFailedByReason ?? {}));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** Cost/estimated per commit and the notional Estimated by role lines. */
|
|
144
|
+
function pushSinceCostLines(lines, data) {
|
|
145
|
+
// Spend per merged commit, the --since twin of the day render's `Cost per commit:` line:
|
|
146
|
+
// costByRole is keyed by role id (instances not folded, so `feature-2` joins `feature`),
|
|
147
|
+
// commitsByRole is already base-role keyed. Prints for a spend-free window as `-` and for a
|
|
148
|
+
// role-scoped window from its scoped maps, matching the day render.
|
|
149
|
+
lines.push(costPerCommitLine("Cost per commit", costPerCommitPairsFrom(foldBaseRoles(data.costByRole), data.commitsByRole)));
|
|
150
|
+
// Subscription spend is notional: its own line, only when the window has any — the same
|
|
151
|
+
// conditional/filtered `usd` form the day render's "Estimated by role" uses.
|
|
152
|
+
if (data.notionalByRole !== undefined) {
|
|
153
|
+
pushRankedRoleLine(lines, "Estimated by role", rankedRoleMap(data.notionalByRole), usd);
|
|
154
|
+
// The day render's `Estimated per commit:` twin, after `Estimated by role`, from the same
|
|
155
|
+
// notionalByRole source folded to base roles and the base-keyed commitsByRole.
|
|
156
|
+
lines.push(costPerCommitLine("Estimated per commit", costPerCommitPairsFrom(foldBaseRoles(data.notionalByRole), data.commitsByRole)));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** The trailing blank and the exactly-one coverage/backlog-omission note. */
|
|
160
|
+
function pushSinceCoverageLines(lines, data) {
|
|
161
|
+
lines.push("");
|
|
162
|
+
// Exactly one final line, chosen by the collector's coverage proof: either a hedged note
|
|
163
|
+
// that stays true whenever it prints (the oldest retained event lies inside the window —
|
|
164
|
+
// whether from rotation or a log born inside the window), or the one-time statement of the
|
|
165
|
+
// backlog-tally omission. A flat "rotated out" claim would be false for a log that never
|
|
166
|
+
// had the older events, so the wording names only what is provably the case. A role-scoped
|
|
167
|
+
// report already states the omission in ROLE_SCOPE_NOTE, so it does not repeat the --days
|
|
168
|
+
// pointer, which would imply a scoped day report shows the tallies it too omits.
|
|
169
|
+
if (!data.coversFullWindow)
|
|
170
|
+
lines.push(SPARSE_WINDOW_NOTE);
|
|
171
|
+
else if (data.role === undefined)
|
|
172
|
+
lines.push("backlog tallies (features done / bugs fixed / plans added / bugs filed) need the day report (--days)");
|
|
173
|
+
}
|
|
@@ -10,7 +10,7 @@ import { addTo } from "../collections.js";
|
|
|
10
10
|
import { isPassingFullCheck, isRedFullCheck } from "../build/build-check-events.js";
|
|
11
11
|
import { eventRole, eventUsage } from "../events/event-read.js";
|
|
12
12
|
import { finiteNumber, nonNegativeNumber } from "../files/json-object.js";
|
|
13
|
-
import { OUTCOME_CLASS } from "../failure/time-spend.js";
|
|
13
|
+
import { OUTCOME_CLASS } from "../failure/failure-time-spend.js";
|
|
14
14
|
import { commitTier } from "../roles/roles.js";
|
|
15
15
|
import { baseRoleOf } from "../roles/runner-ids.js";
|
|
16
16
|
/** Fold one event's `providerCost` map (`Record<provider, {costUsd, notionalCostUsd}>` — the
|
|
@@ -139,10 +139,10 @@ function foldRedCheck(target, _ev) {
|
|
|
139
139
|
// The merge slot's red full-suite checks, for the day report's red-check count.
|
|
140
140
|
target.checkFailures++;
|
|
141
141
|
}
|
|
142
|
-
/** Fold
|
|
143
|
-
function
|
|
144
|
-
// One
|
|
145
|
-
addTo(target.
|
|
142
|
+
/** Fold an image_reroute: one reroute per event, preflight or after a blind read. */
|
|
143
|
+
function foldImageReroute(target, ev) {
|
|
144
|
+
// One reroute per event, preflight or after a blind read.
|
|
145
|
+
addTo(target.imageReroutesByRole, eventRole(ev), 1);
|
|
146
146
|
}
|
|
147
147
|
/** Fold a run_deferred: one deferral per event, bucketed by the raw reason code (team-only). */
|
|
148
148
|
function foldDeferred(target, ev) {
|
|
@@ -245,7 +245,7 @@ function foldMergeOutcome(target, ev) {
|
|
|
245
245
|
}
|
|
246
246
|
/** Fold one event into a usage accumulator by dispatching to a per-family builder —
|
|
247
247
|
* foldRunEnd, foldReviewRejected, foldReviewVerdict, foldRevision, foldPassingCheck,
|
|
248
|
-
* foldRedCheck,
|
|
248
|
+
* foldRedCheck, foldImageReroute, foldDeferred, foldMerged,
|
|
249
249
|
* foldRunStarted, foldProviderWait, foldSlotWait, foldCheckWait or foldMergeOutcome — each
|
|
250
250
|
* named for the family it folds, so how a family folds is found by name. The dispatch holds
|
|
251
251
|
* no accumulation of its own. All behavior lives in the builders: run_end events add a run
|
|
@@ -269,8 +269,8 @@ export function foldUsageEvent(target, ev) {
|
|
|
269
269
|
foldPassingCheck(target, ev);
|
|
270
270
|
else if (isRedFullCheck(ev))
|
|
271
271
|
foldRedCheck(target, ev);
|
|
272
|
-
else if (ev.type === "
|
|
273
|
-
|
|
272
|
+
else if (ev.type === "image_reroute")
|
|
273
|
+
foldImageReroute(target, ev);
|
|
274
274
|
else if (ev.type === "run_deferred")
|
|
275
275
|
foldDeferred(target, ev);
|
|
276
276
|
else if (ev.type === "merged")
|
|
@@ -6,7 +6,8 @@ import { collectReport } from "./report-data.js";
|
|
|
6
6
|
import { collectReportSince } from "./report-since-data.js";
|
|
7
7
|
import { collectFailureReport, collectFailureReportSince } from "../failure/failure-data.js";
|
|
8
8
|
import { renderFailureMarkdown } from "../failure/failure-render.js";
|
|
9
|
-
import { renderReportMarkdown
|
|
9
|
+
import { renderReportMarkdown } from "./report-render.js";
|
|
10
|
+
import { renderSinceReportMarkdown } from "./report-since-render.js";
|
|
10
11
|
import { fail, say, sayJson, sayJsonOrRender } from "../cli/cli-output.js";
|
|
11
12
|
import { followView } from "../cli/follow-view.js";
|
|
12
13
|
import { failRivalShapes, failUntilShorterThanSince, flagValue, parseCountFlag, parseRoleListScope, parseSinceFlag } from "../cli/cli-args.js";
|
|
@@ -26,31 +27,47 @@ export async function cmdReport(root, args, seams = {}) {
|
|
|
26
27
|
// digest, whose day and --since collectors take the same optional list of base roles.
|
|
27
28
|
const role = parseRoleListScope(root, args);
|
|
28
29
|
const follow = hasFollowFlag(args);
|
|
30
|
+
failOnFollowJson(follow, args);
|
|
31
|
+
if (ms !== null) {
|
|
32
|
+
sinceReportShape(root, args, ms, role, untilMs, follow);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await dayReportShape(root, args, reportDays(args), role, follow, seams);
|
|
36
|
+
}
|
|
37
|
+
/** `-f/--follow` with `--json` is refused before either shape dispatches: the live view
|
|
38
|
+
* repaints a document, `--json` prints one — drop one of them. */
|
|
39
|
+
function failOnFollowJson(follow, args) {
|
|
29
40
|
if (follow && args.includes("--json"))
|
|
30
41
|
fail("report --json prints one document; -f/--follow repaints a live view — drop one of them");
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const since = collectReportSince(root, ms, role ?? undefined, untilMs ?? undefined);
|
|
51
|
-
sayJsonOrRender(args, since, renderSinceReportMarkdown);
|
|
42
|
+
}
|
|
43
|
+
/** The `--since`/`--until` trailing-window shape: its rival-shape guards against
|
|
44
|
+
* `-f/--follow` and `--days`, then the `--failures` digest or the plain totals, both through
|
|
45
|
+
* the `--json` / render choice. Fatal on any rival flag; returns only after printing. */
|
|
46
|
+
function sinceReportShape(root, args, ms, role, untilMs, follow) {
|
|
47
|
+
// --days is still a rival shape: it counts whole local days, while --since totals a trailing
|
|
48
|
+
// window. --failures is not a rival anymore — the digest has a windowed-since collector that
|
|
49
|
+
// reuses the day digest's own sections, so `report --failures --since <d>` dispatches it
|
|
50
|
+
// through the existing --json / render choice; --until bounds its window's end.
|
|
51
|
+
if (follow)
|
|
52
|
+
failRivalShapes("report --since", "-f/--follow", 'follow means "from now on"; --since shows a bounded past window');
|
|
53
|
+
if (args.includes("--days"))
|
|
54
|
+
failRivalShapes("report --since", "--days", "--days counts whole local days; --since totals a trailing window");
|
|
55
|
+
if (args.includes("--failures")) {
|
|
56
|
+
const digest = collectFailureReportSince(root, ms, role ?? undefined, untilMs ?? undefined);
|
|
57
|
+
if (args.includes("--json"))
|
|
58
|
+
sayJson(digest);
|
|
59
|
+
else
|
|
60
|
+
say(renderFailureMarkdown(digest));
|
|
52
61
|
return;
|
|
53
62
|
}
|
|
63
|
+
// --json swaps the renderer for the collector's own payload, exactly as status --json
|
|
64
|
+
// does: bounds and cap checks above are shared, only the printing differs.
|
|
65
|
+
const since = collectReportSince(root, ms, role ?? undefined, untilMs ?? undefined);
|
|
66
|
+
sayJsonOrRender(args, since, renderSinceReportMarkdown);
|
|
67
|
+
}
|
|
68
|
+
/** The `--days` parse: the explicit flag's value checked against REPORT_MAX_DAYS, or the
|
|
69
|
+
* default series length when absent. Fatal on a value past the shared bound. */
|
|
70
|
+
function reportDays(args) {
|
|
54
71
|
const daysRaw = flagValue(args, "--days");
|
|
55
72
|
let days = REPORT_DEFAULT_DAYS;
|
|
56
73
|
if (daysRaw !== null) {
|
|
@@ -61,6 +78,13 @@ export async function cmdReport(root, args, seams = {}) {
|
|
|
61
78
|
// rejected 0, non-decimals, and a missing value.
|
|
62
79
|
days = parseCountFlag("--days", daysRaw, REPORT_MAX_DAYS);
|
|
63
80
|
}
|
|
81
|
+
return days;
|
|
82
|
+
}
|
|
83
|
+
/** The whole-day report shape: the `-f/--follow` live repaint, the `--failures --json`
|
|
84
|
+
* digest, the plain `--json` payload, or the day Markdown/failure-digest render. Fatal on a
|
|
85
|
+
* `follow && --json` clash (already refused by cmdReport's failOnFollowJson); returns only
|
|
86
|
+
* after printing. */
|
|
87
|
+
async function dayReportShape(root, args, days, role, follow, seams) {
|
|
64
88
|
// --failures --json prints the collector's own payload (the FailureReportData object), the
|
|
65
89
|
// report --json and doctor --json precedent: the time-and-spend fold gave the digest a
|
|
66
90
|
// stable data shape, so the machine-readable form is the data itself, not a re-parse of the
|
|
@@ -33,7 +33,9 @@ export function postReviewState(entry) {
|
|
|
33
33
|
export const POST_REVIEW_STATES = ["waiting", "failing", "rejected", "fixed"];
|
|
34
34
|
/** The bracketed status suffix one post-merge review entry carries on every observer surface
|
|
35
35
|
* (`tumwater reviews`, the dashboard's Post-merge reviews card, and the TUI Pending pane's
|
|
36
|
-
* review block): the reviewer's recorded rejected reasons when `rejected` is set
|
|
36
|
+
* review block): the reviewer's recorded rejected reasons when `rejected` is set — or just
|
|
37
|
+
* `[rejected]` when the recorded list is empty, so a rejection with no reasons never renders
|
|
38
|
+
* a dangling `rejected — `; else the
|
|
37
39
|
* number of verdict-less review runs when `failures` is positive, naming the
|
|
38
40
|
* last failure's reason beside the count when `lastFailure` is a non-empty string; else `""`.
|
|
39
41
|
* Reasons are squashed to 60 as `tumwater reviews` has always printed them, and the failure
|
|
@@ -44,8 +46,12 @@ export function postReviewStatusLabel(entry) {
|
|
|
44
46
|
switch (postReviewState(entry)) {
|
|
45
47
|
case "fixed":
|
|
46
48
|
return `[fixed — the repair ${shortSha(entry.fixed)} already merged on main]`;
|
|
47
|
-
case "rejected":
|
|
48
|
-
|
|
49
|
+
case "rejected": {
|
|
50
|
+
const reasons = entry.rejected; // postReviewState only says "rejected" when rejected is an array
|
|
51
|
+
return reasons.length === 0
|
|
52
|
+
? "[rejected]"
|
|
53
|
+
: `[rejected — ${reasons.map((r) => squash(r, 60)).join("; ")}]`;
|
|
54
|
+
}
|
|
49
55
|
case "failing": {
|
|
50
56
|
const failures = entry.failures; // postReviewState only says "failing" when failures > 0
|
|
51
57
|
if (typeof entry.lastFailure === "string" && entry.lastFailure.length > 0) {
|
|
@@ -13,7 +13,7 @@ import { buildReviewPrompt } from "../prompt/gate-prompts.js";
|
|
|
13
13
|
import { shortSha } from "../text/format.js";
|
|
14
14
|
import { errorMessage } from "../text/text.js";
|
|
15
15
|
import { revisionRoundOf } from "../git/commit-message.js";
|
|
16
|
-
import { REVISION_BACKSTOP } from "../runner/revision.js";
|
|
16
|
+
import { REVISION_BACKSTOP } from "../runner/runner-revision.js";
|
|
17
17
|
import { eventRole } from "../events/event-read.js";
|
|
18
18
|
import { baseRoleOf } from "../roles/runner-ids.js";
|
|
19
19
|
import { fixLine } from "../verdict/reply-contract.js";
|
|
@@ -56,8 +56,18 @@ export async function gateBuildPrecheck(ctx, state, head, reject) {
|
|
|
56
56
|
// The gate's one check invocation — the pre-check and its one flake re-run below are the
|
|
57
57
|
// same run in the same scope, worktree, and config with the same resolved timeout, so the
|
|
58
58
|
// scope and timeout resolution live here once and cannot drift between the two calls.
|
|
59
|
-
const runGateCheck = () => runScopedBuildCheck(root, role, "gate", wt, config, ctx.buildCheckTimeoutMs ?? BUILD_CHECK_TIMEOUT_MS, ctx.sampleSleep ?? sampleSleepClock, undefined, checkWaitStage(root, [role]));
|
|
59
|
+
const runGateCheck = () => runScopedBuildCheck(root, role, "gate", wt, config, ctx.buildCheckTimeoutMs ?? BUILD_CHECK_TIMEOUT_MS, ctx.sampleSleep ?? sampleSleepClock, undefined, checkWaitStage(root, [role]), undefined, ctx.signal);
|
|
60
60
|
const preCheck = await runGateCheck();
|
|
61
|
+
// A check the harness stopped on the verify's abort (a user abort or a shutdown) is not the
|
|
62
|
+
// tree's verdict and not a flake: fail closed as aborted before the re-run, the quarantine
|
|
63
|
+
// and the attribution — the signal fires MID-check (runScriptGroup returns an `aborted`
|
|
64
|
+
// skip), and `reviewPinnedChange` settles the verify `aborted` on this result so an abort
|
|
65
|
+
// never has to wait a parked check out. A signal already aborted before the check started is
|
|
66
|
+
// caught by the `aborted` checks in the attribution path below (the check ran and reached
|
|
67
|
+
// its own verdict), so the pre-existing shutdown case keeps its two attempts.
|
|
68
|
+
if (preCheck?.outcome.skipReason === "aborted") {
|
|
69
|
+
return { resolved: { decision: "failed", aborted: true } };
|
|
70
|
+
}
|
|
61
71
|
if (preCheck) {
|
|
62
72
|
const { check } = preCheck;
|
|
63
73
|
let { outcome } = preCheck;
|