wicked-crew 0.7.1 → 0.7.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.
Files changed (56) hide show
  1. package/dist/api/delivery-index.d.ts +61 -0
  2. package/dist/api/delivery-index.d.ts.map +1 -0
  3. package/dist/api/delivery-index.js +87 -0
  4. package/dist/api/delivery-index.js.map +1 -0
  5. package/dist/api/governance-steering.d.ts +139 -0
  6. package/dist/api/governance-steering.d.ts.map +1 -0
  7. package/dist/api/governance-steering.js +302 -0
  8. package/dist/api/governance-steering.js.map +1 -0
  9. package/dist/api/governance-wiki.d.ts +28 -0
  10. package/dist/api/governance-wiki.d.ts.map +1 -0
  11. package/dist/api/governance-wiki.js +97 -0
  12. package/dist/api/governance-wiki.js.map +1 -0
  13. package/dist/api/routes.d.ts +21 -16
  14. package/dist/api/routes.d.ts.map +1 -1
  15. package/dist/api/routes.js +196 -10
  16. package/dist/api/routes.js.map +1 -1
  17. package/dist/api/server.d.ts.map +1 -1
  18. package/dist/api/server.js +56 -1
  19. package/dist/api/server.js.map +1 -1
  20. package/dist/api/testing.d.ts +117 -0
  21. package/dist/api/testing.d.ts.map +1 -0
  22. package/dist/api/testing.js +152 -0
  23. package/dist/api/testing.js.map +1 -0
  24. package/dist/campaign/supervision.d.ts +296 -0
  25. package/dist/campaign/supervision.d.ts.map +1 -0
  26. package/dist/campaign/supervision.js +515 -0
  27. package/dist/campaign/supervision.js.map +1 -0
  28. package/dist/campaigns/plan.d.ts +64 -0
  29. package/dist/campaigns/plan.d.ts.map +1 -0
  30. package/dist/campaigns/plan.js +201 -0
  31. package/dist/campaigns/plan.js.map +1 -0
  32. package/dist/campaigns/routes.d.ts +28 -0
  33. package/dist/campaigns/routes.d.ts.map +1 -0
  34. package/dist/campaigns/routes.js +177 -0
  35. package/dist/campaigns/routes.js.map +1 -0
  36. package/dist/core/adapter.d.ts +160 -1
  37. package/dist/core/adapter.d.ts.map +1 -1
  38. package/dist/core/adapter.js +300 -1
  39. package/dist/core/adapter.js.map +1 -1
  40. package/dist/projects/routes.d.ts +4 -4
  41. package/dist/qe/acceptance.d.ts +20 -1
  42. package/dist/qe/acceptance.d.ts.map +1 -1
  43. package/dist/qe/acceptance.js +32 -0
  44. package/dist/qe/acceptance.js.map +1 -1
  45. package/dist/qe/conformance.d.ts +158 -0
  46. package/dist/qe/conformance.d.ts.map +1 -0
  47. package/dist/qe/conformance.js +244 -0
  48. package/dist/qe/conformance.js.map +1 -0
  49. package/dist/studio/assets/index-BAW9rqNT.css +32 -0
  50. package/dist/studio/assets/index-UahdRwF7.js +532 -0
  51. package/dist/studio/index.html +2 -2
  52. package/dist/studio/testid-inventory.json +4387 -0
  53. package/endpoint-manifest.json +154 -9
  54. package/package.json +3 -3
  55. package/dist/studio/assets/index-D-BFUYnY.js +0 -530
  56. package/dist/studio/assets/index-D6S9zUtO.css +0 -32
@@ -0,0 +1,515 @@
1
+ // Campaign budget/runtime governance (TH-20 / test-R22): the crew-side supervision layer
2
+ // that makes campaign spend BOUNDED and every abort VISIBLE.
3
+ //
4
+ // Why this exists: core's `CampaignDef` carries exactly one resource knob — `max_concurrency`
5
+ // (DES-CAMPAIGN-001 §2) — and the engine's per-unit wall-clock default is 2 HOURS
6
+ // (wicked-core `execute_wrapped.rs` `unit_timeout()`, override `WICKED_UNIT_TIMEOUT_SECS`).
7
+ // A nightly 20+-node governed campaign with no ceiling is an unbounded spend commitment.
8
+ //
9
+ // KNOB PLACEMENT — the P-9 interim decision, recorded here on purpose:
10
+ // Budget/timeout/kill knobs live CREW-SIDE (this module), NOT as `CampaignDef` fields in
11
+ // core. Crew supervision ships without a core release and rides the cancel path that
12
+ // already exists (`CancelRun` via the adapter). The accepted cost: supervision state does
13
+ // not survive an ENGINE-ONLY resume — resume a campaign without the crew daemon and
14
+ // nothing enforces the budget. Moving the knobs into `CampaignDef` (where they would
15
+ // survive resume) stays open as P-9's core-maintainer call; this module is written so the
16
+ // policy type can migrate onto the def without changing its semantics.
17
+ // Doctrine + operator docs: `docs/campaign-budgets.md`.
18
+ //
19
+ // What the supervisor enforces, sweep-driven like `api/stall-watchdog.ts` (same
20
+ // deps-injected/fake-clock idiom) but ENFORCING where the watchdog is detection-only:
21
+ // 1. Per-node timeout — a node RUNNING longer than `nodeTimeoutMs` is cancelled with an
22
+ // explicit `node-timeout` exclusion. `awaiting-human` never counts against it (a gate
23
+ // is quiet by design and burns no worker spend).
24
+ // 2. Campaign wall-clock budget — when `elapsed > wallClockBudgetMs`, remaining nodes are
25
+ // aborted with EXCLUDED-WITH-REASON status: never-launched nodes are excluded at the
26
+ // scheduler so they can never dispatch; in-flight nodes follow the kill policy
27
+ // (`kill-running` cancels them, `abandon-running` lets them finish while launching
28
+ // nothing new). Exclusion is NEVER silent: it lands in supervision state, in the
29
+ // evidence report, on the /ws fan-out as synthetic frames, and in the warn log.
30
+ // 3. Campaign cost budget (opt-in) — same exhaustion path keyed on recorded USD cost.
31
+ // 4. Nightly cap — `assertWithinNightlyCap` fail-closed preflight on node count, so the
32
+ // TH-23 nightly recipe cannot launch an uncapped campaign by omission.
33
+ //
34
+ // Cost per node is recorded in evidence: the supervisor stamps wall-clock per node itself
35
+ // (always available) and accepts token/USD figures via `recordNodeCost` when the worker CLI
36
+ // reports them; `buildSupervisionReport`/`writeSupervisionReport` emit the
37
+ // `campaign-supervision.json` evidence artifact whose per-node rows the TH-14 scoreboard
38
+ // cost column diffs — cost regressions become visible exactly like verdict regressions.
39
+ // (Deliberately an ARTIFACT, not new ledger-manifest fields: manifest 2.1 is wicked-ledger's
40
+ // contract and moves through its owner's release train, never ad hoc — ADR 0006.)
41
+ import { writeFile } from 'node:fs/promises';
42
+ import { join } from 'node:path';
43
+ /** Campaign-proven pin (the 2026-08 studio E2E campaign ran `WICKED_UNIT_TIMEOUT_SECS=900`). */
44
+ export const DEFAULT_UNIT_TIMEOUT_SECS = 900;
45
+ /** Default per-node timeout: 30 min ≈ 4× the observed 7m41s governed feature run. */
46
+ export const DEFAULT_NODE_TIMEOUT_SECS = 1_800;
47
+ /** Default campaign wall-clock budget: 4 h (a 21-node ladder ≈ 2.7 h serial at observed rate). */
48
+ export const DEFAULT_WALL_CLOCK_BUDGET_SECS = 14_400;
49
+ /** Default nightly node cap (the proven ladder was 21 scenarios; 25 leaves headroom). */
50
+ export const DEFAULT_MAX_NODES = 25;
51
+ export const DEFAULT_KILL_POLICY = 'kill-running';
52
+ /** The env knobs `resolveBudgetPolicy` reads. All crew-side; documented in docs/campaign-budgets.md. */
53
+ export const ENV_BUDGET_SECS = 'WICKED_CAMPAIGN_BUDGET_SECS';
54
+ export const ENV_NODE_TIMEOUT_SECS = 'WICKED_CAMPAIGN_NODE_TIMEOUT_SECS';
55
+ export const ENV_UNIT_TIMEOUT_SECS = 'WICKED_UNIT_TIMEOUT_SECS';
56
+ export const ENV_MAX_NODES = 'WICKED_CAMPAIGN_MAX_NODES';
57
+ export const ENV_MAX_COST_USD = 'WICKED_CAMPAIGN_MAX_COST_USD';
58
+ export const ENV_KILL_POLICY = 'WICKED_CAMPAIGN_KILL_POLICY';
59
+ /** Strict positive-number parse: rejects '', '4h', '-1', '0', NaN — never a silent fallback. */
60
+ function parsePositive(name, raw) {
61
+ const trimmed = raw.trim();
62
+ const n = /^\d+(\.\d+)?$/.test(trimmed) ? Number(trimmed) : Number.NaN;
63
+ if (!Number.isFinite(n) || n <= 0) {
64
+ throw new Error(`campaign budget policy: ${name}=${JSON.stringify(raw)} is not a positive number — ` +
65
+ `refusing to guess (a malformed ceiling must never mean "unbounded")`);
66
+ }
67
+ return n;
68
+ }
69
+ /**
70
+ * Resolve the budget policy from an environment (defaults ↦ documented ceilings; malformed
71
+ * values throw — fail closed, TH-8 posture). Pass `process.env` in the daemon; tests pass
72
+ * a literal.
73
+ */
74
+ export function resolveBudgetPolicy(env = process.env) {
75
+ const budgetSecs = env[ENV_BUDGET_SECS] !== undefined && env[ENV_BUDGET_SECS] !== ''
76
+ ? parsePositive(ENV_BUDGET_SECS, env[ENV_BUDGET_SECS])
77
+ : DEFAULT_WALL_CLOCK_BUDGET_SECS;
78
+ const nodeTimeoutSecs = env[ENV_NODE_TIMEOUT_SECS] !== undefined && env[ENV_NODE_TIMEOUT_SECS] !== ''
79
+ ? parsePositive(ENV_NODE_TIMEOUT_SECS, env[ENV_NODE_TIMEOUT_SECS])
80
+ : DEFAULT_NODE_TIMEOUT_SECS;
81
+ // The ambient WICKED_UNIT_TIMEOUT_SECS is HONORED as the pin (integer seconds — the
82
+ // engine's own u64 parse would reject a fraction, so we must too).
83
+ const rawUnit = env[ENV_UNIT_TIMEOUT_SECS];
84
+ let unitTimeoutSecs = DEFAULT_UNIT_TIMEOUT_SECS;
85
+ if (rawUnit !== undefined && rawUnit !== '') {
86
+ unitTimeoutSecs = parsePositive(ENV_UNIT_TIMEOUT_SECS, rawUnit);
87
+ if (!Number.isInteger(unitTimeoutSecs)) {
88
+ throw new Error(`campaign budget policy: ${ENV_UNIT_TIMEOUT_SECS}=${JSON.stringify(rawUnit)} must be ` +
89
+ `an integer number of seconds (the engine parses it as u64 and would silently ` +
90
+ `ignore this value, falling back to its 2-hour default)`);
91
+ }
92
+ }
93
+ const maxNodes = env[ENV_MAX_NODES] !== undefined && env[ENV_MAX_NODES] !== ''
94
+ ? parsePositive(ENV_MAX_NODES, env[ENV_MAX_NODES])
95
+ : DEFAULT_MAX_NODES;
96
+ if (!Number.isInteger(maxNodes)) {
97
+ throw new Error(`campaign budget policy: ${ENV_MAX_NODES} must be an integer node count, got ` +
98
+ JSON.stringify(env[ENV_MAX_NODES]));
99
+ }
100
+ const rawKill = env[ENV_KILL_POLICY];
101
+ let killPolicy = DEFAULT_KILL_POLICY;
102
+ if (rawKill !== undefined && rawKill !== '') {
103
+ if (rawKill !== 'kill-running' && rawKill !== 'abandon-running') {
104
+ throw new Error(`campaign budget policy: ${ENV_KILL_POLICY}=${JSON.stringify(rawKill)} — expected ` +
105
+ `"kill-running" or "abandon-running"`);
106
+ }
107
+ killPolicy = rawKill;
108
+ }
109
+ const policy = {
110
+ wallClockBudgetMs: budgetSecs * 1_000,
111
+ nodeTimeoutMs: nodeTimeoutSecs * 1_000,
112
+ unitTimeoutSecs,
113
+ maxNodes,
114
+ killPolicy,
115
+ };
116
+ const rawCost = env[ENV_MAX_COST_USD];
117
+ if (rawCost !== undefined && rawCost !== '') {
118
+ policy.maxCostUsd = parsePositive(ENV_MAX_COST_USD, rawCost);
119
+ }
120
+ return policy;
121
+ }
122
+ /**
123
+ * The TH-8-style environment pins every campaign node MUST launch with. Until the full
124
+ * TH-8 environment manifest lands, this is the manifest's budget block: the interim
125
+ * mitigation test-R22 names ("the environment manifest must pin WICKED_UNIT_TIMEOUT_SECS").
126
+ */
127
+ export function nodeEnvPins(policy) {
128
+ return { [ENV_UNIT_TIMEOUT_SECS]: String(policy.unitTimeoutSecs) };
129
+ }
130
+ /**
131
+ * Fail-closed preflight: a node environment must carry the exact `WICKED_UNIT_TIMEOUT_SECS`
132
+ * pin the policy resolved. Missing ⇒ the engine's 2-hour default applies silently;
133
+ * mismatched ⇒ the manifest lies about the environment. Both refuse.
134
+ */
135
+ export function assertNodeEnvPinned(nodeEnv, policy) {
136
+ const pin = nodeEnv[ENV_UNIT_TIMEOUT_SECS];
137
+ if (pin === undefined || pin.trim() === '') {
138
+ throw new Error(`campaign preflight: node environment is missing the ${ENV_UNIT_TIMEOUT_SECS} pin ` +
139
+ `(expected "${policy.unitTimeoutSecs}"). Unpinned, the engine defaults to 7200 s ` +
140
+ `per unit — refusing to launch an effectively unbounded node`);
141
+ }
142
+ if (pin.trim() !== String(policy.unitTimeoutSecs)) {
143
+ throw new Error(`campaign preflight: ${ENV_UNIT_TIMEOUT_SECS} pin mismatch — node environment says ` +
144
+ `${JSON.stringify(pin)}, the budget policy pinned "${policy.unitTimeoutSecs}". ` +
145
+ `Fail-closed: fix the environment or the policy, never both silently`);
146
+ }
147
+ }
148
+ /**
149
+ * Nightly cap, fail-closed (test-R22 interim: "the nightly recipe must cap campaign size
150
+ * explicitly"). Called with the campaign's node count BEFORE launch; over-cap refuses with
151
+ * both numbers so the operator sees exactly what to trim or raise.
152
+ */
153
+ export function assertWithinNightlyCap(nodeCount, policy) {
154
+ if (!Number.isInteger(nodeCount) || nodeCount < 0) {
155
+ throw new Error(`campaign preflight: node count must be a non-negative integer, got ${nodeCount}`);
156
+ }
157
+ if (nodeCount > policy.maxNodes) {
158
+ throw new Error(`campaign preflight: ${nodeCount} nodes exceeds the nightly cap of ${policy.maxNodes} ` +
159
+ `(${ENV_MAX_NODES}). Refusing to launch — split the campaign or raise the cap ` +
160
+ `explicitly; an uncapped nightly is an unbounded spend commitment`);
161
+ }
162
+ }
163
+ /** Default sweep cadence: budgets are minutes-scale; 15 s keeps overshoot ≪ any ceiling. */
164
+ export const DEFAULT_SUPERVISION_SWEEP_INTERVAL_MS = 15_000;
165
+ export class CampaignSupervisor {
166
+ deps;
167
+ campaigns = new Map();
168
+ now;
169
+ log;
170
+ handle = null;
171
+ sweeping = false;
172
+ constructor(deps) {
173
+ this.deps = deps;
174
+ this.now = deps.now ?? Date.now;
175
+ this.log = deps.log ?? (() => undefined);
176
+ }
177
+ /**
178
+ * Begin supervising a campaign under a policy. Call at launch — the wall clock starts
179
+ * HERE, not at the first sweep. Re-registering an id is a bug (two clocks, one spend).
180
+ */
181
+ supervise(campaignId, policy) {
182
+ if (this.campaigns.has(campaignId)) {
183
+ throw new Error(`campaign ${campaignId} is already supervised — refusing a second clock`);
184
+ }
185
+ this.campaigns.set(campaignId, {
186
+ policy,
187
+ startedAt: this.now(),
188
+ elapsedMs: 0,
189
+ released: false,
190
+ nodes: new Map(),
191
+ });
192
+ }
193
+ /**
194
+ * Stop ENFORCING (campaign reached a terminal status). State stays readable — deliberately
195
+ * no delete, or a post-terminal report would be irrecoverable. Also reached automatically
196
+ * when a sweep observes every node terminal.
197
+ */
198
+ release(campaignId) {
199
+ const c = this.campaigns.get(campaignId);
200
+ if (c !== undefined)
201
+ c.released = true;
202
+ }
203
+ /** Record pipeline-reported cost for a node (tokens/USD — wall-clock is supervisor-owned). */
204
+ recordNodeCost(campaignId, nodeId, cost) {
205
+ const c = this.campaigns.get(campaignId);
206
+ if (c === undefined)
207
+ return; // unknown campaign: cost with no supervision to attribute it to
208
+ const node = this.trackNode(c, nodeId);
209
+ if (cost.usd !== undefined)
210
+ node.cost.usd = (node.cost.usd ?? 0) + cost.usd;
211
+ if (cost.inputTokens !== undefined) {
212
+ node.cost.inputTokens = (node.cost.inputTokens ?? 0) + cost.inputTokens;
213
+ }
214
+ if (cost.outputTokens !== undefined) {
215
+ node.cost.outputTokens = (node.cost.outputTokens ?? 0) + cost.outputTokens;
216
+ }
217
+ }
218
+ /** The supervision state for one campaign, or undefined when it was never supervised. */
219
+ state(campaignId) {
220
+ const c = this.campaigns.get(campaignId);
221
+ if (c === undefined)
222
+ return undefined;
223
+ const totalCostUsd = this.totalCostUsd(c);
224
+ const state = {
225
+ campaignId,
226
+ policy: c.policy,
227
+ startedAt: c.startedAt,
228
+ elapsedMs: c.elapsedMs,
229
+ // Explicit copy: the internal `runningSince` bookkeeping never leaves the supervisor.
230
+ nodes: [...c.nodes.values()].map((n) => {
231
+ const row = { nodeId: n.nodeId, phase: n.phase, cost: { ...n.cost } };
232
+ if (n.runId !== undefined)
233
+ row.runId = n.runId;
234
+ if (n.startedAt !== undefined)
235
+ row.startedAt = n.startedAt;
236
+ if (n.exclusion !== undefined)
237
+ row.exclusion = { ...n.exclusion };
238
+ return row;
239
+ }),
240
+ };
241
+ if (totalCostUsd !== undefined)
242
+ state.totalCostUsd = totalCostUsd;
243
+ if (c.exhausted !== undefined)
244
+ state.exhausted = { ...c.exhausted };
245
+ return state;
246
+ }
247
+ /** One enforcement pass over every supervised campaign. Tests drive it directly. */
248
+ async sweep() {
249
+ if (this.sweeping)
250
+ return; // a slow scheduler read must never stack sweeps
251
+ this.sweeping = true;
252
+ try {
253
+ for (const [campaignId, c] of this.campaigns) {
254
+ try {
255
+ await this.sweepCampaign(campaignId, c);
256
+ }
257
+ catch (err) {
258
+ this.log(`[campaign-supervision] sweep of ${campaignId} failed: ${String(err)}`);
259
+ }
260
+ }
261
+ }
262
+ finally {
263
+ this.sweeping = false;
264
+ }
265
+ }
266
+ /** Arm the periodic sweep. Unref'd — the daemon's server lifecycle owns shutdown. */
267
+ start(intervalMs = DEFAULT_SUPERVISION_SWEEP_INTERVAL_MS) {
268
+ if (this.handle !== null)
269
+ return;
270
+ this.handle = setInterval(() => {
271
+ this.sweep().catch((err) => {
272
+ this.log(`[campaign-supervision] sweep failed: ${String(err)}`);
273
+ });
274
+ }, intervalMs);
275
+ this.handle.unref?.();
276
+ }
277
+ stop() {
278
+ if (this.handle !== null)
279
+ clearInterval(this.handle);
280
+ this.handle = null;
281
+ }
282
+ // -------------------------------------------------------------------------
283
+ async sweepCampaign(campaignId, c) {
284
+ if (c.released)
285
+ return; // over: nothing left to enforce, state stays as it ended
286
+ const nodes = await this.deps.scheduler.listNodes(campaignId);
287
+ const now = this.now();
288
+ c.elapsedMs = now - c.startedAt;
289
+ // Reconcile observed phases + accumulate per-node RUNNING wall clocks. `cost.wallMs`
290
+ // counts running intervals only — an awaiting-human interlude neither bills the node
291
+ // nor counts against its timeout (a gate is quiet by design; sweep granularity bounds
292
+ // the error at one interval).
293
+ for (const view of nodes) {
294
+ const node = this.trackNode(c, view.nodeId);
295
+ if (view.runId !== undefined)
296
+ node.runId = view.runId;
297
+ if (view.phase === 'running') {
298
+ if (node.runningSince === undefined) {
299
+ node.runningSince = now; // (re-)entered running
300
+ node.startedAt ??= now;
301
+ }
302
+ else {
303
+ node.cost.wallMs += now - node.runningSince; // extend the open interval
304
+ node.runningSince = now;
305
+ }
306
+ }
307
+ else if (node.runningSince !== undefined) {
308
+ // Left `running` since the last sweep — close the interval at this observation.
309
+ node.cost.wallMs += now - node.runningSince;
310
+ node.runningSince = undefined;
311
+ }
312
+ node.phase = view.phase;
313
+ }
314
+ // A campaign whose every node is terminal is OVER — a later sweep must never trip its
315
+ // budget on wall time nobody is spending.
316
+ if (nodes.length > 0 && nodes.every((n) => n.phase === 'terminal')) {
317
+ c.released = true;
318
+ return;
319
+ }
320
+ if (c.exhausted !== undefined)
321
+ return; // exhaustion is enforced exactly once
322
+ // 1) Per-node timeout (accumulated RUNNING time only; awaiting-human never counts).
323
+ for (const node of c.nodes.values()) {
324
+ if (node.phase !== 'running' || node.exclusion !== undefined)
325
+ continue;
326
+ if (node.cost.wallMs <= c.policy.nodeTimeoutMs)
327
+ continue;
328
+ const reason = `node-timeout: ran ${Math.round(node.cost.wallMs / 1000)}s, per-node timeout is ` +
329
+ `${Math.round(c.policy.nodeTimeoutMs / 1000)}s (${ENV_NODE_TIMEOUT_SECS})`;
330
+ await this.excludeOne(campaignId, c, node, 'node-timeout', reason, 'cancelled');
331
+ }
332
+ // 2) Campaign-level ceilings — wall-clock first (always armed), then cost (opt-in).
333
+ if (c.elapsedMs > c.policy.wallClockBudgetMs) {
334
+ await this.exhaust(campaignId, c, {
335
+ kind: 'wall-clock',
336
+ at: new Date(now).toISOString(),
337
+ spent: c.elapsedMs,
338
+ budget: c.policy.wallClockBudgetMs,
339
+ policyApplied: c.policy.killPolicy,
340
+ });
341
+ return;
342
+ }
343
+ const totalCostUsd = this.totalCostUsd(c);
344
+ if (c.policy.maxCostUsd !== undefined &&
345
+ totalCostUsd !== undefined &&
346
+ totalCostUsd > c.policy.maxCostUsd) {
347
+ await this.exhaust(campaignId, c, {
348
+ kind: 'cost',
349
+ at: new Date(now).toISOString(),
350
+ spent: totalCostUsd,
351
+ budget: c.policy.maxCostUsd,
352
+ policyApplied: c.policy.killPolicy,
353
+ });
354
+ }
355
+ }
356
+ /**
357
+ * Abort everything the budget no longer covers. Every touched node gets an
358
+ * excluded-with-reason record; one campaignBudgetExceeded frame + one
359
+ * campaignNodeExcluded frame per node ride the /ws fan-out; the warn log names them all.
360
+ */
361
+ async exhaust(campaignId, c, exhaustion) {
362
+ c.exhausted = exhaustion;
363
+ const kind = exhaustion.kind === 'wall-clock' ? 'campaign-budget' : 'cost-budget';
364
+ const ceiling = exhaustion.kind === 'wall-clock'
365
+ ? `wall-clock budget ${Math.round(exhaustion.budget / 1000)}s exceeded ` +
366
+ `(elapsed ${Math.round(exhaustion.spent / 1000)}s)`
367
+ : `cost budget $${exhaustion.budget} exceeded (spent $${exhaustion.spent.toFixed(2)})`;
368
+ const touched = [];
369
+ for (const node of c.nodes.values()) {
370
+ if (node.phase === 'terminal' || node.exclusion !== undefined)
371
+ continue;
372
+ if (node.phase === 'pending') {
373
+ await this.excludeOne(campaignId, c, node, kind, `${ceiling}; node never launched`, 'excluded');
374
+ touched.push(node.nodeId);
375
+ }
376
+ else if (c.policy.killPolicy === 'kill-running') {
377
+ await this.excludeOne(campaignId, c, node, kind, `${ceiling}; in-flight node killed`, 'cancelled');
378
+ touched.push(node.nodeId);
379
+ }
380
+ else {
381
+ // abandon-running: the node may finish, but it is on record as budget-breached and
382
+ // nothing new launches after it. Recorded + broadcast — never silent.
383
+ await this.excludeOne(campaignId, c, node, kind, `${ceiling}; in-flight node abandoned to finish (abandon-running)`, 'abandoned');
384
+ touched.push(node.nodeId);
385
+ }
386
+ }
387
+ this.deps.broadcast({
388
+ type: 'campaignBudgetExceeded',
389
+ campaign: campaignId,
390
+ kind: exhaustion.kind,
391
+ budget: exhaustion.budget,
392
+ spent: exhaustion.spent,
393
+ policyApplied: exhaustion.policyApplied,
394
+ nodes: touched,
395
+ });
396
+ this.log(`[campaign-supervision] campaign ${campaignId}: ${ceiling} — policy ` +
397
+ `${exhaustion.policyApplied} applied to ${touched.length} node(s): ` +
398
+ `${touched.join(', ') || '(none left to touch)'}`);
399
+ }
400
+ async excludeOne(campaignId, c, node, kind, reason, applied) {
401
+ node.exclusion = { kind, reason, at: new Date(this.now()).toISOString(), applied };
402
+ if (applied === 'cancelled') {
403
+ await this.deps.scheduler.cancelNode(campaignId, node.nodeId, node.runId, reason);
404
+ }
405
+ else if (applied === 'excluded') {
406
+ await this.deps.scheduler.excludeNode(campaignId, node.nodeId, reason);
407
+ }
408
+ this.deps.broadcast({
409
+ type: 'campaignNodeExcluded',
410
+ campaign: campaignId,
411
+ node: node.nodeId,
412
+ kind,
413
+ reason,
414
+ applied,
415
+ });
416
+ this.log(`[campaign-supervision] campaign ${campaignId} node ${node.nodeId}: ${applied} — ${reason}`);
417
+ }
418
+ trackNode(c, nodeId) {
419
+ let node = c.nodes.get(nodeId);
420
+ if (node === undefined) {
421
+ node = { nodeId, phase: 'pending', cost: { wallMs: 0 } };
422
+ c.nodes.set(nodeId, node);
423
+ }
424
+ return node;
425
+ }
426
+ totalCostUsd(c) {
427
+ let total;
428
+ for (const node of c.nodes.values()) {
429
+ if (node.cost.usd !== undefined)
430
+ total = (total ?? 0) + node.cost.usd;
431
+ }
432
+ return total;
433
+ }
434
+ }
435
+ // ---------------------------------------------------------------------------
436
+ // Evidence: the campaign-supervision report artifact
437
+ // ---------------------------------------------------------------------------
438
+ /** Filename of the supervision evidence artifact inside a campaign's evidence dir. */
439
+ export const SUPERVISION_REPORT_FILENAME = 'campaign-supervision.json';
440
+ /** Build the evidence report from live supervision state. */
441
+ export function buildSupervisionReport(state) {
442
+ let wallMs = 0;
443
+ let inputTokens;
444
+ let outputTokens;
445
+ for (const n of state.nodes) {
446
+ wallMs += n.cost.wallMs;
447
+ if (n.cost.inputTokens !== undefined)
448
+ inputTokens = (inputTokens ?? 0) + n.cost.inputTokens;
449
+ if (n.cost.outputTokens !== undefined)
450
+ outputTokens = (outputTokens ?? 0) + n.cost.outputTokens;
451
+ }
452
+ const report = {
453
+ report: 'campaign-supervision',
454
+ version: 1,
455
+ campaign_id: state.campaignId,
456
+ generated_at: new Date().toISOString(),
457
+ policy: {
458
+ wall_clock_budget_secs: Math.round(state.policy.wallClockBudgetMs / 1000),
459
+ node_timeout_secs: Math.round(state.policy.nodeTimeoutMs / 1000),
460
+ unit_timeout_pin_secs: state.policy.unitTimeoutSecs,
461
+ max_nodes: state.policy.maxNodes,
462
+ ...(state.policy.maxCostUsd !== undefined ? { max_cost_usd: state.policy.maxCostUsd } : {}),
463
+ kill_policy: state.policy.killPolicy,
464
+ },
465
+ started_at: new Date(state.startedAt).toISOString(),
466
+ elapsed_ms: state.elapsedMs,
467
+ budget: state.exhausted !== undefined
468
+ ? {
469
+ exhausted: true,
470
+ kind: state.exhausted.kind,
471
+ at: state.exhausted.at,
472
+ policy_applied: state.exhausted.policyApplied,
473
+ }
474
+ : { exhausted: false },
475
+ totals: {
476
+ wall_ms: wallMs,
477
+ ...(state.totalCostUsd !== undefined ? { cost_usd: state.totalCostUsd } : {}),
478
+ ...(inputTokens !== undefined ? { input_tokens: inputTokens } : {}),
479
+ ...(outputTokens !== undefined ? { output_tokens: outputTokens } : {}),
480
+ },
481
+ nodes: state.nodes.map((n) => ({
482
+ node_id: n.nodeId,
483
+ phase: n.phase,
484
+ ...(n.runId !== undefined ? { run_id: n.runId } : {}),
485
+ cost: {
486
+ wall_ms: n.cost.wallMs,
487
+ ...(n.cost.usd !== undefined ? { usd: n.cost.usd } : {}),
488
+ ...(n.cost.inputTokens !== undefined ? { input_tokens: n.cost.inputTokens } : {}),
489
+ ...(n.cost.outputTokens !== undefined ? { output_tokens: n.cost.outputTokens } : {}),
490
+ },
491
+ ...(n.exclusion !== undefined
492
+ ? {
493
+ excluded: {
494
+ kind: n.exclusion.kind,
495
+ reason: n.exclusion.reason,
496
+ at: n.exclusion.at,
497
+ applied: n.exclusion.applied,
498
+ },
499
+ }
500
+ : {}),
501
+ })),
502
+ };
503
+ return report;
504
+ }
505
+ /**
506
+ * Write the report into a campaign's evidence directory (caller resolves the dir — the
507
+ * QE pipeline owns evidence placement; crew never writes ledger DOMAIN records, and this
508
+ * is an artifact file, not a runs/verdicts row). Returns the absolute path written.
509
+ */
510
+ export async function writeSupervisionReport(evidenceDir, report) {
511
+ const path = join(evidenceDir, SUPERVISION_REPORT_FILENAME);
512
+ await writeFile(path, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
513
+ return path;
514
+ }
515
+ //# sourceMappingURL=supervision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervision.js","sourceRoot":"","sources":["../../src/campaign/supervision.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Scenario → CampaignNode mapping (TH-9; DES-CAMPAIGN-001 §8).
3
+ *
4
+ * `POST /campaigns` accepts a batch of SCENARIOS (the qe-campaign vocabulary: garden's
5
+ * campaign-recon rungs, or any caller's batch of checks) and this module maps them onto the
6
+ * engine's `CampaignDef` — nodes, `on_success`/`on_terminal` edges, and one composed
7
+ * single-phase workflow per deterministic scenario. Pure data-in/data-out: no engine handle,
8
+ * no I/O, so every rule below is unit-testable without spawning a Core.
9
+ *
10
+ * ## The two scenario shapes, and where the work actually lives
11
+ *
12
+ * - **`tool` (deterministic)** — the scenario is a spec FILE (garden's runner spec, a script,
13
+ * any argv-runnable check) and the node's workflow is one Tool-executor phase running that
14
+ * argv verbatim: no council, no LLM, near-zero cost. The engine's Tool path (`run_tool_cmd`)
15
+ * spawns the command directly.
16
+ * - **`agent` (exploratory / authoring)** — a governed agent run over a problem statement;
17
+ * seats are spent only where judgment is the work (test-R9).
18
+ *
19
+ * ## The 1022-byte rule (load-bearing, enforced here)
20
+ *
21
+ * PTY worker prompts longer than 1022 bytes are SILENTLY DISCARDED by the canonical line
22
+ * discipline — the worker waits on input that never arrives and the unit burns its full
23
+ * timeout (the S8 campaign finding; memory: "PTY canonical line limit"). The mapping
24
+ * therefore refuses, loudly and per-token, anything that smells like a scenario BODY inlined
25
+ * where a FILE PATH belongs: every Tool argv token and every agent problem statement must be
26
+ * a single line of at most {@link MAX_INLINE_BYTES} bytes. The fix is always the same and the
27
+ * error says it: persist the content as a file and pass its path.
28
+ */
29
+ import type { CampaignDef, LaunchCampaignBody, WorkflowDef } from '../core/types.js';
30
+ /**
31
+ * Byte ceiling for a single inline string handed to a node (a Tool argv token, an agent
32
+ * problem). 1022 is the PTY canonical-line limit — the longest line a worker's stdin
33
+ * accepts before the kernel silently discards it. Tool argv does not itself traverse a PTY,
34
+ * but the ceiling is enforced uniformly because the failure it guards against is the same
35
+ * SHAPE either way: scenario bodies belong in files, and a token this long is a body.
36
+ */
37
+ export declare const MAX_INLINE_BYTES = 1022;
38
+ /** Composed node workflows carry this prefix — the adapter keeps them OUT of the workflow
39
+ * catalog (`listWorkflows`) while still persisting them to the overlay dir so a fresh-process
40
+ * `resumeCampaign` can re-dispatch pending nodes by workflow id. */
41
+ export declare const CAMPAIGN_WORKFLOW_PREFIX = "campaign-";
42
+ /** The composed workflow id for one deterministic scenario node. */
43
+ export declare function scenarioWorkflowId(campaignId: string, scenarioId: string): string;
44
+ /** A built campaign: the engine def plus the per-node Tool workflows the adapter must arm
45
+ * (hot-register + overlay-persist) BEFORE the def launches. */
46
+ export interface BuiltCampaign {
47
+ def: CampaignDef;
48
+ workflows: WorkflowDef[];
49
+ }
50
+ /**
51
+ * Map a `POST /campaigns` body onto the engine's `CampaignDef` + the per-node workflows to arm.
52
+ *
53
+ * Throws a plain `Error` (the route answers 400 with the message) on: a malformed campaign or
54
+ * scenario id, a duplicate scenario id, a scenario with neither/both of `tool`/`agent`, an
55
+ * empty argv, an inline-rule break ({@link assertInline}), an unknown/self/duplicate dep, or an
56
+ * `agent.workflow` that is itself a composed campaign workflow. Cycles are LEFT to the engine's
57
+ * own launch validation — one authority for graph shape, and its reject arrives before anything
58
+ * persists.
59
+ *
60
+ * `defaultClis` is the parsed council roster used for every node that does not carry its own —
61
+ * Tool nodes never convene it (the phase bypasses the council), agent nodes do.
62
+ */
63
+ export declare function buildCampaign(body: LaunchCampaignBody, defaultClis: unknown[]): BuiltCampaign;
64
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/campaigns/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EACV,WAAW,EAIX,kBAAkB,EAElB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAE1B;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAC;AASrC;;qEAEqE;AACrE,eAAO,MAAM,wBAAwB,cAAc,CAAC;AAEpD,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;gEACgE;AAChE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,WAAW,CAAC;IACjB,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B;AAiDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,aAAa,CAwD7F"}