@tangle-network/agent-eval 0.150.1 → 0.150.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/analyst/index.d.ts +3 -3
  3. package/dist/analyst/index.js +3 -3
  4. package/dist/{benchmark-command-CAFwbH0L.js → benchmark-command-BU1Las59.js} +6 -6
  5. package/dist/{benchmark-command-CAFwbH0L.js.map → benchmark-command-BU1Las59.js.map} +1 -1
  6. package/dist/benchmarks/index.js +3 -3
  7. package/dist/builder-eval/index.js +1 -1
  8. package/dist/campaign/index.js +5 -5
  9. package/dist/{campaign-CN_7xJdV.js → campaign-la-gEYNz.js} +7 -7
  10. package/dist/{campaign-CN_7xJdV.js.map → campaign-la-gEYNz.js.map} +1 -1
  11. package/dist/{chat-client-2bVfrzhN.js → chat-client-Bvmxedyv.js} +2 -2
  12. package/dist/{chat-client-2bVfrzhN.js.map → chat-client-Bvmxedyv.js.map} +1 -1
  13. package/dist/cli.js +1 -1
  14. package/dist/contract/index.js +5 -5
  15. package/dist/{define-agent-eval-rqNyVhVV.js → define-agent-eval-C8V8sMqP.js} +7 -7
  16. package/dist/{define-agent-eval-rqNyVhVV.js.map → define-agent-eval-C8V8sMqP.js.map} +1 -1
  17. package/dist/{descriptive-B5MwKfbf.js → descriptive-jDOuI6mz.js} +22 -2
  18. package/dist/descriptive-jDOuI6mz.js.map +1 -0
  19. package/dist/{dspy-rlm-engine-DHI0WrUU.js → dspy-rlm-engine-CBYlPvNy.js} +2 -2
  20. package/dist/{dspy-rlm-engine-DHI0WrUU.js.map → dspy-rlm-engine-CBYlPvNy.js.map} +1 -1
  21. package/dist/{eval-campaign-C4jmuM-b.js → eval-campaign-CQuZrLR_.js} +2 -2
  22. package/dist/{eval-campaign-C4jmuM-b.js.map → eval-campaign-CQuZrLR_.js.map} +1 -1
  23. package/dist/{external-optimizer-process-Bhmzngf-.js → external-optimizer-process-x9oEXKsU.js} +2 -2
  24. package/dist/{external-optimizer-process-Bhmzngf-.js.map → external-optimizer-process-x9oEXKsU.js.map} +1 -1
  25. package/dist/{external-optimizer-subprocess-Dn90UqN2.js → external-optimizer-subprocess-CKNb42oM.js} +7 -3
  26. package/dist/external-optimizer-subprocess-CKNb42oM.js.map +1 -0
  27. package/dist/{index-CTKpu9ry.d.ts → index-Aj3WO3_a.d.ts} +2 -2
  28. package/dist/{index-CTKpu9ry.d.ts.map → index-Aj3WO3_a.d.ts.map} +1 -1
  29. package/dist/index.d.ts +3 -72
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +11 -11
  32. package/dist/{integrity-DysDBWDu.js → integrity-DL91tucI.js} +16 -3
  33. package/dist/integrity-DL91tucI.js.map +1 -0
  34. package/dist/{judge-calibration-DZkWrm5H.js → judge-calibration-zZjLz8hr.js} +2 -2
  35. package/dist/{judge-calibration-DZkWrm5H.js.map → judge-calibration-zZjLz8hr.js.map} +1 -1
  36. package/dist/{llm-judge-CVq33oz1.js → llm-judge-BqqMS8t7.js} +4 -4
  37. package/dist/{llm-judge-CVq33oz1.js.map → llm-judge-BqqMS8t7.js.map} +1 -1
  38. package/dist/meta-eval/index.js +2 -2
  39. package/dist/openapi.json +1 -1
  40. package/dist/pipelines/index.js +1 -1
  41. package/dist/{produced-state-jfk8Du3b.js → produced-state-Bnq4FaDO.js} +2 -2
  42. package/dist/{produced-state-jfk8Du3b.js.map → produced-state-Bnq4FaDO.js.map} +1 -1
  43. package/dist/reporting.js +2 -2
  44. package/dist/{reward-hacking-DKI9T52l.js → reward-hacking-C0x0xihA.js} +2 -2
  45. package/dist/{reward-hacking-DKI9T52l.js.map → reward-hacking-C0x0xihA.js.map} +1 -1
  46. package/dist/rl.js +3 -3
  47. package/dist/{rubric-predictive-validity-Cwwyd7ah.js → rubric-predictive-validity-CzxLoZge.js} +2 -2
  48. package/dist/{rubric-predictive-validity-Cwwyd7ah.js.map → rubric-predictive-validity-CzxLoZge.js.map} +1 -1
  49. package/dist/{skillopt-optimization-method-DLeUcK-K.js → skillopt-optimization-method-CPBlTcj5.js} +4 -4
  50. package/dist/{skillopt-optimization-method-DLeUcK-K.js.map → skillopt-optimization-method-CPBlTcj5.js.map} +1 -1
  51. package/dist/{summary-report-Blysd6Z2.js → summary-report-DW2bEpdB.js} +2 -2
  52. package/dist/{summary-report-Blysd6Z2.js.map → summary-report-DW2bEpdB.js.map} +1 -1
  53. package/dist/supervisor-run/index.d.ts +26 -4
  54. package/dist/supervisor-run/index.d.ts.map +1 -1
  55. package/dist/supervisor-run/index.js +102 -24
  56. package/dist/supervisor-run/index.js.map +1 -1
  57. package/dist/{tool-waste-BDdBZG1F.js → tool-waste-C-VHSRwF.js} +3 -3
  58. package/dist/{tool-waste-BDdBZG1F.js.map → tool-waste-C-VHSRwF.js.map} +1 -1
  59. package/dist/{types-yLK8gXE9.d.ts → types-I5WwVzQ7.d.ts} +159 -9
  60. package/dist/types-I5WwVzQ7.d.ts.map +1 -0
  61. package/docs/adapters-observability.md +9 -23
  62. package/docs/campaign-proposers.md +13 -5
  63. package/docs/concepts.md +3 -4
  64. package/docs/wire-protocol.md +1 -1
  65. package/package.json +1 -1
  66. package/dist/descriptive-B5MwKfbf.js.map +0 -1
  67. package/dist/external-optimizer-subprocess-Dn90UqN2.js.map +0 -1
  68. package/dist/integrity-DysDBWDu.js.map +0 -1
  69. package/dist/types-yLK8gXE9.d.ts.map +0 -1
@@ -1,5 +1,6 @@
1
+ import { o as summarizeNumberSeries } from "../descriptive-jDOuI6mz.js";
1
2
  import { d as transcriptFromEntries, i as openOpencodeDb, l as parseClaudeEntries, r as findOpencodeSessionsByDirectory, t as DEFAULT_OPENCODE_DB } from "../opencode-sqlite-DJWAXLms.js";
2
- import { a as parseJsonl, c as SUPERVISOR_RUN_INTEGRITY_SCHEMA, i as parseJson, n as supervisorRunRolloutLines, o as parseSupervisorTree, r as asRecord, s as workerSourceKey, t as analyzeSupervisorRunIntegrity } from "../integrity-DysDBWDu.js";
3
+ import { a as parseJsonl, c as SUPERVISOR_RUN_INTEGRITY_SCHEMA, i as parseJson, n as supervisorRunRolloutLines, o as parseSupervisorTree, r as asRecord, s as workerSourceKey, t as analyzeSupervisorRunIntegrity } from "../integrity-DL91tucI.js";
3
4
  import { basename, dirname, join } from "node:path";
4
5
  import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
5
6
  //#region src/supervisor-run/types.ts
@@ -44,7 +45,7 @@ function analyzeSupervisorRunSources(src, now = Date.now) {
44
45
  gaps.push(`${what}: ${reason}`);
45
46
  return unavailable(reason);
46
47
  };
47
- const journalMissing = src.supRunDir === null ? "no supervisor run dir under <ws>/.agent/supervisor (or legacy <ws>/.loops/supervisor)" : "journal.jsonl absent";
48
+ const journalMissing = src.journalMissingReason ?? (src.supRunDir === null ? "no supervisor run dir under <ws>/.agent/supervisor (or legacy <ws>/.loops/supervisor)" : "journal.jsonl absent");
48
49
  const haveJournal = src.journal !== null;
49
50
  const tree = parseSupervisorTree(src);
50
51
  const state = tree.state;
@@ -68,7 +69,22 @@ function analyzeSupervisorRunSources(src, now = Date.now) {
68
69
  const matches = spawnsForSource(worker);
69
70
  return matches.length === 1 ? matches[0] ?? null : null;
70
71
  };
71
- const supervisorWallMs = startedAt !== null && completedAt !== null && completedAt >= startedAt ? completedAt - startedAt : !haveJournal ? gap("supervisorWallMs", journalMissing) : gap("supervisorWallMs", "no parseable start/complete timestamps in state.json or journal");
72
+ const wallSpanStart = startedAt ?? tree.firstEventAt;
73
+ const wallSpanEnd = tree.lastEventAt;
74
+ let supervisorWallMs;
75
+ let supervisorWallSource;
76
+ if (startedAt !== null && completedAt !== null && completedAt >= startedAt) {
77
+ supervisorWallMs = completedAt - startedAt;
78
+ supervisorWallSource = "stamps";
79
+ } else if (completedAt === null && wallSpanStart !== null && wallSpanEnd !== null && wallSpanEnd >= wallSpanStart) {
80
+ supervisorWallMs = wallSpanEnd - wallSpanStart;
81
+ supervisorWallSource = "journal-span";
82
+ } else {
83
+ const reason = !haveJournal ? journalMissing : "no parseable start/complete timestamps in state.json or journal";
84
+ supervisorWallMs = gap("supervisorWallMs", reason);
85
+ supervisorWallSource = unavailable(reason);
86
+ }
87
+ const wallEndAt = completedAt ?? (supervisorWallSource === "journal-span" ? wallSpanEnd : null);
72
88
  const steerRows = [];
73
89
  let steerQueuedTotal = 0;
74
90
  let steerDeliveredTotal = 0;
@@ -135,8 +151,8 @@ function analyzeSupervisorRunSources(src, now = Date.now) {
135
151
  if (live > maxConcurrency) maxConcurrency = live;
136
152
  prev = step.at;
137
153
  }
138
- if (prev !== null && completedAt !== null && completedAt >= prev) {
139
- const span = completedAt - prev;
154
+ if (prev !== null && wallEndAt !== null && wallEndAt >= prev) {
155
+ const span = wallEndAt - prev;
140
156
  if (live === 0) idleMs += span;
141
157
  sumWorkerWallMs += span * live;
142
158
  }
@@ -203,6 +219,7 @@ function analyzeSupervisorRunSources(src, now = Date.now) {
203
219
  delegationDepth: haveJournal ? delegationDepth : unavailable(journalMissing),
204
220
  timeToFirstSpawnMs: startedAt !== null && firstWorkerSpawnAt !== null ? firstWorkerSpawnAt - startedAt : haveJournal ? unavailable("no worker spawn timestamps") : unavailable(journalMissing),
205
221
  supervisorWallMs,
222
+ supervisorWallSource,
206
223
  idleMs: isUnavailable(supervisorWallMs) ? unavailable(supervisorWallMs.unavailable) : idleMs,
207
224
  idlePct: isUnavailable(supervisorWallMs) || supervisorWallMs === 0 ? isUnavailable(supervisorWallMs) ? unavailable(supervisorWallMs.unavailable) : unavailable("supervisor wall is 0ms") : round(idleMs / supervisorWallMs * 100, 1),
208
225
  workerUtilization: isUnavailable(supervisorWallMs) || supervisorWallMs === 0 ? isUnavailable(supervisorWallMs) ? unavailable(supervisorWallMs.unavailable) : unavailable("supervisor wall is 0ms") : round(sumWorkerWallMs / supervisorWallMs, 3)
@@ -267,8 +284,24 @@ function analyzeSupervisorRunSources(src, now = Date.now) {
267
284
  const workerOut = workerTokenLimit !== null ? unavailable(workerTokenLimit) : sq !== null ? journalWorkerOut + sq.output : haveJournal ? journalWorkerOut : unavailable(harnessGapReason);
268
285
  const stateResult = asRecord(state?.result);
269
286
  const stateUsd = typeof stateResult.spentUsd === "number" ? stateResult.spentUsd : null;
287
+ const resultSpentTotal = asRecord(result?.spentTotal);
288
+ const resultCloseUsd = typeof resultSpentTotal.usd === "number" && Number.isFinite(resultSpentTotal.usd) ? resultSpentTotal.usd : null;
289
+ const closeUsd = stateUsd ?? resultCloseUsd;
270
290
  const usdLimit = src.limits.spendUsd;
271
291
  const totalUsd = usdLimit !== null ? gap("totalUsd", usdLimit) : stateUsd !== null ? round(stateUsd, 6) : haveJournal ? round(tree.brain.usd + journalWorkerUsd, 6) : gap("totalUsd", journalMissing);
292
+ const journalSpendRecords = tree.brain.meteredCount + rootChildCloses.filter((close) => close.hasSpend).length;
293
+ const journalDerivedAvailable = usdLimit === null && haveJournal;
294
+ const closeRecordAvailable = usdLimit === null && closeUsd !== null;
295
+ const spend = {
296
+ journalDerived: {
297
+ usd: journalDerivedAvailable ? round(tree.brain.usd + journalWorkerUsd, 6) : unavailable(usdLimit ?? journalMissing),
298
+ records: journalDerivedAvailable ? journalSpendRecords : 0
299
+ },
300
+ closeRecord: {
301
+ usd: closeRecordAvailable ? round(closeUsd, 6) : unavailable(usdLimit ?? "no close record: neither state.json result.spentUsd nor result.json spentTotal.usd is present"),
302
+ records: closeRecordAvailable ? 1 : 0
303
+ }
304
+ };
272
305
  const perWorker = (src.workers ?? []).map((w) => {
273
306
  const f = tree.workerLogs.get(workerSourceKey(w));
274
307
  const matchingSpawns = spawnsForSource(w);
@@ -297,7 +330,7 @@ function analyzeSupervisorRunSources(src, now = Date.now) {
297
330
  score: close?.score ?? f?.score ?? null
298
331
  };
299
332
  });
300
- const walls = perWorker.map((w) => w.wallMs).filter((w) => w !== null).sort((a, b) => a - b);
333
+ const wallDistribution = summarizeNumberSeries(perWorker.map((w) => w.wallMs).filter((w) => w !== null));
301
334
  const brainCalls = parseJsonl(src.brainLog);
302
335
  const managerTokenLimit = src.limits.managerTokens;
303
336
  const economics = {
@@ -318,17 +351,11 @@ function analyzeSupervisorRunSources(src, now = Date.now) {
318
351
  usd: usdLimit !== null ? unavailable(usdLimit) : haveJournal ? round(journalWorkerUsd, 6) : unavailable(journalMissing),
319
352
  source: workerTokenLimit !== null ? workerTokenLimit : sq !== null ? `journal settled spend + ${sq.store} sessions (n=${sq.sessions})` : `journal settled spend only — ${src.harnessMissingReason ?? "harness session store unavailable"}`
320
353
  },
354
+ spend,
321
355
  totalUsd,
322
356
  totalUsdSource: usdLimit !== null ? usdLimit : stateUsd !== null ? `state.json result.spentUsd${rootChildCloses.length > 0 && journalWorkerUsd === 0 ? " — brain-priced only; worker CLI inference is unpriced (see worker token counts)" : ""}` : haveJournal ? "journal metered + settled usd" : journalMissing,
323
357
  costPerAcceptedPatchUsd: isUnavailable(totalUsd) ? unavailable(totalUsd.unavailable) : isUnavailable(decision.accepted) ? unavailable(decision.accepted.unavailable) : decision.accepted === 0 ? unavailable("no accepted worker patch (cost has no denominator)") : round(totalUsd / decision.accepted, 6),
324
- workerWallMsDistribution: walls.length === 0 ? unavailable(src.workers === null ? workersGapReason : "no worker start/finish pairs captured") : {
325
- n: walls.length,
326
- min: walls[0],
327
- p50: quantile(walls, .5),
328
- p90: quantile(walls, .9),
329
- max: walls[walls.length - 1],
330
- sum: walls.reduce((a, b) => a + b, 0)
331
- },
358
+ workerWallMsDistribution: wallDistribution === null ? unavailable(src.workers === null ? workersGapReason : "no worker start/finish pairs captured") : wallDistribution,
332
359
  perWorker: src.workers === null ? unavailable(workersGapReason) : perWorker
333
360
  };
334
361
  const patchStats = src.patch === null ? gap("patch", src.limits.deliverables ?? "delivered patch file absent") : parsePatch(src.patch);
@@ -386,10 +413,6 @@ function round(v, digits) {
386
413
  const f = 10 ** digits;
387
414
  return Math.round(v * f) / f;
388
415
  }
389
- function quantile(sorted, q) {
390
- if (sorted.length === 0) return 0;
391
- return sorted[Math.min(sorted.length - 1, Math.max(0, Math.ceil(q * sorted.length) - 1))];
392
- }
393
416
  /** Unified-diff stats. Counts `+++ b/<path>` targets, body +/- lines, and test-file touches. */
394
417
  function parsePatch(text) {
395
418
  const files = /* @__PURE__ */ new Set();
@@ -435,6 +458,8 @@ function rollupSupervisorRuns(reports) {
435
458
  const spawnVals = known(reports.map((r) => r.orchestration.workersSpawned));
436
459
  const acceptVals = known(reports.map((r) => r.decision.accepted));
437
460
  const usdVals = known(reports.map((r) => r.economics.totalUsd));
461
+ const journalSpendVals = known(reports.map((r) => r.economics.spend.journalDerived.usd));
462
+ const closeSpendVals = known(reports.map((r) => r.economics.spend.closeRecord.usd));
438
463
  const resolvedVals = known(reports.map((r) => r.outcome.judgeResolved));
439
464
  const sum = (xs) => xs.reduce((a, b) => a + b, 0);
440
465
  const mean = (xs) => xs.length === 0 ? unavailable("no cell reported this metric") : round(sum(xs) / xs.length, 3);
@@ -461,6 +486,16 @@ function rollupSupervisorRuns(reports) {
461
486
  workersSpawnedTotal: spawnVals.length === 0 ? unavailable("no cell reported spawns") : sum(spawnVals),
462
487
  acceptedTotal: acceptVals.length === 0 ? unavailable("no cell reported acceptance") : sum(acceptVals),
463
488
  usdTotal: usdVals.length === 0 ? unavailable("no cell reported spend") : round(sum(usdVals), 6),
489
+ spendUsd: {
490
+ journalDerived: {
491
+ value: journalSpendVals.length === 0 ? unavailable("no cell measured journal-derived spend") : round(sum(journalSpendVals), 6),
492
+ runs: journalSpendVals.length
493
+ },
494
+ closeRecord: {
495
+ value: closeSpendVals.length === 0 ? unavailable("no cell carried a close record") : round(sum(closeSpendVals), 6),
496
+ runs: closeSpendVals.length
497
+ }
498
+ },
464
499
  resolvedCount: resolvedVals.length === 0 ? unavailable("no cell reported a judge verdict") : resolvedVals.filter((v) => v === true).length,
465
500
  perCell
466
501
  };
@@ -961,7 +996,7 @@ function renderSupervisorRunHeadline(r) {
961
996
  `RUN-REPORT ${r.instanceId ?? "?"} [${r.arm ?? "?"}]`,
962
997
  ` steers=${steerNote}`,
963
998
  ` waves=${showMeasured(o.waves)} sizes=${isUnavailable(o.waveSizes) ? `unavailable — ${o.waveSizes.unavailable}` : `[${o.waveSizes.join(",")}]`} workers=${showMeasured(o.workersSpawned)} settled=${showMeasured(o.workersSettled)} cancelled=${showMeasured(o.workersCancelled)}`,
964
- ` concurrency max=${showMeasured(o.maxConcurrency)} utilization=${showMeasured(o.workerUtilization)} idle=${fmtMs(o.idleMs)} (${showMeasured(o.idlePct)}%) wall=${fmtMs(o.supervisorWallMs)}`,
999
+ ` concurrency max=${showMeasured(o.maxConcurrency)} utilization=${showMeasured(o.workerUtilization)} idle=${fmtMs(o.idleMs)} (${showMeasured(o.idlePct)}%) wall=${fmtMs(o.supervisorWallMs)}${o.supervisorWallSource === "journal-span" ? " (journal-span lower bound)" : ""}`,
965
1000
  ` respawns=${showMeasured(o.respawns)} evidence→respawn=${showMeasured(r.decision.observeThenRespawn)} blind-respawn=${showMeasured(r.decision.respawnWithoutEvidence)} depth=${showMeasured(o.delegationDepth)}`,
966
1001
  ` accepted=${showMeasured(r.decision.accepted)} rejected=${showMeasured(r.decision.rejected)} empty-pass=${showMeasured(r.decision.emptyPass)}`,
967
1002
  ` brain=$${showMeasured(r.economics.brain.usd)} total=$${showMeasured(r.economics.totalUsd)} judge.resolved=${showMeasured(r.outcome.judgeResolved)} score=${showMeasured(r.outcome.judgeScore)} verify=${showMeasured(r.outcome.verifyPass)}`,
@@ -1008,7 +1043,7 @@ function renderSupervisorRunMarkdown(r) {
1008
1043
  out.push(`| Repeated labels | ${isUnavailable(o.repeatedLabels) ? showMeasured(o.repeatedLabels) : o.repeatedLabels.length === 0 ? "none" : o.repeatedLabels.join(", ")} |`);
1009
1044
  out.push(`| Delegation depth | ${showMeasured(o.delegationDepth)} |`);
1010
1045
  out.push(`| Time to first spawn | ${fmtMs(o.timeToFirstSpawnMs)} |`);
1011
- out.push(`| Supervisor wall | ${fmtMs(o.supervisorWallMs)} |`);
1046
+ out.push(`| Supervisor wall | ${fmtMs(o.supervisorWallMs)}${isUnavailable(o.supervisorWallSource) ? "" : ` (source: ${o.supervisorWallSource}${o.supervisorWallSource === "journal-span" ? ", lower bound" : ""})`} |`);
1012
1047
  out.push(`| Idle (zero live workers) | ${fmtMs(o.idleMs)} (${showMeasured(o.idlePct)}%) |`);
1013
1048
  out.push(`| Worker utilization (Σ worker wall ÷ supervisor wall) | ${showMeasured(o.workerUtilization)} |`);
1014
1049
  out.push("");
@@ -1044,6 +1079,7 @@ function renderSupervisorRunMarkdown(r) {
1044
1079
  if (!isUnavailable(e.brainTruncations) && e.brainTruncations > 0) out.push(`- **BRAIN OUTPUT TRUNCATED: ${e.brainTruncations} completion(s) hit \`finish_reason: "length"\`** — the supervisor acted on a half-written plan. Its output ceiling is too low; see \`brain.jsonl\` for the per-call \`req_max_tokens\`.`);
1045
1080
  else out.push(`- Brain completions truncated (finish_reason=length): ${showMeasured(e.brainTruncations)}`);
1046
1081
  out.push(`- Total USD: ${showMeasured(e.totalUsd)} (source: ${e.totalUsdSource})`);
1082
+ out.push(`- Spend measured two ways: journal-derived $${showMeasured(e.spend.journalDerived.usd)} over ${e.spend.journalDerived.records} journal record(s) (execution accounting) · close-record $${showMeasured(e.spend.closeRecord.usd)} over ${e.spend.closeRecord.records} close record(s) (billing-shaped). Divergence is a signal, not an error.`);
1047
1083
  out.push(`- Cost per accepted patch: ${showMeasured(e.costPerAcceptedPatchUsd)}`);
1048
1084
  if (isUnavailable(e.workerWallMsDistribution)) out.push(`- Worker wall distribution: unavailable — ${e.workerWallMsDistribution.unavailable}`);
1049
1085
  else {
@@ -1101,6 +1137,7 @@ function renderSupervisorRollupMarkdown(rollup, title = "Round rollup") {
1101
1137
  out.push(`- Idle share (mean): ${showMeasured(rollup.idlePctMean)}%`);
1102
1138
  out.push(`- Workers spawned: ${showMeasured(rollup.workersSpawnedTotal)} · accepted: ${showMeasured(rollup.acceptedTotal)}`);
1103
1139
  out.push(`- Spend: $${showMeasured(rollup.usdTotal)} · judged resolved: ${showMeasured(rollup.resolvedCount)}/${rollup.cells}`);
1140
+ out.push(`- Spend measured two ways: journal-derived $${showMeasured(rollup.spendUsd.journalDerived.value)} over ${rollup.spendUsd.journalDerived.runs}/${rollup.cells} runs (execution accounting) · close-record $${showMeasured(rollup.spendUsd.closeRecord.value)} over ${rollup.spendUsd.closeRecord.runs}/${rollup.cells} runs (billing-shaped)`);
1104
1141
  out.push("");
1105
1142
  out.push("| Instance | Arm | Steers | Waves | Utilization | Idle % | Resolved | USD |");
1106
1143
  out.push("|---|---|---:|---:|---:|---:|---|---:|");
@@ -1330,15 +1367,56 @@ function runtimeState(root, startedAt, result, trajectory, resultPath, trajector
1330
1367
  });
1331
1368
  }
1332
1369
  /**
1370
+ * Sources for a run dir whose spawn journal does not exist. Mirrors the
1371
+ * absent shape `readLoopsSupervisorRun` returns for a missing store: every
1372
+ * journal-dependent metric downstream reads `unavailable`, never 0.
1373
+ */
1374
+ function absentRuntimeSupervisorRun(runDir, resultText) {
1375
+ const reason = `no Runtime spawn journal (${JOURNAL_FILE}) under ${runDir}`;
1376
+ return {
1377
+ runRef: runDir,
1378
+ instanceId: null,
1379
+ arm: null,
1380
+ supRunDir: null,
1381
+ journal: null,
1382
+ journalMissingReason: reason,
1383
+ brainLog: null,
1384
+ brainLogMissingReason: "Runtime FileRunContext records spend but not model completion finish reasons",
1385
+ state: null,
1386
+ progress: null,
1387
+ workers: null,
1388
+ workersMissingReason: reason,
1389
+ result: resultText,
1390
+ judge: null,
1391
+ judgeSource: null,
1392
+ patch: null,
1393
+ driverLog: null,
1394
+ harnessWorkerTokens: null,
1395
+ harnessMissingReason: "Runtime FileRunContext has no external worker-token join",
1396
+ limits: NO_SOURCE_LIMITS,
1397
+ rootTranscriptRef: null,
1398
+ traceCommand: "unavailable — Runtime FileRunContext records no provider-session trace identity"
1399
+ };
1400
+ }
1401
+ /**
1333
1402
  * Read one agent-runtime `createFileRunContext(dir)` directory.
1334
1403
  *
1404
+ * A run dir without `spawn-journal.jsonl` returns the same absent-shaped
1405
+ * sources `readLoopsSupervisorRun` returns for a missing store: `journal` and
1406
+ * `workers` null, each with its reason, so every dependent metric reads
1407
+ * `unavailable` — never 0 and never a throw. Pass `strict: true` to throw on
1408
+ * the missing journal instead. A journal that exists but cannot be parsed
1409
+ * always throws: a corrupt journal is a defect, not an absence.
1410
+ *
1335
1411
  * The reader translates storage envelopes only. It does not assign research
1336
1412
  * roles, interpret artifacts, or turn process completion into a quality
1337
1413
  * verdict.
1338
1414
  */
1339
- async function readRuntimeSupervisorRun(runDir) {
1415
+ async function readRuntimeSupervisorRun(runDir, opts = {}) {
1340
1416
  const journalPath = join(runDir, JOURNAL_FILE);
1341
- const normalized = parseEnvelopeJournal(await readFile(journalPath, "utf8"), journalPath);
1417
+ const rawJournal = opts.strict === true ? await readFile(journalPath, "utf8") : await readMaybe$1(journalPath);
1418
+ if (rawJournal === null) return absentRuntimeSupervisorRun(runDir, await readMaybe$1(join(runDir, RESULT_FILE)));
1419
+ const normalized = parseEnvelopeJournal(rawJournal, journalPath);
1342
1420
  const resultText = await readMaybe$1(join(runDir, RESULT_FILE));
1343
1421
  const trajectoryText = await readMaybe$1(join(runDir, TRAJECTORY_FILE));
1344
1422
  const result = parseOptionalRecord(resultText, join(runDir, RESULT_FILE));
@@ -1381,10 +1459,10 @@ async function readRuntimeSupervisorRun(runDir) {
1381
1459
  };
1382
1460
  }
1383
1461
  /** The agent-runtime file-backed layout as a `SupervisorRunReader`. */
1384
- function runtimeSupervisorRunReader(runDir) {
1462
+ function runtimeSupervisorRunReader(runDir, opts = {}) {
1385
1463
  return {
1386
1464
  runRef: runDir,
1387
- read: () => readRuntimeSupervisorRun(runDir)
1465
+ read: () => readRuntimeSupervisorRun(runDir, opts)
1388
1466
  };
1389
1467
  }
1390
1468
  /** True when a directory contains Runtime's canonical file-backed journal. */