omp-conductor 0.19.2 → 0.19.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 +4 -3
- package/REFERENCE.md +1 -1
- package/package.json +1 -1
- package/schema/config.schema.json +13 -1
- package/src/arm-challenge.ts +92 -16
- package/src/board.ts +50 -9
- package/src/cli.ts +2 -0
- package/src/command-manifest.ts +10 -0
- package/src/commands/arm.ts +30 -2
- package/src/commands/companion.ts +103 -0
- package/src/commands/context.ts +1 -0
- package/src/commands/worker.ts +1 -0
- package/src/companion-view.ts +81 -0
- package/src/config-schema.ts +7 -1
- package/src/config.ts +6 -3
- package/src/daemon.ts +288 -15
- package/src/dashboard/controls.ts +1 -1
- package/src/doctor.ts +126 -7
- package/src/escalate.ts +47 -1
- package/src/failure-class.fixture.json +546 -0
- package/src/failure-class.ts +192 -0
- package/src/fleet.ts +286 -32
- package/src/orchestrator-tick.ts +115 -34
- package/src/settlement.ts +181 -0
- package/src/setup.ts +6 -4
- package/src/spend-telemetry.ts +74 -2
- package/src/status-render.ts +25 -6
- package/src/store.ts +39 -3
- package/src/types.ts +39 -2
- package/src/usage.ts +25 -0
package/src/spend-telemetry.ts
CHANGED
|
@@ -66,7 +66,34 @@ export type SpendTelemetryVerdict =
|
|
|
66
66
|
/** A majority of working runs reported nothing. */
|
|
67
67
|
| { kind: "partial"; worked: number; missing: number }
|
|
68
68
|
/** Every working run reported nothing — the same fact, stated louder. */
|
|
69
|
-
| { kind: "absent"; worked: number }
|
|
69
|
+
| { kind: "absent"; worked: number }
|
|
70
|
+
/**
|
|
71
|
+
* Every working run reported nothing **and this fleet does not bill in
|
|
72
|
+
* dollars** (#984). Not a fault: a subscription request has no per-request
|
|
73
|
+
* price, so the harness reports none and `spendUsd` is permanently 0.00 —
|
|
74
|
+
* accurate rather than missing. The constraint that actually binds is the
|
|
75
|
+
* provider's allowance window, named here.
|
|
76
|
+
*/
|
|
77
|
+
| { kind: "subscription"; worked: number; window: string; evidence: "declared" | "observed" };
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* What is known about how this project's runs are billed (#984).
|
|
81
|
+
*
|
|
82
|
+
* Two independent grounds, because on a real fleet the declaration is often
|
|
83
|
+
* absent while the evidence is unambiguous:
|
|
84
|
+
*
|
|
85
|
+
* - `declaredSubscription` — the project requires its providers to bill to a
|
|
86
|
+
* subscription credential (`requireOauthProviders`, #852). A statement of
|
|
87
|
+
* intent, so it is believed on its own.
|
|
88
|
+
* - `allowanceWindow` — the provider itself reports an allowance window
|
|
89
|
+
* (`omp usage --json`), which is what a subscription has instead of a price.
|
|
90
|
+
* Positive evidence only: a fleet whose provider reports no window has
|
|
91
|
+
* nothing here, and its zeros stay a fault.
|
|
92
|
+
*/
|
|
93
|
+
export interface SpendBilling {
|
|
94
|
+
declaredSubscription?: boolean;
|
|
95
|
+
allowanceWindow?: string;
|
|
96
|
+
}
|
|
70
97
|
|
|
71
98
|
/**
|
|
72
99
|
* Judge a newest-first sample.
|
|
@@ -78,13 +105,29 @@ export type SpendTelemetryVerdict =
|
|
|
78
105
|
export function judgeSpendTelemetry(
|
|
79
106
|
samples: readonly SpendSample[],
|
|
80
107
|
limit: number,
|
|
108
|
+
billing: SpendBilling = {},
|
|
81
109
|
): SpendTelemetryVerdict {
|
|
82
110
|
const working = samples.filter((row) => row.turns > 0).slice(0, limit);
|
|
83
111
|
if (working.length < limit) {
|
|
84
112
|
return { kind: "insufficient", worked: working.length, needed: limit };
|
|
85
113
|
}
|
|
86
114
|
const missing = working.filter((row) => row.spendUsd === 0).length;
|
|
87
|
-
if (missing === working.length)
|
|
115
|
+
if (missing === working.length) {
|
|
116
|
+
// Total absence is the only verdict a billing class can reclassify, and
|
|
117
|
+
// that asymmetry is the whole safety property: a metered neighbour proves
|
|
118
|
+
// dollars ARE being reported, so a zero beside one is real telemetry loss
|
|
119
|
+
// no matter how the fleet is billed. `partial` therefore always warns.
|
|
120
|
+
const window = subscriptionWindow(billing);
|
|
121
|
+
if (window !== undefined) {
|
|
122
|
+
return {
|
|
123
|
+
kind: "subscription",
|
|
124
|
+
worked: working.length,
|
|
125
|
+
window: window.window,
|
|
126
|
+
evidence: window.evidence,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return { kind: "absent", worked: working.length };
|
|
130
|
+
}
|
|
88
131
|
if (missing / working.length >= SPEND_MISSING_SHARE) {
|
|
89
132
|
return { kind: "partial", worked: working.length, missing };
|
|
90
133
|
}
|
|
@@ -98,8 +141,37 @@ export function judgeSpendTelemetry(
|
|
|
98
141
|
|
|
99
142
|
/** The operator-facing sentence, or undefined when there is nothing to say.
|
|
100
143
|
* Shared so `doctor` and `status` cannot word the same fact differently. */
|
|
144
|
+
/**
|
|
145
|
+
* The allowance window to name, or `undefined` when nothing establishes that
|
|
146
|
+
* this fleet is subscription-billed.
|
|
147
|
+
*
|
|
148
|
+
* A declaration with no readable window still counts — the operator has said
|
|
149
|
+
* how the fleet bills, and a provider that cannot be read is not evidence
|
|
150
|
+
* against it — but it says so rather than inventing a window name.
|
|
151
|
+
*/
|
|
152
|
+
function subscriptionWindow(
|
|
153
|
+
billing: SpendBilling,
|
|
154
|
+
): { window: string; evidence: "declared" | "observed" } | undefined {
|
|
155
|
+
if (billing.allowanceWindow !== undefined && billing.allowanceWindow.trim() !== "") {
|
|
156
|
+
return {
|
|
157
|
+
window: billing.allowanceWindow.trim(),
|
|
158
|
+
evidence: billing.declaredSubscription === true ? "declared" : "observed",
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (billing.declaredSubscription === true) {
|
|
162
|
+
return { window: "unreadable (omp usage --json reported no window)", evidence: "declared" };
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
101
167
|
export function spendTelemetryDetail(verdict: SpendTelemetryVerdict): string | undefined {
|
|
102
168
|
switch (verdict.kind) {
|
|
169
|
+
case "subscription":
|
|
170
|
+
return (
|
|
171
|
+
`the last ${verdict.worked} working runs recorded $0.00, which is correct: this fleet bills to a ` +
|
|
172
|
+
`subscription (${verdict.evidence}), so requests carry no per-request price and no USD cap can fire` +
|
|
173
|
+
` — the constraint that binds is the ${verdict.window} allowance`
|
|
174
|
+
);
|
|
103
175
|
case "absent":
|
|
104
176
|
return (
|
|
105
177
|
`the last ${verdict.worked} completed runs that did any work all recorded $0.00 spend` +
|
package/src/status-render.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { formatDigestBacklog, formatOpenReports } from "./reports.ts";
|
|
|
26
26
|
import { formatDownDuration, formatOrchestratorDown } from "./orchestrator-down.ts";
|
|
27
27
|
import { planUsageLine } from "./usage.ts";
|
|
28
28
|
import { HEALTH_TIMEOUT_MS, SYSTEMD_UNIT, type UnitOwnership } from "./lifecycle.ts";
|
|
29
|
-
import type {
|
|
29
|
+
import type { WorkerPauseView } from "./fleet.ts";
|
|
30
30
|
import { formatRss, rssBytesFromHealthz } from "./host.ts";
|
|
31
31
|
import {
|
|
32
32
|
formatBaseHealth,
|
|
@@ -269,7 +269,8 @@ const GROOMING_IN_FLIGHT_REASON = "in-flight";
|
|
|
269
269
|
* pass saw that no durable row covers: `routed − rows`, clamped at zero so
|
|
270
270
|
* stale rows (candidates since removed from the queue) cannot push it
|
|
271
271
|
* negative. `routed` is the same denominator the tick's grooming trigger
|
|
272
|
-
* reads (`summary.routed >= groomBelow
|
|
272
|
+
* reads (`summary.routed >= groomBelow`; under `groomBelow: "always"`,
|
|
273
|
+
* #988, the duty never clears on volume), and the fleet's queue-digest
|
|
273
274
|
* arithmetic already treats durable rows as current-queue facts
|
|
274
275
|
* (`claimable = routed − knownBlocked`), so this is the operator-facing
|
|
275
276
|
* half of the same subtraction. Parent/epic exclusions and backlog
|
|
@@ -472,7 +473,7 @@ export function formatFleetStatus(
|
|
|
472
473
|
brief: string | undefined = undefined,
|
|
473
474
|
decisions: string | undefined = undefined,
|
|
474
475
|
failureClasses: string | undefined = undefined,
|
|
475
|
-
workerPhases: ReadonlyMap<number,
|
|
476
|
+
workerPhases: ReadonlyMap<number, WorkerPauseView> = new Map(),
|
|
476
477
|
intake: string | undefined = undefined,
|
|
477
478
|
lastStop: DaemonStop | undefined = undefined,
|
|
478
479
|
siblings: { project: string; live: number }[] = [],
|
|
@@ -653,7 +654,7 @@ function formatSiblingLive(siblings: { project: string; live: number }[]): strin
|
|
|
653
654
|
|
|
654
655
|
function formatProjectBody(
|
|
655
656
|
s: StatusSnapshot,
|
|
656
|
-
workerPhases: ReadonlyMap<number,
|
|
657
|
+
workerPhases: ReadonlyMap<number, WorkerPauseView> = new Map(),
|
|
657
658
|
now = Date.now(),
|
|
658
659
|
siblings: { project: string; live: number }[] = [],
|
|
659
660
|
): string {
|
|
@@ -702,6 +703,11 @@ function formatProjectBody(
|
|
|
702
703
|
s.caps.dailySpendUsd === null
|
|
703
704
|
? ` spend today $${s.spendTodayUsd.toFixed(2)} estimated (no daily cap)`
|
|
704
705
|
: ` spend today $${s.spendTodayUsd.toFixed(2)} estimated / $${s.caps.dailySpendUsd.toFixed(2)}` +
|
|
706
|
+
// A dollar ceiling over a subscription-billed fleet can never fire, so
|
|
707
|
+
// showing it bare asserts headroom that is not the constraint (#984).
|
|
708
|
+
// The cap stays visible because it IS configured — what changes is that
|
|
709
|
+
// the row stops implying it governs anything.
|
|
710
|
+
(s.spendTelemetry?.kind === "subscription" ? " (inert — subscription-billed, see below)" : "") +
|
|
705
711
|
(s.reservedSpendUsd === undefined || s.reservedSpendUsd === 0
|
|
706
712
|
? ""
|
|
707
713
|
: ` (+$${s.reservedSpendUsd.toFixed(2)} reserved by runs in flight)`),
|
|
@@ -857,15 +863,28 @@ function formatProjectBody(
|
|
|
857
863
|
} else {
|
|
858
864
|
lines.push(leases.length === 0 ? "mutation leases (none)" : "mutation leases");
|
|
859
865
|
for (const r of leases) {
|
|
860
|
-
const
|
|
866
|
+
const pauseView = workerPhases.get(r.issue);
|
|
867
|
+
const phase = pauseView?.phase;
|
|
861
868
|
// A run in a live review round reads `review-revision N`, distinct from
|
|
862
869
|
// a failure and from an ordinary continuation, with the round number
|
|
863
870
|
// from the durable revision row (#692). The live pause overlay still
|
|
864
871
|
// wins: an operator pause is the newer fact about the same session.
|
|
872
|
+
// The overlay names its source and age when the daemon recorded them
|
|
873
|
+
// (#997): `paused (board, 4m)` answers "who and since when" at the
|
|
874
|
+
// surface where the question actually gets asked.
|
|
865
875
|
const paused = phase === "pausing" || phase === "paused";
|
|
866
876
|
const round = s.reviewRounds?.[r.id];
|
|
877
|
+
const pauseDetail =
|
|
878
|
+
paused && (pauseView?.source !== undefined || pauseView?.pausedAtMs !== undefined)
|
|
879
|
+
? ` (${[
|
|
880
|
+
...(pauseView.source === undefined ? [] : [pauseView.source]),
|
|
881
|
+
...(pauseView.pausedAtMs === undefined
|
|
882
|
+
? []
|
|
883
|
+
: [formatDownDuration(Math.max(0, now - pauseView.pausedAtMs))]),
|
|
884
|
+
].join(", ")})`
|
|
885
|
+
: "";
|
|
867
886
|
const state = paused
|
|
868
|
-
? phase
|
|
887
|
+
? `${phase}${pauseDetail}`
|
|
869
888
|
: round !== undefined
|
|
870
889
|
? `review-revision ${round.round}`
|
|
871
890
|
: r.state;
|
package/src/store.ts
CHANGED
|
@@ -211,6 +211,7 @@ const UPDATABLE_COLUMNS: Record<string, true> = {
|
|
|
211
211
|
graphTools: true,
|
|
212
212
|
failureCharges: true,
|
|
213
213
|
continuationCharges: true,
|
|
214
|
+
terminalEvidence: true,
|
|
214
215
|
};
|
|
215
216
|
|
|
216
217
|
/** Everything SQLite will accept from us. */
|
|
@@ -263,6 +264,7 @@ interface RunRow {
|
|
|
263
264
|
lastError: string | null;
|
|
264
265
|
settlementFlags: string | null;
|
|
265
266
|
report: string | null;
|
|
267
|
+
terminalEvidence: string | null;
|
|
266
268
|
failureClass: string | null;
|
|
267
269
|
recoveryAction: string | null;
|
|
268
270
|
recoveredAt: number | null;
|
|
@@ -746,6 +748,11 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
746
748
|
endedAt INTEGER,
|
|
747
749
|
lastError TEXT,
|
|
748
750
|
settlementFlags TEXT,
|
|
751
|
+
-- The terminal evidence the settlement sweep recorded for this attempt
|
|
752
|
+
-- (#986): exit path, first recoverable error line, audit flags. Nullable:
|
|
753
|
+
-- rows written before the column, or not yet looked at by the sweep, carry
|
|
754
|
+
-- NULL, which reads as "not recorded" — never "nothing was known".
|
|
755
|
+
terminalEvidence TEXT,
|
|
749
756
|
report TEXT,
|
|
750
757
|
failureClass TEXT,
|
|
751
758
|
recoveryAction TEXT,
|
|
@@ -1494,6 +1501,7 @@ function toRecord(row: RunRow): RunRecord {
|
|
|
1494
1501
|
if (flags !== undefined) record.settlementFlags = flags;
|
|
1495
1502
|
}
|
|
1496
1503
|
if (row.report !== null) record.report = row.report;
|
|
1504
|
+
if (row.terminalEvidence !== null) record.terminalEvidence = row.terminalEvidence;
|
|
1497
1505
|
if (row.provider429Count !== null) record.provider429Count = row.provider429Count;
|
|
1498
1506
|
if (row.resolvedModel !== null) record.resolvedModel = row.resolvedModel;
|
|
1499
1507
|
if (row.resolvedProvider !== null) record.resolvedProvider = row.resolvedProvider;
|
|
@@ -1992,6 +2000,13 @@ export function openStore(dbPath: string): Store {
|
|
|
1992
2000
|
if (!columns.some((column) => column.name === "report")) {
|
|
1993
2001
|
db.exec("ALTER TABLE runs ADD COLUMN report TEXT");
|
|
1994
2002
|
}
|
|
2003
|
+
// The terminal evidence the settlement sweep records for every non-clean
|
|
2004
|
+
// row (#986). Rows written before the column were never looked at by the
|
|
2005
|
+
// recorder, so NULL is the honest reading — no backfill, because the
|
|
2006
|
+
// recorder derives evidence from facts only the settling pass holds.
|
|
2007
|
+
if (!columns.some((column) => column.name === "terminalEvidence")) {
|
|
2008
|
+
db.exec("ALTER TABLE runs ADD COLUMN terminalEvidence TEXT");
|
|
2009
|
+
}
|
|
1995
2010
|
// Every row written before #132 is unclassified, and NULL is the honest
|
|
1996
2011
|
// reading of that: the budget counters below deliberately still count an
|
|
1997
2012
|
// unclassified terminal row exactly as this release's predecessor did, so an
|
|
@@ -2516,7 +2531,7 @@ export function openStore(dbPath: string): Store {
|
|
|
2516
2531
|
// the same predicate against the pre-claim row, so a preserved charge can
|
|
2517
2532
|
// never disagree with what the counters would have counted.
|
|
2518
2533
|
const CHARGEABLE_FAILURE_EXCLUSIONS =
|
|
2519
|
-
"'ci-infra', 'settlement-stuck', 'env-start-failure', 'dispatch-infra', 'provider-credit', 'provider-transient', 'provider-capacity', 'returned-for-revision'";
|
|
2534
|
+
"'ci-infra', 'settlement-stuck', 'env-start-failure', 'dispatch-infra', 'provider-credit', 'provider-transient', 'provider-capacity', 'returned-for-revision', 'model-empty-stop'";
|
|
2520
2535
|
const CHARGEABLE_CONTINUATION_EXCLUSIONS =
|
|
2521
2536
|
"'admin-kill', 'settlement-stuck', 'env-start-failure', 'dispatch-infra', 'provider-credit', 'provider-transient', 'provider-capacity'";
|
|
2522
2537
|
|
|
@@ -2545,7 +2560,14 @@ export function openStore(dbPath: string): Store {
|
|
|
2545
2560
|
AND (
|
|
2546
2561
|
(state IN ('killed', 'orphaned', 'blocked')
|
|
2547
2562
|
AND (failureClass IS NULL OR failureClass NOT IN (${CHARGEABLE_CONTINUATION_EXCLUSIONS})))
|
|
2548
|
-
|
|
2563
|
+
-- Two failed-state classes charge a continuation instead of an
|
|
2564
|
+
-- attempt (#795, #1001): a reviewer's decision, and a provider
|
|
2565
|
+
-- that answered with empty turns until the harness gave up.
|
|
2566
|
+
-- Neither is the work failing, and both resume from the head the
|
|
2567
|
+
-- attempt already pushed - but each still consumes a bounded
|
|
2568
|
+
-- budget, so a provider stuck in that state escalates to a human
|
|
2569
|
+
-- rather than looping forever.
|
|
2570
|
+
OR (state = 'failed' AND failureClass IN ('returned-for-revision', 'model-empty-stop'))
|
|
2549
2571
|
)) AS n`,
|
|
2550
2572
|
);
|
|
2551
2573
|
// How many times one issue reached a given class. Recovery uses it to bound a
|
|
@@ -2665,6 +2687,15 @@ export function openStore(dbPath: string): Store {
|
|
|
2665
2687
|
GROUP BY failureClass
|
|
2666
2688
|
ORDER BY n DESC, failureClass ASC`,
|
|
2667
2689
|
);
|
|
2690
|
+
// The surviving `unknown` rows (#986): classified but never explained, each
|
|
2691
|
+
// carrying its recorded terminal evidence for `status` to print. Bounded —
|
|
2692
|
+
// a backlog must not turn one status render into an unbounded scan.
|
|
2693
|
+
const selectUnknownEvidence = db.query<RunRow, [string, number]>(
|
|
2694
|
+
`SELECT * FROM runs
|
|
2695
|
+
WHERE project = ? AND failureClass = 'unknown'
|
|
2696
|
+
ORDER BY startedAt DESC, rowid DESC
|
|
2697
|
+
LIMIT ?`,
|
|
2698
|
+
);
|
|
2668
2699
|
const selectRecoveredSince = db.query<RunRow, [string, number]>(
|
|
2669
2700
|
`SELECT * FROM runs
|
|
2670
2701
|
WHERE project = ? AND recoveredAt IS NOT NULL AND recoveredAt >= ?
|
|
@@ -3727,7 +3758,7 @@ export function openStore(dbPath: string): Store {
|
|
|
3727
3758
|
ELSE 0
|
|
3728
3759
|
END,
|
|
3729
3760
|
continuationCharges = COALESCE(continuationCharges, 0) + CASE
|
|
3730
|
-
WHEN state = 'failed' AND failureClass
|
|
3761
|
+
WHEN state = 'failed' AND failureClass IN ('returned-for-revision', 'model-empty-stop') THEN 1
|
|
3731
3762
|
WHEN state = 'killed' AND (failureClass IS NULL OR failureClass NOT IN (${CHARGEABLE_CONTINUATION_EXCLUSIONS})) THEN 1
|
|
3732
3763
|
ELSE 0
|
|
3733
3764
|
END
|
|
@@ -5082,6 +5113,11 @@ export function openStore(dbPath: string): Store {
|
|
|
5082
5113
|
return entry;
|
|
5083
5114
|
},
|
|
5084
5115
|
|
|
5116
|
+
/** The surviving `unknown` rows, newest first, with whatever terminal
|
|
5117
|
+
* evidence the sweep recorded on them (#986). Bounded per call. */
|
|
5118
|
+
unknownRuns(project: string, limit = 20): RunRecord[] {
|
|
5119
|
+
return selectUnknownEvidence.all(project, limit).map(toRecord);
|
|
5120
|
+
},
|
|
5085
5121
|
verbLedger(
|
|
5086
5122
|
project: string,
|
|
5087
5123
|
opts: { runId?: string; issue?: number; limit?: number } = {},
|
package/src/types.ts
CHANGED
|
@@ -1007,6 +1007,16 @@ export type ArmProof = (typeof ARM_PROOFS)[number];
|
|
|
1007
1007
|
*/
|
|
1008
1008
|
export const DEFAULT_ARM_PROOF: ArmProof = "challenge";
|
|
1009
1009
|
|
|
1010
|
+
/**
|
|
1011
|
+
* The grooming trigger (#988): a numeric threshold keeps today's behaviour —
|
|
1012
|
+
* the tick grooms when the routable count is below it — and the literal
|
|
1013
|
+
* `"always"` grooms on demand every tick while any ungroomed, unrefused,
|
|
1014
|
+
* unparked candidate exists, judged from candidate state rather than queue
|
|
1015
|
+
* volume. Never spell "always" as a large number: a big queue must not
|
|
1016
|
+
* silence the duty.
|
|
1017
|
+
*/
|
|
1018
|
+
export type GroomTrigger = number | "always";
|
|
1019
|
+
|
|
1010
1020
|
/**
|
|
1011
1021
|
* Everything the dispatcher needs to service one product: where work comes
|
|
1012
1022
|
* from, where code goes, and what it may spend doing it. Config is per project
|
|
@@ -1020,9 +1030,10 @@ export interface ProjectConfig {
|
|
|
1020
1030
|
/** The one label that means "a human has signed this off as agent-ready". */
|
|
1021
1031
|
queueLabel: string;
|
|
1022
1032
|
/** Routable-candidate count below which the tick prompt tells the orchestrator
|
|
1023
|
-
* to groom the queue
|
|
1033
|
+
* to groom the queue, or {@link GroomTrigger} `"always"` to groom on demand
|
|
1034
|
+
* every tick (#988). Optional; defaults to {@link DEFAULT_GROOM_BELOW} in
|
|
1024
1035
|
* orchestrator-tick.ts. */
|
|
1025
|
-
groomBelow?:
|
|
1036
|
+
groomBelow?: GroomTrigger;
|
|
1026
1037
|
/** Labels the dispatcher writes back so the tracker alone shows live state
|
|
1027
1038
|
* to a human who never opens the daemon's logs. `backlog` is the
|
|
1028
1039
|
* operator's own park gesture (#507) — not dispatcher-written, but read by
|
|
@@ -1683,6 +1694,19 @@ export const FAILURE_CLASSES = [
|
|
|
1683
1694
|
/** A reviewer closed pushed-green or pushed-pending work without merging it:
|
|
1684
1695
|
* a review decision, not a worker failure. */
|
|
1685
1696
|
"returned-for-revision",
|
|
1697
|
+
/** The session ended without delivering any settlement verdict: no yield, no
|
|
1698
|
+
* green claim, no error — the row's last words are mid-delivery narration
|
|
1699
|
+
* (#986). Distinct from `unknown` on purpose: the evidence proves the run
|
|
1700
|
+
* reached its worker and died between turns, so the gap it names is "the
|
|
1701
|
+
* model stopped before handing over" rather than "nothing is known". It
|
|
1702
|
+
* charges the attempt exactly as `unknown` did; only the name is specific. */
|
|
1703
|
+
"no-verdict",
|
|
1704
|
+
/** The provider answered with empty assistant turns until the harness's own
|
|
1705
|
+
* retry cap gave up and ended the session (#986): the terminal line reads
|
|
1706
|
+
* `empty-stop-retry-cap`. Provider flake, not worker logic — both of the
|
|
1707
|
+
* 2026-08-23 failures were this, at 100/180 and 54/180 turns, while a third
|
|
1708
|
+
* run on the same model finished green in the same window. */
|
|
1709
|
+
"model-empty-stop",
|
|
1686
1710
|
"unknown",
|
|
1687
1711
|
] as const;
|
|
1688
1712
|
|
|
@@ -2084,6 +2108,15 @@ export interface RunRecord {
|
|
|
2084
2108
|
endedAt?: number;
|
|
2085
2109
|
/** Last failure text, surfaced verbatim in escalations. */
|
|
2086
2110
|
lastError?: string;
|
|
2111
|
+
/**
|
|
2112
|
+
* The terminal evidence the settlement sweep recorded for this attempt
|
|
2113
|
+
* (#986): the exit path the row took, the first line of whatever error was
|
|
2114
|
+
* recoverable from the row or its transcript, and any settlement-audit
|
|
2115
|
+
* flags — one bounded line, readable in `status` beside a surviving
|
|
2116
|
+
* `unknown`. Absent means the row predates the column, or the sweep has not
|
|
2117
|
+
* looked at it yet; it never means "nothing was known".
|
|
2118
|
+
*/
|
|
2119
|
+
terminalEvidence?: string;
|
|
2087
2120
|
/** Advisory settlement-audit findings for this attempt (#128). Deliberately
|
|
2088
2121
|
* never consulted by anything that decides `state`: a flagged run settles
|
|
2089
2122
|
* exactly as an unflagged one does, and the flags are evidence for whoever
|
|
@@ -3285,6 +3318,10 @@ export interface Store {
|
|
|
3285
3318
|
* every actionable class behind a monotonically growing tally.
|
|
3286
3319
|
*/
|
|
3287
3320
|
recordedOnlyClassCounts(project: string): { cls: FailureClass; n: number }[];
|
|
3321
|
+
/** The surviving `unknown` rows, newest first, each with whatever terminal
|
|
3322
|
+
* evidence the sweep recorded on it (#986). Bounded per call — a status
|
|
3323
|
+
* render must never walk a whole backlog. */
|
|
3324
|
+
unknownRuns(project: string, limit?: number): RunRecord[];
|
|
3288
3325
|
/** Rows whose recovery ran at or after `since`, newest first — the tick's
|
|
3289
3326
|
* "auto-recovered since last tick" line, and its count. */
|
|
3290
3327
|
recoveredSince(project: string, since: number): RunRecord[];
|
package/src/usage.ts
CHANGED
|
@@ -688,6 +688,31 @@ export async function readPlanUsage(
|
|
|
688
688
|
* to fit on one row. Derived from the same status object as the long form so
|
|
689
689
|
* the two cannot disagree about whether the guard is active.
|
|
690
690
|
*/
|
|
691
|
+
/**
|
|
692
|
+
* The allowance window nearest its ceiling, named for a reader (#984).
|
|
693
|
+
*
|
|
694
|
+
* A subscription has an allowance where an API key has a price, so this is what
|
|
695
|
+
* "the constraint that binds" means on a subscription-billed fleet — and the
|
|
696
|
+
* one nearest exhaustion is the one that will stop the fleet first, whatever
|
|
697
|
+
* the others say. Read from the provider's own reply, never derived: a fleet
|
|
698
|
+
* whose provider reports no window gets `undefined`, which is the honest answer
|
|
699
|
+
* and keeps a genuine telemetry fault a fault.
|
|
700
|
+
*
|
|
701
|
+
* Only windows that reported a comparable fraction are eligible, for the reason
|
|
702
|
+
* this module already documents at length: `used` is not comparable across
|
|
703
|
+
* providers, so a raw count can never stand in for one.
|
|
704
|
+
*/
|
|
705
|
+
export function bindingAllowanceWindow(reading: UsageReading): string | undefined {
|
|
706
|
+
if (reading.kind !== "ok") return undefined;
|
|
707
|
+
const comparable = reading.windows.filter((w) => w.usedFraction !== undefined);
|
|
708
|
+
if (comparable.length === 0) return undefined;
|
|
709
|
+
let worst = comparable[0]!;
|
|
710
|
+
for (const window of comparable.slice(1)) {
|
|
711
|
+
if ((window.usedFraction ?? 0) > (worst.usedFraction ?? 0)) worst = window;
|
|
712
|
+
}
|
|
713
|
+
return worst.label === undefined ? worst.id : `${worst.id} (${worst.label})`;
|
|
714
|
+
}
|
|
715
|
+
|
|
691
716
|
export function planUsageBadge(status: PlanUsageStatus | undefined): string {
|
|
692
717
|
if (status === undefined) return "plan not read";
|
|
693
718
|
let core: string;
|