omp-conductor 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -41
- package/REFERENCE.md +866 -31
- package/agents/to-spec.md +6 -2
- package/package.json +1 -1
- package/schema/config.schema.json +32 -1
- package/src/admission.ts +212 -26
- package/src/arm-challenge.ts +250 -57
- package/src/ask.ts +288 -1
- package/src/briefs/orchestrator.md +27 -13
- package/src/briefs/to-spec.md +6 -2
- package/src/cli.ts +127 -2
- package/src/command-help.ts +9 -1
- package/src/command-manifest.ts +52 -8
- package/src/commands/arm.ts +6 -2
- package/src/commands/context.ts +2 -0
- package/src/commands/intake.ts +4 -19
- package/src/commands/message.ts +26 -2
- package/src/commands/reconcile-units.ts +104 -0
- package/src/commands/release-composition.ts +232 -0
- package/src/commands/resume.ts +2 -27
- package/src/commands/setup.ts +101 -16
- package/src/commands/stats.ts +11 -30
- package/src/commands/tail.ts +31 -1
- package/src/commands/upgrade.ts +20 -3
- package/src/commands/verb.ts +2 -1
- package/src/commands/watch.ts +4 -17
- package/src/config-schema.ts +38 -6
- package/src/config.ts +103 -8
- package/src/credential-class.ts +366 -0
- package/src/daemon.ts +1368 -529
- package/src/dashboard/app.js +504 -2
- package/src/dashboard/controls.ts +336 -0
- package/src/dashboard/index.html +30 -0
- package/src/dashboard/server.ts +271 -30
- package/src/dashboard/style.css +116 -0
- package/src/dashboard/transcript.ts +173 -0
- package/src/decisions.ts +19 -11
- package/src/doctor.ts +431 -148
- package/src/escalate.ts +22 -11
- package/src/failure-class.ts +59 -0
- package/src/fleet.ts +587 -230
- package/src/host.ts +6 -455
- package/src/omp-settings.ts +19 -0
- package/src/omp.ts +40 -56
- package/src/orchestrator-tick.ts +564 -121
- package/src/pause.ts +233 -0
- package/src/session-host.ts +6 -41
- package/src/settlement.ts +159 -2
- package/src/setup-answers.ts +97 -0
- package/src/setup-host.ts +343 -1160
- package/src/setup-install.ts +204 -27
- package/src/setup-wizard.ts +252 -51
- package/src/setup.ts +87 -4
- package/src/spend-telemetry.ts +117 -0
- package/src/stats.ts +35 -0
- package/src/status-render.ts +485 -19
- package/src/store.ts +1229 -55
- package/src/telegram-freshness.ts +269 -0
- package/src/to-spec.ts +50 -2
- package/src/types.ts +759 -10
- package/src/unblock.ts +22 -0
- package/src/unit-reconcile.ts +303 -0
- package/src/upgrade-verify.ts +8 -1
- package/src/upgrade.ts +299 -12
- package/src/verbs/actions.ts +124 -10
- package/src/verbs/protocol.ts +70 -2
- package/src/verbs/server.ts +485 -11
- package/src/wake.ts +48 -0
- package/src/worker.ts +401 -14
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the daily spend cap's own input still arriving? (#970)
|
|
3
|
+
*
|
|
4
|
+
* The cap compares `store.spendSince(project, startOfToday())` — a sum of the
|
|
5
|
+
* per-run `spendUsd` column — against its ceiling. When the harness stops
|
|
6
|
+
* reporting cost, that column reads 0.00 and the cap silently measures a
|
|
7
|
+
* fraction of reality. #46 named this once already: **$0.00 spend is not proof
|
|
8
|
+
* of no spend.**
|
|
9
|
+
*
|
|
10
|
+
* Measured on this fleet, counting only runs that actually did work:
|
|
11
|
+
*
|
|
12
|
+
* | day | worked | reported $0.00 |
|
|
13
|
+
* |------------|--------|----------------|
|
|
14
|
+
* | 2026-08-15 | 53 | 3.8% |
|
|
15
|
+
* | … | | 0–2.5% |
|
|
16
|
+
* | 2026-08-20 | 15 | 0.0% |
|
|
17
|
+
* | 2026-08-21 | 22 | 31.8% |
|
|
18
|
+
* | 2026-08-22 | 12 | 75.0% |
|
|
19
|
+
*
|
|
20
|
+
* Six days of ≤3.8% baseline, then 32%, then 75% — with #614 running 213 turns
|
|
21
|
+
* for $0.00.
|
|
22
|
+
*
|
|
23
|
+
* Two properties are load-bearing, and both were wrong in the predicate this
|
|
24
|
+
* replaces:
|
|
25
|
+
*
|
|
26
|
+
* - **Only runs that did work are judged.** A run killed before its first turn
|
|
27
|
+
* records $0.00 honestly; 26 of this project's 48 zero-spend rows are exactly
|
|
28
|
+
* that. Counting them inflates the ratio *and* lets a run of administrative
|
|
29
|
+
* kills fire a telemetry finding that says nothing about telemetry.
|
|
30
|
+
* - **A ratio, not `every`.** The old check fired only when every sampled run
|
|
31
|
+
* reported zero. Replayed over all 410 windows in this fleet's history it
|
|
32
|
+
* would have fired 14 times while staying silent through 37 windows that had
|
|
33
|
+
* lost a majority of their telemetry — so the common case, partial loss, was
|
|
34
|
+
* the invisible one. On 2026-08-21, at 31.8% loss, every window that day
|
|
35
|
+
* contained one metered run and the probe was silent all day.
|
|
36
|
+
*
|
|
37
|
+
* One judgement, two consumers: `doctor`'s finding and the `status` row. The
|
|
38
|
+
* cap's figure and the statement about whether that figure means anything must
|
|
39
|
+
* never come from two implementations that can disagree.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/** One sampled run, reduced to what the judgement needs. */
|
|
43
|
+
export interface SpendSample {
|
|
44
|
+
/** Turns taken. Zero means the run never worked, so its $0.00 is honest. */
|
|
45
|
+
turns: number;
|
|
46
|
+
spendUsd: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The share of *working* runs whose spend is missing at or above which this is
|
|
51
|
+
* reported.
|
|
52
|
+
*
|
|
53
|
+
* A majority, deliberately — the observed healthy baseline is 0–3.8%, not zero.
|
|
54
|
+
* A legitimately free or aborted-after-one-turn run happens, and a check that
|
|
55
|
+
* fires on one of them is a check that gets ignored, which is the failure the
|
|
56
|
+
* install-surface rows already name: a warning repeated every fifteen minutes
|
|
57
|
+
* trains an operator to ignore the row that matters.
|
|
58
|
+
*/
|
|
59
|
+
export const SPEND_MISSING_SHARE = 0.5;
|
|
60
|
+
|
|
61
|
+
export type SpendTelemetryVerdict =
|
|
62
|
+
/** Enough working runs, and most of them metered. */
|
|
63
|
+
| { kind: "healthy"; worked: number; missing: number; totalUsd: number }
|
|
64
|
+
/** Not enough working runs to say anything — never a pass by default. */
|
|
65
|
+
| { kind: "insufficient"; worked: number; needed: number }
|
|
66
|
+
/** A majority of working runs reported nothing. */
|
|
67
|
+
| { kind: "partial"; worked: number; missing: number }
|
|
68
|
+
/** Every working run reported nothing — the same fact, stated louder. */
|
|
69
|
+
| { kind: "absent"; worked: number };
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Judge a newest-first sample.
|
|
73
|
+
*
|
|
74
|
+
* `limit` is how many *working* runs are wanted, not how many rows to read: the
|
|
75
|
+
* caller over-samples and this takes the first `limit` runs that did work, so a
|
|
76
|
+
* burst of 0-turn kills cannot push the real evidence out of the window.
|
|
77
|
+
*/
|
|
78
|
+
export function judgeSpendTelemetry(
|
|
79
|
+
samples: readonly SpendSample[],
|
|
80
|
+
limit: number,
|
|
81
|
+
): SpendTelemetryVerdict {
|
|
82
|
+
const working = samples.filter((row) => row.turns > 0).slice(0, limit);
|
|
83
|
+
if (working.length < limit) {
|
|
84
|
+
return { kind: "insufficient", worked: working.length, needed: limit };
|
|
85
|
+
}
|
|
86
|
+
const missing = working.filter((row) => row.spendUsd === 0).length;
|
|
87
|
+
if (missing === working.length) return { kind: "absent", worked: working.length };
|
|
88
|
+
if (missing / working.length >= SPEND_MISSING_SHARE) {
|
|
89
|
+
return { kind: "partial", worked: working.length, missing };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
kind: "healthy",
|
|
93
|
+
worked: working.length,
|
|
94
|
+
missing,
|
|
95
|
+
totalUsd: working.reduce((sum, row) => sum + row.spendUsd, 0),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The operator-facing sentence, or undefined when there is nothing to say.
|
|
100
|
+
* Shared so `doctor` and `status` cannot word the same fact differently. */
|
|
101
|
+
export function spendTelemetryDetail(verdict: SpendTelemetryVerdict): string | undefined {
|
|
102
|
+
switch (verdict.kind) {
|
|
103
|
+
case "absent":
|
|
104
|
+
return (
|
|
105
|
+
`the last ${verdict.worked} completed runs that did any work all recorded $0.00 spend` +
|
|
106
|
+
" — the USD cap cannot fire on zeros, and $0.00 is not proof of no spend"
|
|
107
|
+
);
|
|
108
|
+
case "partial":
|
|
109
|
+
return (
|
|
110
|
+
`${verdict.missing} of the last ${verdict.worked} completed runs that did any work recorded ` +
|
|
111
|
+
"$0.00 spend — the USD cap is comparing a fraction of actual spend against its ceiling"
|
|
112
|
+
);
|
|
113
|
+
case "healthy":
|
|
114
|
+
case "insufficient":
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
package/src/stats.ts
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* counted separately as **unmetered** and never averaged into a per-issue cost.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import { utcDay } from "./store.ts";
|
|
22
23
|
import type { RunRecord } from "./types.ts";
|
|
23
24
|
|
|
24
25
|
/** The bounded window a report answers over. Day keys are UTC (`YYYY-MM-DD`). */
|
|
@@ -181,6 +182,40 @@ function summarize(args: {
|
|
|
181
182
|
* settled there. Rows within an issue must be ordered by `startedAt`, so the
|
|
182
183
|
* first row of a chain is its first queue-label claim.
|
|
183
184
|
*/
|
|
185
|
+
/** `7d` / `30d` / any positive whole-day duration. */
|
|
186
|
+
const DURATION_FORM = /^(\d+)d$/;
|
|
187
|
+
/** A bare UTC date: the window starts at 00:00:00Z that day. */
|
|
188
|
+
const DATE_FORM = /^\d{4}-\d{2}-\d{2}$/;
|
|
189
|
+
const DAY_MS = 86_400_000;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* `--since` (or `?since=`) to a full window, or `undefined` when it is not one.
|
|
193
|
+
*
|
|
194
|
+
* Here rather than in the CLI because the dashboard needs the identical parse
|
|
195
|
+
* (#297): two parsers would eventually disagree about what `30d` means, and the
|
|
196
|
+
* page's numbers are supposed to equal `stats --json` for the same window. The
|
|
197
|
+
* CLI turns `undefined` into its exit-2 message and the endpoint turns it into a
|
|
198
|
+
* 400 — neither silently defaults a typo'd window, because measuring the wrong
|
|
199
|
+
* week and saying nothing is the failure this shape prevents.
|
|
200
|
+
*/
|
|
201
|
+
export function parseStatsWindow(raw: string, now: number): StatsWindow | undefined {
|
|
202
|
+
const until = { untilEpochMs: now, untilDay: utcDay(now) };
|
|
203
|
+
const duration = DURATION_FORM.exec(raw);
|
|
204
|
+
if (duration !== null) {
|
|
205
|
+
const days = Number(duration[1]);
|
|
206
|
+
if (days >= 1) {
|
|
207
|
+
const sinceEpochMs = now - days * DAY_MS;
|
|
208
|
+
return { sinceEpochMs, sinceDay: utcDay(sinceEpochMs), ...until };
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
if (DATE_FORM.test(raw)) {
|
|
213
|
+
const sinceEpochMs = Date.parse(`${raw}T00:00:00Z`);
|
|
214
|
+
if (Number.isFinite(sinceEpochMs)) return { sinceEpochMs, sinceDay: raw, ...until };
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
184
219
|
export function computeStats(args: {
|
|
185
220
|
project: string;
|
|
186
221
|
window: StatsWindow;
|