tumwater 0.4.0 → 0.4.2
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 +4 -6
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-backend.js +5 -2
- package/dist/src/agent/backend-traits.js +21 -0
- package/dist/src/agent/claude/claude-backend.js +1 -1
- package/dist/src/agent/claude/claude-stream.js +181 -182
- package/dist/src/agent/claude/{write-guard-hook.js → claude-write-guard-hook.js} +1 -1
- package/dist/src/agent/claude/claude-write-guard.js +5 -4
- package/dist/src/agent/codex/codex-backend.js +1 -1
- package/dist/src/agent/codex/codex-rollout.js +48 -45
- package/dist/src/agent/codex/codex-stream.js +269 -215
- package/dist/src/agent/codex/{write-guard-hook.js → codex-write-guard-hook.js} +2 -1
- package/dist/src/agent/codex/codex-write-guard.js +3 -3
- package/dist/src/agent/image-input.js +18 -87
- 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 +2 -2
- package/dist/src/agent/opencode/opencode-config.js +2 -2
- package/dist/src/agent/opencode/opencode-stream.js +101 -92
- package/dist/src/agent/run-agent.js +76 -382
- package/dist/src/agent/run-settle.js +25 -15
- package/dist/src/agent/run-spawn.js +226 -0
- package/dist/src/agent/run-stream.js +185 -0
- package/dist/src/agent/session-ledger.js +16 -0
- package/dist/src/agent/write-guard.js +5 -3
- package/dist/src/backlog/backlog-archive.js +86 -170
- package/dist/src/backlog/backlog-cli-view.js +89 -70
- package/dist/src/backlog/backlog-eligibility.js +51 -59
- package/dist/src/backlog/backlog-file-bugs.js +133 -0
- package/dist/src/backlog/backlog-index-data.js +159 -0
- package/dist/src/backlog/backlog-index.js +7 -5
- package/dist/src/backlog/backlog-md.js +39 -28
- package/dist/src/backlog/backlog-release.js +21 -36
- package/dist/src/backlog/backlog-render.js +1 -1
- package/dist/src/backlog/backlog-structure.js +14 -145
- package/dist/src/backlog/backlog-write.js +31 -18
- package/dist/src/backlog/backlog.js +14 -4
- package/dist/src/backlog/question-answer.js +33 -15
- package/dist/src/baseline/main-baseline.js +71 -26
- package/dist/src/baseline/main-guardrails.js +76 -15
- package/dist/src/baseline/main-red.js +45 -94
- package/dist/src/baseline/main-watch.js +9 -3
- package/dist/src/brief/brief.js +40 -0
- package/dist/src/budget/budget-fallback-view.js +50 -8
- package/dist/src/budget/budget-wiring.js +15 -11
- package/dist/src/budget/budget.js +3 -2
- package/dist/src/build/build-check-events.js +4 -2
- package/dist/src/build/build-check-report.js +2 -1
- package/dist/src/build/build-check-scoped.js +70 -41
- package/dist/src/change/change-data.js +4 -2
- package/dist/src/cli/cli-args.js +9 -0
- package/dist/src/cli/cli-command-args.js +199 -98
- package/dist/src/cli/cli-flag-specs.js +6 -4
- package/dist/src/cli/cli-marker-commands.js +2 -2
- package/dist/src/cli/cli-query-commands.js +41 -6
- package/dist/src/cli/cli-review-commands.js +2 -1
- package/dist/src/cli/cli-role-command.js +46 -92
- package/dist/src/cli/cli-start.js +123 -47
- package/dist/src/cli/follow-view.js +3 -1
- package/dist/src/cli/help.js +19 -12
- package/dist/src/cli/log-commands.js +128 -107
- package/dist/src/cli/question-commands.js +82 -63
- package/dist/src/cli.js +396 -343
- package/dist/src/collections.js +46 -9
- package/dist/src/config/config-fallbacks.js +0 -4
- package/dist/src/config/config-live.js +179 -147
- package/dist/src/config/config-request.js +176 -51
- package/dist/src/config/config-validation-roles.js +271 -0
- package/dist/src/config/config-validation.js +10 -205
- package/dist/src/config/config-views.js +2 -7
- package/dist/src/config/config-write.js +205 -175
- package/dist/src/config/config.js +2 -1
- package/dist/src/doctor/doctor-backlog-bugs.js +24 -122
- package/dist/src/doctor/doctor-backlog-plans.js +24 -25
- package/dist/src/doctor/doctor-backlog.js +34 -42
- package/dist/src/doctor/doctor-check-names.js +4 -2
- package/dist/src/doctor/doctor-checks.js +16 -4
- package/dist/src/doctor/doctor-claude-checks.js +2 -2
- package/dist/src/doctor/doctor-codex-checks.js +9 -20
- package/dist/src/doctor/doctor-contention-checks.js +52 -19
- package/dist/src/doctor/doctor-holds.js +22 -20
- package/dist/src/doctor/doctor-model-checks.js +8 -184
- package/dist/src/doctor/doctor-notify.js +2 -5
- package/dist/src/doctor/doctor-opencode-checks.js +90 -41
- package/dist/src/doctor/doctor-principles.js +26 -0
- package/dist/src/doctor/doctor-project-checks.js +69 -19
- package/dist/src/doctor/doctor-role-notes.js +33 -0
- package/dist/src/doctor/doctor.js +13 -6
- package/dist/src/doctor/probe.js +1 -1
- package/dist/src/events/event-format.js +307 -162
- package/dist/src/events/event-labels.js +0 -1
- package/dist/src/events/event-legacy.js +1 -1
- package/dist/src/events/event-window.js +1 -1
- package/dist/src/events/events.js +15 -2
- package/dist/src/events/notify.js +1 -1
- package/dist/src/failure/failure-cluster.js +4 -3
- package/dist/src/failure/failure-data.js +126 -94
- package/dist/src/failure/failure-render.js +58 -17
- package/dist/src/failure/failure-state-change.js +308 -27
- package/dist/src/failure/time-spend.js +139 -106
- package/dist/src/files/files.js +7 -0
- package/dist/src/files/json-object.js +15 -2
- package/dist/src/gates/budget-gates.js +90 -68
- package/dist/src/gates/gate-polls.js +92 -61
- package/dist/src/gates/role-cap-gates.js +1 -1
- package/dist/src/gates/startup-gate.js +28 -28
- package/dist/src/gates/streak-gate.js +1 -1
- package/dist/src/git/git.js +1 -1
- package/dist/src/git/slot-salvage.js +9 -9
- package/dist/src/git/slots-state.js +12 -3
- package/dist/src/git/worktree-pool-startup.js +127 -0
- package/dist/src/git/worktree-pool.js +1 -120
- package/dist/src/gui/gui-admin-endpoints.js +3 -4
- package/dist/src/gui/gui-args.js +1 -1
- package/dist/src/gui/gui-backlog-commands.js +20 -11
- package/dist/src/gui/gui-backlog-endpoints.js +2 -8
- package/dist/src/gui/gui-config-commands.js +51 -76
- package/dist/src/gui/gui-endpoint-commands.js +4 -2
- package/dist/src/gui/gui-endpoint-common.js +4 -3
- package/dist/src/gui/gui-history-endpoints.js +36 -27
- package/dist/src/gui/gui-operator-commands.js +43 -17
- package/dist/src/gui/gui-prompt-commands.js +27 -21
- package/dist/src/gui/gui-quarantine-commands.js +30 -0
- package/dist/src/gui/gui-report-endpoints.js +13 -9
- package/dist/src/gui/gui-server.js +5 -3
- package/dist/src/gui/gui-team-commands.js +1 -1
- package/dist/src/history/history-data.js +7 -1
- package/dist/src/history/history-render.js +15 -10
- package/dist/src/history/history.js +103 -68
- package/dist/src/inbox/inbox-submit.js +1 -5
- package/dist/src/inbox/pending-prompt.js +3 -2
- package/dist/src/inbox/prompt-commands.js +194 -161
- package/dist/src/init/init.js +104 -25
- package/dist/src/merge/merge-core.js +1 -1
- package/dist/src/merge/merge-drain.js +1 -1
- package/dist/src/merge/merge-git.js +5 -5
- package/dist/src/merge/{merge-post-review.js → merge-post-review-drain.js} +1 -1
- package/dist/src/merge/merge-queue.js +2 -1
- package/dist/src/merge/merge-to-main.js +1 -1
- package/dist/src/merge/merge-verify.js +3 -2
- package/dist/src/operator/operator-commands.js +84 -52
- package/dist/src/operator/operator-intent.js +26 -3
- package/dist/src/operator/retire.js +2 -1
- package/dist/src/operator/revert.js +18 -12
- package/dist/src/orchestrator/orchestrator-poll.js +2 -2
- package/dist/src/orchestrator/orchestrator.js +1 -1
- package/dist/src/orchestrator/retention.js +6 -5
- package/dist/src/paths.js +10 -10
- package/dist/src/pi/pi-bin.js +2 -2
- package/dist/src/pi/pi-models.js +9 -0
- package/dist/src/pi/pi-stream.js +63 -41
- package/dist/src/pi/pi-watchdogs.js +84 -49
- package/dist/src/pi-extension/context-shake.js +55 -31
- package/dist/src/pi-extension/full-output.js +2 -3
- package/dist/src/process/process.js +24 -1
- package/dist/src/process/run-marker.js +2 -2
- package/dist/src/process/supervisor.js +24 -5
- package/dist/src/prompt/gate-notes.js +41 -16
- package/dist/src/prompt/notes-backlog.js +22 -29
- package/dist/src/prompt/principles.js +11 -0
- package/dist/src/prompt/prompt-followup.js +4 -12
- package/dist/src/prompt/prompt.js +15 -12
- package/dist/src/redeploy/redeploy-probes.js +11 -1
- package/dist/src/redeploy/redeployer.js +54 -7
- package/dist/src/redeploy/self-reload.js +7 -2
- package/dist/src/report/report-data.js +224 -320
- package/dist/src/report/report-fold.js +23 -231
- package/dist/src/report/report-main-red.js +70 -3
- package/dist/src/report/report-render.js +181 -48
- package/dist/src/report/report-since-data.js +218 -0
- package/dist/src/report/report-usage-fold.js +288 -0
- package/dist/src/report/report.js +2 -1
- package/dist/src/review/known-flakes.js +22 -3
- package/dist/src/review/post-merge-review-accept.js +3 -2
- package/dist/src/review/post-merge-review.js +16 -14
- package/dist/src/review/quarantine-render.js +29 -11
- package/dist/src/review/review-precheck.js +2 -1
- package/dist/src/roles/role-catalog.js +30 -29
- package/dist/src/roles/role-guidance.js +26 -43
- package/dist/src/roles/role-render.js +19 -0
- package/dist/src/roles/role-view.js +19 -5
- package/dist/src/roles/roles.js +2 -22
- package/dist/src/run/ci-parity.js +5 -6
- package/dist/src/run/feedback-filings.js +10 -1
- package/dist/src/run/qa-coverage.js +4 -4
- package/dist/src/run/run-apply.js +14 -2
- package/dist/src/run/run-detail-data.js +50 -10
- package/dist/src/run/run-detail-render.js +32 -1
- package/dist/src/run/run-detail.js +44 -22
- package/dist/src/run/run-dismiss.js +43 -0
- package/dist/src/run/run-finalize.js +75 -46
- package/dist/src/run/run-prompt.js +271 -187
- package/dist/src/run/run-resume.js +32 -31
- package/dist/src/run/run-stage.js +116 -64
- package/dist/src/run/run-timing.js +18 -4
- package/dist/src/run/run-verdict.js +119 -83
- package/dist/src/run/stage-backlog-checks.js +4 -31
- package/dist/src/run/stage-check.js +4 -12
- package/dist/src/run/stage-checks-shared.js +15 -0
- package/dist/src/run/stage-name-checks.js +3 -13
- package/dist/src/run/stage-path-checks.js +26 -29
- package/dist/src/runner/leftover.js +13 -4
- package/dist/src/runner/revision.js +2 -2
- package/dist/src/runner/runner-fallback.js +1 -1
- package/dist/src/runner/runner-outcome.js +0 -5
- package/dist/src/runner/runner-pi.js +72 -49
- package/dist/src/runner/runner-run.js +19 -102
- package/dist/src/runner/runner-state-load.js +0 -10
- package/dist/src/runner/runner.js +1 -32
- package/dist/src/scheduling/claims.js +4 -11
- package/dist/src/status/status-data.js +140 -91
- package/dist/src/status/status-polls.js +41 -5
- package/dist/src/status/status-role-notes.js +53 -0
- package/dist/src/status/status-roles.js +10 -37
- package/dist/src/text/datetime.js +8 -1
- package/dist/src/text/format.js +13 -1
- package/dist/src/text/phrases.js +16 -2
- package/dist/src/text/text.js +12 -0
- package/dist/src/ui/alert-feeds.js +115 -0
- package/dist/src/ui/badges.js +3 -4
- package/dist/src/ui/gui/gui-client-boot.js +78 -57
- package/dist/src/ui/gui/gui-client-drawer-config.js +8 -3
- package/dist/src/ui/gui/gui-client-drawer.js +18 -2
- package/dist/src/ui/gui/gui-client-feed.js +3 -8
- package/dist/src/ui/gui/gui-client-history.js +16 -16
- package/dist/src/ui/gui/gui-client-model.js +9 -9
- package/dist/src/ui/gui/gui-client-operator.js +6 -10
- package/dist/src/ui/gui/gui-client-pending.js +4 -4
- package/dist/src/ui/gui/gui-client-report-chart.js +111 -10
- package/dist/src/ui/gui/gui-client-report.js +70 -11
- package/dist/src/ui/gui/gui-client-roles.js +52 -12
- package/dist/src/ui/gui/gui-client-settings.js +31 -26
- package/dist/src/ui/gui/gui-client-team.js +28 -2
- package/dist/src/ui/gui/gui-client.js +33 -4
- package/dist/src/ui/gui/gui-icons.js +7 -1
- package/dist/src/ui/progress-data.js +2 -2
- package/dist/src/ui/run-progress-model.js +2 -2
- package/dist/src/ui/status-model.js +26 -16
- package/dist/src/ui/status-payload.js +149 -326
- package/dist/src/ui/status-render.js +131 -80
- package/dist/src/ui/team-alert-backlog.js +236 -0
- package/dist/src/ui/team-alert-shared.js +92 -0
- package/dist/src/ui/team-alert-status.js +326 -0
- package/dist/src/ui/team-alert-tail.js +238 -0
- package/dist/src/ui/team-alerts.js +16 -984
- package/dist/src/ui/transcript-tail.js +137 -94
- package/dist/src/ui/tui/tui-backlog.js +1 -1
- package/dist/src/ui/tui/tui-editor-answer.js +54 -44
- package/dist/src/ui/tui/tui-editor-attach.js +74 -63
- package/dist/src/ui/tui/tui-editor-backends.js +62 -56
- package/dist/src/ui/tui/tui-editor-brief.js +64 -54
- package/dist/src/ui/tui/tui-editor-budget.js +106 -89
- package/dist/src/ui/tui/tui-editor-config.js +2 -4
- package/dist/src/ui/tui/tui-editor-ctx.js +12 -1
- package/dist/src/ui/tui/tui-editor-entry.js +171 -153
- package/dist/src/ui/tui/tui-editor-instances.js +2 -1
- package/dist/src/ui/tui/tui-editor-instructions.js +50 -107
- package/dist/src/ui/tui/tui-editor-interval.js +39 -112
- package/dist/src/ui/tui/tui-editor-notes.js +28 -84
- package/dist/src/ui/tui/tui-editor-pause.js +210 -183
- package/dist/src/ui/tui/tui-editor-prompt.js +46 -35
- package/dist/src/ui/tui/tui-editor-queued.js +74 -64
- package/dist/src/ui/tui/tui-editor-quiet.js +122 -108
- package/dist/src/ui/tui/tui-editor-release.js +85 -0
- package/dist/src/ui/tui/tui-editor-retire.js +57 -46
- package/dist/src/ui/tui/tui-editor-role-enabled.js +90 -80
- package/dist/src/ui/tui/tui-editor-role-file.js +87 -0
- package/dist/src/ui/tui/tui-editor-role-key.js +50 -42
- package/dist/src/ui/tui/tui-editor-search.js +70 -58
- package/dist/src/ui/tui/tui-editor-session.js +3 -0
- package/dist/src/ui/tui/tui-editors.js +4 -1
- package/dist/src/ui/tui/tui-frame.js +7 -3
- package/dist/src/ui/tui/tui-history.js +12 -10
- package/dist/src/ui/tui/tui-keys.js +6 -1
- package/dist/src/ui/tui/tui-pane-activity.js +62 -50
- package/dist/src/ui/tui/tui-pane-backlog.js +4 -4
- package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
- package/dist/src/ui/tui/tui-pane-health.js +45 -26
- package/dist/src/ui/tui/tui-pane-history.js +296 -264
- package/dist/src/ui/tui/tui-pane-report.js +74 -57
- package/dist/src/ui/tui/tui-pane-session.js +6 -2
- package/dist/src/ui/tui/tui-pane-transcript.js +158 -131
- package/dist/src/ui/tui/tui-pane.js +201 -174
- package/dist/src/ui/tui/tui-panes.js +211 -148
- package/dist/src/ui/tui/tui.js +4 -4
- package/dist/src/verdict/fix-claim.js +18 -6
- package/dist/src/verdict/refusal.js +32 -7
- package/package.json +1 -1
- package/dist/src/doctor/image-probe.js +0 -257
- /package/dist/src/gui/{http-body.js → gui-http-body.js} +0 -0
- /package/dist/src/merge/{backlog-conflicts.js → merge-backlog-conflicts.js} +0 -0
|
@@ -7,16 +7,24 @@
|
|
|
7
7
|
* and a roleCell-sliced role, so no unbounded field reaches the page. */
|
|
8
8
|
import { truncateExample } from "./failure-cluster.js";
|
|
9
9
|
import { cutSplitsSurrogatePair, isNonBlankString } from "../text/text.js";
|
|
10
|
-
import { finiteNumber, stringList } from "../files/json-object.js";
|
|
11
|
-
import { orchestratorStopPhrase, backendKindPhrase, budgetPhrase, teamPausePhrase, teamResumePhrase, holdPhrase, plural, rolePausePhrase, roleResumePhrase, rolesPhrase, shortSpanPhrase, PAUSE_EXPIRED_SUFFIX } from "../text/phrases.js";
|
|
10
|
+
import { finiteNumber, isJsonObject, stringList } from "../files/json-object.js";
|
|
11
|
+
import { orchestratorStopPhrase, backendKindPhrase, budgetPhrase, teamPausePhrase, teamResumePhrase, holdPhrase, plural, rolePausePhrase, roleResumePhrase, rolesPhrase, shortSpanPhrase, rendersAsBackendFailure, PAUSE_EXPIRED_SUFFIX } from "../text/phrases.js";
|
|
12
12
|
import { shortSha } from "../text/format.js";
|
|
13
|
-
import { deferredReasonText, untilClockSuffix } from "../events/event-format.js";
|
|
13
|
+
import { deferredReasonText, untilClockSuffix, gigabytes, windowSuffix, windowResetPhrase, backendLabel } from "../events/event-format.js";
|
|
14
|
+
import { claudeUtilizationPct } from "../agent/claude/claude-account.js";
|
|
14
15
|
/** The transition events the digest replays: the decisions the harness made about itself (the
|
|
15
16
|
* cap/team gates and the team hold, the error-streak breaker's pause, the role's own daily
|
|
16
17
|
* cap, model fallbacks, live-config edits, self-hosted redeploys, need-based deferrals,
|
|
17
|
-
* orchestrator lifecycle
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* orchestrator lifecycle, and the gate holds/lifts that stop and restart work — low disk,
|
|
19
|
+
* quiet hours, the maintenance allowance and the subscription-window reserve). Everything the
|
|
20
|
+
* set omits is deliberately excluded and named in STATE_CHANGE_EXCLUDED_TYPES; the two sets
|
|
21
|
+
* partition EVENT_TYPE_LABELS' keys, so a new event type must land in exactly one of them and
|
|
22
|
+
* cannot ship unclassified. The wiring twins `fallback_switched` and `budget_handback` are the
|
|
23
|
+
* run-side consequence of `budget_resumed` — the budget/window reopening that aborts in-flight
|
|
24
|
+
* fallback runs so their next run continues on the primary. The rest are the evidence the
|
|
25
|
+
* selfcheck role's load-bearing rule asks it to
|
|
26
|
+
* judge — whether the harness's RESPONSE to a failure was wrong (plans/telemetry-role.md) —
|
|
27
|
+
* so they sit beside the outcomes rather than being dropped. */
|
|
20
28
|
export const STATE_CHANGE_TYPES = new Set([
|
|
21
29
|
"budget_paused",
|
|
22
30
|
"budget_fallback",
|
|
@@ -44,6 +52,81 @@ export const STATE_CHANGE_TYPES = new Set([
|
|
|
44
52
|
"orchestrator_start",
|
|
45
53
|
"orchestrator_stop",
|
|
46
54
|
"supervisor_exit",
|
|
55
|
+
"disk_low",
|
|
56
|
+
"disk_ok",
|
|
57
|
+
"quiet_hours_started",
|
|
58
|
+
"quiet_hours_ended",
|
|
59
|
+
"maintenance_quota_hold",
|
|
60
|
+
"maintenance_quota_resumed",
|
|
61
|
+
"subscription_window_reserved",
|
|
62
|
+
"subscription_window_limited",
|
|
63
|
+
"subscription_window_reset",
|
|
64
|
+
"provider_concurrency_changed",
|
|
65
|
+
"model_changed",
|
|
66
|
+
"bootstrap_complete",
|
|
67
|
+
"fallback_switched",
|
|
68
|
+
"budget_handback",
|
|
69
|
+
]);
|
|
70
|
+
/** The event types the digest deliberately does not replay, one per EVENT_TYPE_LABELS key that
|
|
71
|
+
* is not a STATE_CHANGE_TYPES member. Together with STATE_CHANGE_TYPES it partitions
|
|
72
|
+
* EVENT_TYPE_LABELS' keys, so a new event type must be classified in one set or the other — the
|
|
73
|
+
* disjoint-and-covering commitment the digest's Team state changes section rests on. Grouped by
|
|
74
|
+
* reason, one line per group (the run-outcome half, the per-run advisories and the observations
|
|
75
|
+
* were named in the older comment; the state-adjacent group is newly listed). */
|
|
76
|
+
export const STATE_CHANGE_EXCLUDED_TYPES = new Set([
|
|
77
|
+
// Run, merge and main-watch outcomes and per-run bookkeeping.
|
|
78
|
+
"run_start",
|
|
79
|
+
"run_end",
|
|
80
|
+
"merge_queued",
|
|
81
|
+
"merge_cancelled",
|
|
82
|
+
"merge_topped",
|
|
83
|
+
"merge_done",
|
|
84
|
+
"merge_failed",
|
|
85
|
+
"merged",
|
|
86
|
+
"counters_reset",
|
|
87
|
+
"run_aborted",
|
|
88
|
+
"resume",
|
|
89
|
+
"wake",
|
|
90
|
+
"operator_revert",
|
|
91
|
+
"build_check",
|
|
92
|
+
"dep_install",
|
|
93
|
+
"budget_warning",
|
|
94
|
+
"role_cap_warning",
|
|
95
|
+
"main_check",
|
|
96
|
+
"main_culprit",
|
|
97
|
+
"main_red_hold",
|
|
98
|
+
"warning",
|
|
99
|
+
// Reviews and revisions.
|
|
100
|
+
"review_start",
|
|
101
|
+
"review_verdict",
|
|
102
|
+
"review_rejected",
|
|
103
|
+
"review_failed",
|
|
104
|
+
"revision",
|
|
105
|
+
"conflict_handback",
|
|
106
|
+
// Inbox and backlog.
|
|
107
|
+
"question_posted",
|
|
108
|
+
"issue_filed",
|
|
109
|
+
"entry_refused",
|
|
110
|
+
"prompt_enqueued",
|
|
111
|
+
"prompt_cancelled",
|
|
112
|
+
"prompt_edited",
|
|
113
|
+
"prompt_topped",
|
|
114
|
+
"prompt_taken",
|
|
115
|
+
// Per-run advisories and assignments.
|
|
116
|
+
"claim",
|
|
117
|
+
"permit_reserve",
|
|
118
|
+
"slot_wait",
|
|
119
|
+
"provider_wait",
|
|
120
|
+
"check_wait",
|
|
121
|
+
// Observations and actions.
|
|
122
|
+
"disk_reclaim",
|
|
123
|
+
"subscription_window_warning",
|
|
124
|
+
"image_blind",
|
|
125
|
+
// State-adjacent but not self-decisions.
|
|
126
|
+
"state_migrated",
|
|
127
|
+
"restart_forced",
|
|
128
|
+
"backend_binary",
|
|
129
|
+
"opencode_free_model_billed",
|
|
47
130
|
]);
|
|
48
131
|
/** The Team state changes section's caps: newest N transitions, each line's payload capped at
|
|
49
132
|
* STATE_CHANGE_MAX (a marked word-boundary cut per truncateExample), each free field within it
|
|
@@ -53,12 +136,33 @@ export const STATE_CHANGE_TYPES = new Set([
|
|
|
53
136
|
export const STATE_CHANGE_TOP = 6;
|
|
54
137
|
const STATE_CHANGE_MAX = 72;
|
|
55
138
|
const STATE_CHANGE_FIELD_MAX = 24;
|
|
139
|
+
/** Join a list of `items` into one comma/`;`-separated line that fits STATE_CHANGE_MAX: pop
|
|
140
|
+
* tail items until `prefix + items.join(separator)` — with a `… +N more <noun>` marker naming
|
|
141
|
+
* the drop, or a bare `bare` fallback once none remain — stays under the cap, so the cut never
|
|
142
|
+
* lands mid-item and the marker survives the final pass. Shared by the fallback_switched and
|
|
143
|
+
* config_changed lines, which both bounded-drop a hand-edited/list payload that can overshoot
|
|
144
|
+
* the cap (BUGS.md 2026-10-01), so the loop shape and marker wording cannot drift between them. */
|
|
145
|
+
function joinToCap(bare, prefix, separator, noun, items) {
|
|
146
|
+
let text = items.length > 0 ? prefix + items.join(separator) : bare;
|
|
147
|
+
let hidden = 0;
|
|
148
|
+
while (text.length > STATE_CHANGE_MAX) {
|
|
149
|
+
items.pop();
|
|
150
|
+
hidden += 1;
|
|
151
|
+
text =
|
|
152
|
+
items.length > 0
|
|
153
|
+
? `${prefix}${items.join(separator)} … +${hidden} more ${noun}`
|
|
154
|
+
: `${bare} … +${hidden} more ${noun}`;
|
|
155
|
+
}
|
|
156
|
+
return text;
|
|
157
|
+
}
|
|
56
158
|
/** A free event field, sliced so one hand-edited or future payload cannot blow the section's
|
|
57
159
|
* byte budget. Takes unknown because HarnessEvent carries its fields loosely typed. A cut that
|
|
58
160
|
* would land between an astral character's two UTF-16 units backs off one unit — dropping the
|
|
59
161
|
* whole character rather than leaving a lone high surrogate the terminal renders as a
|
|
60
162
|
* replacement box (text.ts's truncate rule, shared rather than re-derived). */
|
|
61
163
|
function field(v) {
|
|
164
|
+
if (v === undefined || v === null)
|
|
165
|
+
return ""; // A torn event must never render "undefined".
|
|
62
166
|
const s = String(v);
|
|
63
167
|
let cut = STATE_CHANGE_FIELD_MAX;
|
|
64
168
|
if (cutSplitsSurrogatePair(s, cut))
|
|
@@ -71,13 +175,18 @@ function field(v) {
|
|
|
71
175
|
function providerModel(ev) {
|
|
72
176
|
return `${field(ev.provider ?? "pi default")}/${field(ev.model ?? "pi default")}`;
|
|
73
177
|
}
|
|
74
|
-
/** A
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
178
|
+
/** A utilization fraction as its whole-number percent, read through finiteNumber so a torn or
|
|
179
|
+
* hand-edited subscription event renders 0% rather than NaN — the same corrupt-field rule the
|
|
180
|
+
* feed's own subscription lines apply. */
|
|
181
|
+
function pct(v) {
|
|
182
|
+
return claudeUtilizationPct(finiteNumber(v, 0));
|
|
183
|
+
}
|
|
184
|
+
/** A per-family builder of one state-change line, one per event family, so the budget, pause,
|
|
185
|
+
* model-wiring, provider-hold, live-config, update, wait and lifecycle wordings are each found
|
|
186
|
+
* by name. Each is a total function over any HarnessEvent — its `default` returns the bare type
|
|
187
|
+
* name, unreachable through the dispatcher below — and each returns the untruncated line; the
|
|
188
|
+
* caller truncates exactly once, so the byte bound and its marker apply as before. */
|
|
189
|
+
function budgetStateChange(ev) {
|
|
81
190
|
let text;
|
|
82
191
|
switch (ev.type) {
|
|
83
192
|
case "budget_paused": {
|
|
@@ -99,6 +208,14 @@ export function describeStateChange(ev) {
|
|
|
99
208
|
case "budget_resumed":
|
|
100
209
|
text = `budget resumed (${budgetPhrase(ev.spentUsd, ev.capUsd)} today)`;
|
|
101
210
|
break;
|
|
211
|
+
default:
|
|
212
|
+
return ev.type;
|
|
213
|
+
}
|
|
214
|
+
return text;
|
|
215
|
+
}
|
|
216
|
+
function pauseStateChange(ev) {
|
|
217
|
+
let text;
|
|
218
|
+
switch (ev.type) {
|
|
102
219
|
case "team_paused":
|
|
103
220
|
text = teamPausePhrase() + untilClockSuffix(ev.until);
|
|
104
221
|
if (isNonBlankString(ev.reason))
|
|
@@ -125,6 +242,14 @@ export function describeStateChange(ev) {
|
|
|
125
242
|
case "role_cap_resumed":
|
|
126
243
|
text = `role ${field(ev.role ?? "?")} resumed — under its daily cap again`;
|
|
127
244
|
break;
|
|
245
|
+
default:
|
|
246
|
+
return ev.type;
|
|
247
|
+
}
|
|
248
|
+
return text;
|
|
249
|
+
}
|
|
250
|
+
function modelStateChange(ev) {
|
|
251
|
+
let text;
|
|
252
|
+
switch (ev.type) {
|
|
128
253
|
case "model_fallback_started":
|
|
129
254
|
// The role tripped off its primary onto its fallback after provider-class failures.
|
|
130
255
|
text = `model fallback — ${providerModel(ev)} engaged (${field(ev.reason ?? "backend failure")})`;
|
|
@@ -134,13 +259,54 @@ export function describeStateChange(ev) {
|
|
|
134
259
|
// off-model episode ran.
|
|
135
260
|
text = `model fallback ended — back on ${providerModel(ev)} after ${shortSpanPhrase(finiteNumber(ev.durationMs, 0))}`;
|
|
136
261
|
break;
|
|
262
|
+
case "provider_concurrency_changed":
|
|
263
|
+
text = `${field(ev.provider)} concurrency ${field(ev.from)} → ${field(ev.to)}`;
|
|
264
|
+
break;
|
|
265
|
+
case "model_changed":
|
|
266
|
+
// A model_cleared (or a roles.<id>.model-only) live edit arrives with `to: null`;
|
|
267
|
+
// name pi's default the way the feed does so the digest never renders a bare
|
|
268
|
+
// `model changed — now `.
|
|
269
|
+
text = `model changed — now ${field(ev.to ?? "pi's default")}`;
|
|
270
|
+
break;
|
|
271
|
+
case "fallback_switched": {
|
|
272
|
+
// A tier's fallback pair or step changed (or returned to the primary) this poll. Null
|
|
273
|
+
// `to`/`from` read `primary`, matching the feed's fallback_switched line. Whole parts
|
|
274
|
+
// only, dropped from the tail with a marker when the tier list overshoots the cap — the
|
|
275
|
+
// same bounded-drop rule config_changed uses, because a hand-edited `tiers` object can
|
|
276
|
+
// carry more entries than MODEL_TIERS; an empty or torn object reads `fallback switched`.
|
|
277
|
+
const tiers = isJsonObject(ev.tiers) ? ev.tiers : {};
|
|
278
|
+
const parts = Object.entries(tiers).map(([tier, v]) => {
|
|
279
|
+
const t = isJsonObject(v) ? v : {};
|
|
280
|
+
return `${field(tier)}: ${field(t.from ?? "primary")} → ${field(t.to ?? "primary")} (${field(t.reason ?? "available")})`;
|
|
281
|
+
});
|
|
282
|
+
text = joinToCap("fallback switched", "fallback switched — ", "; ", "tiers", parts);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case "budget_handback":
|
|
286
|
+
// The budget/window reopened and aborted in-flight fallback runs; their next run
|
|
287
|
+
// continues on the primary. Mirrors the feed's budget_handback wording in the digest's
|
|
288
|
+
// compact form; the window's backend is not named since subscription_window_reset
|
|
289
|
+
// already carries it.
|
|
290
|
+
text =
|
|
291
|
+
ev.reason === "window"
|
|
292
|
+
? `window reset — handed ${rolesPhrase(ev.roles, "?", 4, field)} back to the primary`
|
|
293
|
+
: `budget reopened — handed ${rolesPhrase(ev.roles, "?", 4, field)} back to the primary`;
|
|
294
|
+
break;
|
|
295
|
+
default:
|
|
296
|
+
return ev.type;
|
|
297
|
+
}
|
|
298
|
+
return text;
|
|
299
|
+
}
|
|
300
|
+
function providerHoldStateChange(ev) {
|
|
301
|
+
let text;
|
|
302
|
+
switch (ev.type) {
|
|
137
303
|
case "rate_limit_hold": {
|
|
138
304
|
// Same split as the event feed's rendering (event-format.ts): a rate-limit hold keeps
|
|
139
305
|
// the 429 wording; a backend-failure kind names itself. The role list caps at four and
|
|
140
306
|
// each role is byte-sliced through field() so the section's bound holds.
|
|
141
307
|
const roles = rolesPhrase(ev.roles, "?", 4, field);
|
|
142
308
|
text =
|
|
143
|
-
ev.kind
|
|
309
|
+
rendersAsBackendFailure(ev.kind)
|
|
144
310
|
? `backend hold (${backendKindPhrase(ev.kind)}) ${holdPhrase(ev.holdMs, ev.escalation)} — ${roles}`
|
|
145
311
|
: `429 hold ${holdPhrase(ev.holdMs, ev.escalation)} — ${roles}`;
|
|
146
312
|
break;
|
|
@@ -149,10 +315,18 @@ export function describeStateChange(ev) {
|
|
|
149
315
|
// Same split as the hold line above (and the event feed's rendering): the resumed event
|
|
150
316
|
// carries the ended hold's kind, so a backend hold's lift names itself.
|
|
151
317
|
text =
|
|
152
|
-
ev.kind
|
|
318
|
+
rendersAsBackendFailure(ev.kind)
|
|
153
319
|
? `backend hold lifted (${backendKindPhrase(ev.kind)}) — roles run again`
|
|
154
320
|
: "429 hold lifted — roles run again";
|
|
155
321
|
break;
|
|
322
|
+
default:
|
|
323
|
+
return ev.type;
|
|
324
|
+
}
|
|
325
|
+
return text;
|
|
326
|
+
}
|
|
327
|
+
function liveConfigStateChange(ev) {
|
|
328
|
+
let text;
|
|
329
|
+
switch (ev.type) {
|
|
156
330
|
case "max_concurrent_changed":
|
|
157
331
|
text = `maxConcurrent ${field(ev.from)} → ${field(ev.to)}`;
|
|
158
332
|
break;
|
|
@@ -166,18 +340,17 @@ export function describeStateChange(ev) {
|
|
|
166
340
|
// marker naming the drop — fits the section cap, so the final STATE_CHANGE_MAX pass
|
|
167
341
|
// below can never cut the marker or a key mid-word.
|
|
168
342
|
const keys = stringList(ev.keys).map(field);
|
|
169
|
-
text =
|
|
170
|
-
let hidden = 0;
|
|
171
|
-
while (text.length > STATE_CHANGE_MAX) {
|
|
172
|
-
keys.pop();
|
|
173
|
-
hidden += 1;
|
|
174
|
-
text =
|
|
175
|
-
keys.length > 0
|
|
176
|
-
? `config changed: ${keys.join(", ")} … +${hidden} more keys`
|
|
177
|
-
: `config changed … +${hidden} more keys`;
|
|
178
|
-
}
|
|
343
|
+
text = joinToCap("config changed", "config changed: ", ", ", "keys", keys);
|
|
179
344
|
break;
|
|
180
345
|
}
|
|
346
|
+
default:
|
|
347
|
+
return ev.type;
|
|
348
|
+
}
|
|
349
|
+
return text;
|
|
350
|
+
}
|
|
351
|
+
function updateStateChange(ev) {
|
|
352
|
+
let text;
|
|
353
|
+
switch (ev.type) {
|
|
181
354
|
case "build_stale":
|
|
182
355
|
// The digest's 72-char line cap (§ STATE_CHANGE_MAX) cannot hold the full alert phrase
|
|
183
356
|
// "restarts after cooldown; work continues" beside the ahead-count prefix, so it says
|
|
@@ -197,9 +370,54 @@ export function describeStateChange(ev) {
|
|
|
197
370
|
case "restart_blocked":
|
|
198
371
|
text = `restart blocked — ${field(ev.reason)}; staying on build ${shortSha(ev.from)}`;
|
|
199
372
|
break;
|
|
373
|
+
default:
|
|
374
|
+
return ev.type;
|
|
375
|
+
}
|
|
376
|
+
return text;
|
|
377
|
+
}
|
|
378
|
+
function waitStateChange(ev) {
|
|
379
|
+
let text;
|
|
380
|
+
switch (ev.type) {
|
|
200
381
|
case "run_deferred":
|
|
201
382
|
text = `deferred — ${deferredReasonText(ev)}`;
|
|
202
383
|
break;
|
|
384
|
+
case "disk_low":
|
|
385
|
+
// The disk floor stopped the team from starting runs; the free space and floor read
|
|
386
|
+
// through the feed's own gigabytes so the digest and the event feed cannot drift.
|
|
387
|
+
text = `waiting: low disk — ${gigabytes(ev.freeGB)} free (floor ${gigabytes(ev.holdGB)})`;
|
|
388
|
+
break;
|
|
389
|
+
case "disk_ok":
|
|
390
|
+
text = `disk recovered — ${gigabytes(ev.freeGB)} free`;
|
|
391
|
+
break;
|
|
392
|
+
case "quiet_hours_started":
|
|
393
|
+
text = `waiting: quiet hours${windowSuffix(ev)}`;
|
|
394
|
+
break;
|
|
395
|
+
case "quiet_hours_ended":
|
|
396
|
+
text = `quiet hours ended${windowSuffix(ev)}`;
|
|
397
|
+
break;
|
|
398
|
+
case "maintenance_quota_hold":
|
|
399
|
+
text = `waiting: maintenance allowance used — ${field(ev.maint)}/${field(ev.allowance)} in 24h (${field(ev.work)} work)`;
|
|
400
|
+
break;
|
|
401
|
+
case "maintenance_quota_resumed":
|
|
402
|
+
text = `maintenance allowance reopened — ${field(ev.maint)}/${field(ev.allowance)} in 24h`;
|
|
403
|
+
break;
|
|
404
|
+
case "subscription_window_reserved":
|
|
405
|
+
text = `waiting: ${backendLabel(ev)} window ${field(ev.windowType)} at ${pct(ev.utilization)}% past its ${pct(ev.reserve)}% reserve`;
|
|
406
|
+
break;
|
|
407
|
+
case "subscription_window_limited":
|
|
408
|
+
text = `waiting: ${backendLabel(ev)} window ${field(ev.windowType)} rejected${windowResetPhrase(ev)}`;
|
|
409
|
+
break;
|
|
410
|
+
case "subscription_window_reset":
|
|
411
|
+
text = `${backendLabel(ev)} window reset — model jobs run again`;
|
|
412
|
+
break;
|
|
413
|
+
default:
|
|
414
|
+
return ev.type;
|
|
415
|
+
}
|
|
416
|
+
return text;
|
|
417
|
+
}
|
|
418
|
+
function lifecycleStateChange(ev) {
|
|
419
|
+
let text;
|
|
420
|
+
switch (ev.type) {
|
|
203
421
|
case "orchestrator_start":
|
|
204
422
|
text = `tumwater started (pid ${field(ev.pid)}${ev.build ? `, build ${shortSha(ev.build)}` : ""})`;
|
|
205
423
|
break;
|
|
@@ -209,8 +427,71 @@ export function describeStateChange(ev) {
|
|
|
209
427
|
case "supervisor_exit":
|
|
210
428
|
text = `tumwater down — restart ${field(ev.generation)} ${ev.signal ? `killed by ${field(ev.signal)}` : `exited ${field(ev.code)}`}${ev.reason ? `: ${field(ev.reason)}` : ""}`;
|
|
211
429
|
break;
|
|
430
|
+
case "bootstrap_complete":
|
|
431
|
+
text = `bootstrap complete — ${field(ev.plansDone)}/${field(ev.untilPlansDone)} plans done`;
|
|
432
|
+
break;
|
|
433
|
+
default:
|
|
434
|
+
return ev.type;
|
|
435
|
+
}
|
|
436
|
+
return text;
|
|
437
|
+
}
|
|
438
|
+
/** A compact, bounded one-liner for one harness decision event, for the Team state changes
|
|
439
|
+
* section. Every free string is sliced (field) and the whole line is capped again at
|
|
440
|
+
* STATE_CHANGE_MAX so the digest's byte bound holds for any event shape — the cap cut falls on
|
|
441
|
+
* a word boundary and carries truncateExample's `… (+N chars)` marker, so a cut line can never
|
|
442
|
+
* read as complete or stand mid-word where a key or value should be; the render adds the
|
|
443
|
+
* timestamp and a roleCell-sliced role, so no unbounded field reaches the page. */
|
|
444
|
+
export function describeStateChange(ev) {
|
|
445
|
+
switch (ev.type) {
|
|
446
|
+
case "budget_paused":
|
|
447
|
+
case "budget_fallback":
|
|
448
|
+
case "budget_resumed":
|
|
449
|
+
return truncateExample(budgetStateChange(ev), STATE_CHANGE_MAX);
|
|
450
|
+
case "team_paused":
|
|
451
|
+
case "team_resumed":
|
|
452
|
+
case "role_paused":
|
|
453
|
+
case "role_resumed":
|
|
454
|
+
case "role_streak_paused":
|
|
455
|
+
case "role_cap_paused":
|
|
456
|
+
case "role_cap_resumed":
|
|
457
|
+
return truncateExample(pauseStateChange(ev), STATE_CHANGE_MAX);
|
|
458
|
+
case "model_fallback_started":
|
|
459
|
+
case "model_fallback_ended":
|
|
460
|
+
case "provider_concurrency_changed":
|
|
461
|
+
case "model_changed":
|
|
462
|
+
case "fallback_switched":
|
|
463
|
+
case "budget_handback":
|
|
464
|
+
return truncateExample(modelStateChange(ev), STATE_CHANGE_MAX);
|
|
465
|
+
case "rate_limit_hold":
|
|
466
|
+
case "rate_limit_resumed":
|
|
467
|
+
return truncateExample(providerHoldStateChange(ev), STATE_CHANGE_MAX);
|
|
468
|
+
case "max_concurrent_changed":
|
|
469
|
+
case "retention_changed":
|
|
470
|
+
case "config_changed":
|
|
471
|
+
return truncateExample(liveConfigStateChange(ev), STATE_CHANGE_MAX);
|
|
472
|
+
case "build_stale":
|
|
473
|
+
case "restart_pending":
|
|
474
|
+
case "restart":
|
|
475
|
+
case "restart_refused":
|
|
476
|
+
case "restart_blocked":
|
|
477
|
+
return truncateExample(updateStateChange(ev), STATE_CHANGE_MAX);
|
|
478
|
+
case "run_deferred":
|
|
479
|
+
case "disk_low":
|
|
480
|
+
case "disk_ok":
|
|
481
|
+
case "quiet_hours_started":
|
|
482
|
+
case "quiet_hours_ended":
|
|
483
|
+
case "maintenance_quota_hold":
|
|
484
|
+
case "maintenance_quota_resumed":
|
|
485
|
+
case "subscription_window_reserved":
|
|
486
|
+
case "subscription_window_limited":
|
|
487
|
+
case "subscription_window_reset":
|
|
488
|
+
return truncateExample(waitStateChange(ev), STATE_CHANGE_MAX);
|
|
489
|
+
case "orchestrator_start":
|
|
490
|
+
case "orchestrator_stop":
|
|
491
|
+
case "supervisor_exit":
|
|
492
|
+
case "bootstrap_complete":
|
|
493
|
+
return truncateExample(lifecycleStateChange(ev), STATE_CHANGE_MAX);
|
|
212
494
|
default:
|
|
213
|
-
|
|
495
|
+
return truncateExample(ev.type, STATE_CHANGE_MAX);
|
|
214
496
|
}
|
|
215
|
-
return truncateExample(text, STATE_CHANGE_MAX);
|
|
216
497
|
}
|