@tangle-network/agent-eval 0.126.7 → 0.128.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.
Files changed (132) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +6 -2
  3. package/dist/analyst/index.d.ts +52 -43
  4. package/dist/analyst/index.js +4 -4
  5. package/dist/belief-state/index.d.ts +35 -40
  6. package/dist/belief-state/index.js +2 -2
  7. package/dist/benchmarks/index.d.ts +30 -12
  8. package/dist/benchmarks/index.js +12 -11
  9. package/dist/builder-eval/index.js +1 -1
  10. package/dist/campaign/index.d.ts +441 -160
  11. package/dist/campaign/index.js +11 -10
  12. package/dist/{chunk-IILEIWGW.js → chunk-2JX3CFMB.js} +100 -40
  13. package/dist/chunk-2JX3CFMB.js.map +1 -0
  14. package/dist/chunk-2MKQIFS4.js +183 -0
  15. package/dist/chunk-2MKQIFS4.js.map +1 -0
  16. package/dist/{chunk-NUKSVU3W.js → chunk-5DTSBUL2.js} +2 -2
  17. package/dist/{chunk-H5UD2323.js → chunk-7ZZMD7UK.js} +102 -2
  18. package/dist/chunk-7ZZMD7UK.js.map +1 -0
  19. package/dist/{chunk-474LBSOX.js → chunk-BYT7ELPS.js} +45 -18
  20. package/dist/chunk-BYT7ELPS.js.map +1 -0
  21. package/dist/{chunk-LUNF2SEL.js → chunk-DJKY2TSY.js} +3 -3
  22. package/dist/chunk-DPUHNQLN.js +232 -0
  23. package/dist/chunk-DPUHNQLN.js.map +1 -0
  24. package/dist/chunk-DRYIUNWY.js +622 -0
  25. package/dist/chunk-DRYIUNWY.js.map +1 -0
  26. package/dist/{chunk-IPYXE555.js → chunk-EJGRPCO3.js} +36 -13
  27. package/dist/chunk-EJGRPCO3.js.map +1 -0
  28. package/dist/{chunk-OCFJACJU.js → chunk-EOSZT7PL.js} +82 -18
  29. package/dist/chunk-EOSZT7PL.js.map +1 -0
  30. package/dist/{chunk-ZVCHKKOP.js → chunk-EZJEIH2R.js} +39 -15
  31. package/dist/chunk-EZJEIH2R.js.map +1 -0
  32. package/dist/{chunk-M7AH34KV.js → chunk-IHQDPH7D.js} +32 -16
  33. package/dist/chunk-IHQDPH7D.js.map +1 -0
  34. package/dist/{chunk-PJQFMIOX.js → chunk-MHELPNRP.js} +31 -1
  35. package/dist/chunk-MHELPNRP.js.map +1 -0
  36. package/dist/{chunk-K6IAZZ6L.js → chunk-NACAGYSY.js} +153 -122
  37. package/dist/chunk-NACAGYSY.js.map +1 -0
  38. package/dist/{chunk-BTACVO2W.js → chunk-NKAGIDE2.js} +542 -258
  39. package/dist/chunk-NKAGIDE2.js.map +1 -0
  40. package/dist/{chunk-ARU2PZFM.js → chunk-NYLOYM6N.js} +43 -23
  41. package/dist/chunk-NYLOYM6N.js.map +1 -0
  42. package/dist/{chunk-QOTFXW5L.js → chunk-P5W7RQKK.js} +2 -2
  43. package/dist/{chunk-IR3KBHOY.js → chunk-P6FYH6K4.js} +39 -3
  44. package/dist/chunk-P6FYH6K4.js.map +1 -0
  45. package/dist/{chunk-J5SQWP6Y.js → chunk-PBE2LOSS.js} +18 -2
  46. package/dist/chunk-PBE2LOSS.js.map +1 -0
  47. package/dist/{chunk-X4UCIOTZ.js → chunk-S5YLIBFX.js} +2 -2
  48. package/dist/{chunk-7VYEGHWF.js → chunk-SZLVEKMJ.js} +41 -20
  49. package/dist/chunk-SZLVEKMJ.js.map +1 -0
  50. package/dist/{chunk-P22LJ3Y2.js → chunk-TBL77AUT.js} +8 -8
  51. package/dist/chunk-TBL77AUT.js.map +1 -0
  52. package/dist/{chunk-LKKT3IVV.js → chunk-TSN7JT6D.js} +6 -4
  53. package/dist/{chunk-LKKT3IVV.js.map → chunk-TSN7JT6D.js.map} +1 -1
  54. package/dist/{chunk-E3IADPLG.js → chunk-UB2LOJ6Q.js} +51 -227
  55. package/dist/chunk-UB2LOJ6Q.js.map +1 -0
  56. package/dist/{chunk-MAX3TN3C.js → chunk-UWZZKKU7.js} +7 -19
  57. package/dist/chunk-UWZZKKU7.js.map +1 -0
  58. package/dist/{chunk-DPZAEKA6.js → chunk-VLOATJQ2.js} +85 -57
  59. package/dist/chunk-VLOATJQ2.js.map +1 -0
  60. package/dist/{chunk-MOXWMGPC.js → chunk-XDWDC2MP.js} +197 -79
  61. package/dist/chunk-XDWDC2MP.js.map +1 -0
  62. package/dist/{chunk-CGG5SLH3.js → chunk-XPRT64IE.js} +2 -2
  63. package/dist/{chunk-FO7HEH76.js → chunk-YJBNWCAA.js} +2 -2
  64. package/dist/{chunk-KE2VWPZX.js → chunk-ZUUWPZCV.js} +3 -3
  65. package/dist/cli.js +4 -4
  66. package/dist/contract/index.d.ts +438 -138
  67. package/dist/contract/index.js +259 -113
  68. package/dist/contract/index.js.map +1 -1
  69. package/dist/control.d.ts +56 -39
  70. package/dist/control.js +2 -3
  71. package/dist/hosted/index.d.ts +152 -51
  72. package/dist/hosted/index.js +24 -1
  73. package/dist/index.d.ts +399 -277
  74. package/dist/index.js +363 -244
  75. package/dist/index.js.map +1 -1
  76. package/dist/meta-eval/index.d.ts +33 -36
  77. package/dist/meta-eval/index.js +2 -2
  78. package/dist/multishot/index.d.ts +1 -0
  79. package/dist/openapi.json +1 -1
  80. package/dist/pipelines/index.js +2 -2
  81. package/dist/reporting.d.ts +101 -80
  82. package/dist/reporting.js +7 -5
  83. package/dist/rl.d.ts +198 -142
  84. package/dist/rl.js +235 -127
  85. package/dist/rl.js.map +1 -1
  86. package/dist/rollout/index.d.ts +54 -67
  87. package/dist/rollout/index.js +6 -5
  88. package/dist/{run-campaign-FFRM3RH5.js → run-campaign-ISHFZ7FJ.js} +6 -3
  89. package/dist/supervisor-run/index.d.ts +7 -15
  90. package/dist/supervisor-run/index.js +2 -2
  91. package/dist/traces.d.ts +72 -50
  92. package/dist/traces.js +9 -6
  93. package/dist/wire/index.d.ts +1 -0
  94. package/dist/wire/index.js +2 -2
  95. package/docs/concepts.md +18 -1
  96. package/docs/customer-journeys.md +7 -6
  97. package/docs/hosted-ingest-spec.md +37 -40
  98. package/docs/insight-report.md +78 -7
  99. package/docs/research-report-methodology.md +20 -21
  100. package/docs/rollout.md +13 -8
  101. package/package.json +4 -3
  102. package/dist/chunk-474LBSOX.js.map +0 -1
  103. package/dist/chunk-7VYEGHWF.js.map +0 -1
  104. package/dist/chunk-ARU2PZFM.js.map +0 -1
  105. package/dist/chunk-BTACVO2W.js.map +0 -1
  106. package/dist/chunk-DPZAEKA6.js.map +0 -1
  107. package/dist/chunk-E3IADPLG.js.map +0 -1
  108. package/dist/chunk-H5UD2323.js.map +0 -1
  109. package/dist/chunk-IILEIWGW.js.map +0 -1
  110. package/dist/chunk-IPYXE555.js.map +0 -1
  111. package/dist/chunk-IR3KBHOY.js.map +0 -1
  112. package/dist/chunk-J5SQWP6Y.js.map +0 -1
  113. package/dist/chunk-K6IAZZ6L.js.map +0 -1
  114. package/dist/chunk-M7AH34KV.js.map +0 -1
  115. package/dist/chunk-MAX3TN3C.js.map +0 -1
  116. package/dist/chunk-MOXWMGPC.js.map +0 -1
  117. package/dist/chunk-OCFJACJU.js.map +0 -1
  118. package/dist/chunk-P22LJ3Y2.js.map +0 -1
  119. package/dist/chunk-PJQFMIOX.js.map +0 -1
  120. package/dist/chunk-VSMTAMNK.js +0 -53
  121. package/dist/chunk-VSMTAMNK.js.map +0 -1
  122. package/dist/chunk-ZVCHKKOP.js.map +0 -1
  123. package/dist/chunk-ZZUXHH3R.js +0 -99
  124. package/dist/chunk-ZZUXHH3R.js.map +0 -1
  125. /package/dist/{chunk-NUKSVU3W.js.map → chunk-5DTSBUL2.js.map} +0 -0
  126. /package/dist/{chunk-LUNF2SEL.js.map → chunk-DJKY2TSY.js.map} +0 -0
  127. /package/dist/{chunk-QOTFXW5L.js.map → chunk-P5W7RQKK.js.map} +0 -0
  128. /package/dist/{chunk-X4UCIOTZ.js.map → chunk-S5YLIBFX.js.map} +0 -0
  129. /package/dist/{chunk-CGG5SLH3.js.map → chunk-XPRT64IE.js.map} +0 -0
  130. /package/dist/{chunk-FO7HEH76.js.map → chunk-YJBNWCAA.js.map} +0 -0
  131. /package/dist/{chunk-KE2VWPZX.js.map → chunk-ZUUWPZCV.js.map} +0 -0
  132. /package/dist/{run-campaign-FFRM3RH5.js.map → run-campaign-ISHFZ7FJ.js.map} +0 -0
package/dist/index.js CHANGED
@@ -9,11 +9,11 @@ import {
9
9
  checkBehavioralCanary,
10
10
  checkCanaries,
11
11
  runBehavioralCanaries
12
- } from "./chunk-K6IAZZ6L.js";
12
+ } from "./chunk-NACAGYSY.js";
13
13
  import {
14
14
  mintRolloutRows,
15
15
  rolloutReward
16
- } from "./chunk-M7AH34KV.js";
16
+ } from "./chunk-IHQDPH7D.js";
17
17
  import {
18
18
  SUPERVISOR_RUN_SCHEMA,
19
19
  analyzeSupervisorRun,
@@ -27,26 +27,25 @@ import {
27
27
  showMeasured,
28
28
  supervisorRunRolloutLines,
29
29
  writeSupervisorRunReport
30
- } from "./chunk-LKKT3IVV.js";
30
+ } from "./chunk-TSN7JT6D.js";
31
31
  import "./chunk-VBQ3CRKH.js";
32
32
  import {
33
33
  toJsonl,
34
34
  toRewardRows,
35
35
  toSftRows
36
- } from "./chunk-IPYXE555.js";
36
+ } from "./chunk-EJGRPCO3.js";
37
37
  import {
38
- ROLLOUT_FORMAT,
39
38
  ROLLOUT_SCHEMA,
40
39
  assertRolloutLine,
41
40
  isRolloutLine,
42
41
  isTrainableSplit,
43
42
  validateRolloutLine
44
- } from "./chunk-MAX3TN3C.js";
43
+ } from "./chunk-UWZZKKU7.js";
45
44
  import {
46
45
  BENCHMARK_SPLIT_SEED,
47
46
  benchmarks_exports,
48
47
  deterministicSplit
49
- } from "./chunk-CGG5SLH3.js";
48
+ } from "./chunk-XPRT64IE.js";
50
49
  import {
51
50
  DEFAULT_RULES,
52
51
  classifyFailure,
@@ -54,7 +53,7 @@ import {
54
53
  computeToolUseMetrics,
55
54
  iqr,
56
55
  welchsTTest
57
- } from "./chunk-QOTFXW5L.js";
56
+ } from "./chunk-P5W7RQKK.js";
58
57
  import {
59
58
  buildTrajectory
60
59
  } from "./chunk-RZTMDUO7.js";
@@ -77,17 +76,15 @@ import {
77
76
  agentProfileHash,
78
77
  agentProfileId,
79
78
  agentProfileModelId,
80
- comparePairedArms,
81
79
  completionVerdict,
82
80
  createLlmCorrectnessChecker,
83
81
  createTokenRecallChecker,
84
82
  expandProfileAxes,
85
83
  extractProducedState,
86
84
  harnessAxisOf,
87
- pairArms,
88
85
  parseCorrectnessResponse,
89
86
  verifyCompletion
90
- } from "./chunk-E3IADPLG.js";
87
+ } from "./chunk-UB2LOJ6Q.js";
91
88
  import {
92
89
  DEFAULT_MUTATION_PRIMITIVES,
93
90
  DEFAULT_RED_TEAM_CORPUS,
@@ -121,7 +118,7 @@ import {
121
118
  scoreRedTeamOutput,
122
119
  surfaceContentHash,
123
120
  toolNamesForRun
124
- } from "./chunk-BTACVO2W.js";
121
+ } from "./chunk-NKAGIDE2.js";
125
122
  import {
126
123
  BackendIntegrityError,
127
124
  assertRealAgentReceipts,
@@ -133,7 +130,7 @@ import {
133
130
  inMemoryVerdictCache,
134
131
  summarizeAgentReceiptIntegrity,
135
132
  summarizeBackendIntegrity
136
- } from "./chunk-ZVCHKKOP.js";
133
+ } from "./chunk-EZJEIH2R.js";
137
134
  import {
138
135
  DEFAULT_COMPLEXITY_WEIGHTS,
139
136
  FindingsStore,
@@ -146,7 +143,7 @@ import {
146
143
  defaultIsMaterial,
147
144
  diffFindings,
148
145
  runSemanticConceptJudge
149
- } from "./chunk-KE2VWPZX.js";
146
+ } from "./chunk-ZUUWPZCV.js";
150
147
  import {
151
148
  AnalystRegistry,
152
149
  DEFAULT_TRACE_ANALYST_KINDS,
@@ -163,7 +160,7 @@ import {
163
160
  makeFinding,
164
161
  renderPriorFindings,
165
162
  renderUpstreamFindings
166
- } from "./chunk-LUNF2SEL.js";
163
+ } from "./chunk-DJKY2TSY.js";
167
164
  import "./chunk-HHWE3POT.js";
168
165
  import {
169
166
  DEFAULT_RUN_SCORE_WEIGHTS,
@@ -192,18 +189,19 @@ import {
192
189
  stopOnNoProgress,
193
190
  stopOnRepeatedAction,
194
191
  subjectiveEval
195
- } from "./chunk-474LBSOX.js";
192
+ } from "./chunk-BYT7ELPS.js";
196
193
  import {
197
194
  assertReleaseConfidence,
198
195
  bootstrapCi,
199
196
  evaluateReleaseConfidence,
200
197
  judgeReplayGate,
201
198
  renderReleaseReport
202
- } from "./chunk-MOXWMGPC.js";
199
+ } from "./chunk-XDWDC2MP.js";
203
200
  import {
204
201
  runEvalCampaign
205
- } from "./chunk-P22LJ3Y2.js";
206
- import "./chunk-ARU2PZFM.js";
202
+ } from "./chunk-TBL77AUT.js";
203
+ import "./chunk-NYLOYM6N.js";
204
+ import "./chunk-2MKQIFS4.js";
207
205
  import {
208
206
  LlmCallError,
209
207
  LlmClient,
@@ -219,7 +217,7 @@ import {
219
217
  maximumChargeForLlmRequest,
220
218
  probeLlm,
221
219
  stripFencedJson
222
- } from "./chunk-J5SQWP6Y.js";
220
+ } from "./chunk-PBE2LOSS.js";
223
221
  import {
224
222
  evaluateInterimReleaseConfidence,
225
223
  pairedEvalueSequence
@@ -230,7 +228,12 @@ import {
230
228
  paretoChart,
231
229
  researchReport,
232
230
  summaryTable
233
- } from "./chunk-DPZAEKA6.js";
231
+ } from "./chunk-VLOATJQ2.js";
232
+ import {
233
+ comparePairedArms,
234
+ pairArms,
235
+ pairRunRecords
236
+ } from "./chunk-DPUHNQLN.js";
234
237
  import {
235
238
  benjaminiHochberg,
236
239
  bonferroni,
@@ -253,6 +256,7 @@ import {
253
256
  mulberry32,
254
257
  normalizeScores,
255
258
  pairedBootstrap,
259
+ pairedCohensDz,
256
260
  pairedMde,
257
261
  pairedRiskDifference,
258
262
  pairedSignTest,
@@ -262,6 +266,7 @@ import {
262
266
  pearsonR,
263
267
  positionalBias,
264
268
  ranks,
269
+ requiredPairedSampleSize,
265
270
  requiredSampleSize,
266
271
  selfPreference,
267
272
  spearmanR,
@@ -270,7 +275,7 @@ import {
270
275
  weightedMean,
271
276
  wilcoxonSignedRank,
272
277
  wilson
273
- } from "./chunk-PJQFMIOX.js";
278
+ } from "./chunk-MHELPNRP.js";
274
279
  import {
275
280
  CostAccountingIncompleteError,
276
281
  CostCallConflictError,
@@ -321,8 +326,8 @@ import {
321
326
  scoreTraceInsightReadiness,
322
327
  tokenizeDomainWords,
323
328
  traceAnalystOnRunComplete
324
- } from "./chunk-OCFJACJU.js";
325
- import "./chunk-H5UD2323.js";
329
+ } from "./chunk-EOSZT7PL.js";
330
+ import "./chunk-7ZZMD7UK.js";
326
331
  import {
327
332
  extractUsage,
328
333
  extractUsageFromResponse,
@@ -343,7 +348,7 @@ import {
343
348
  TRACE_ANALYST_ACTOR_DESCRIPTION,
344
349
  TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION,
345
350
  analyzeTraces
346
- } from "./chunk-NUKSVU3W.js";
351
+ } from "./chunk-5DTSBUL2.js";
347
352
  import {
348
353
  DEFAULT_REDACTION_RULES,
349
354
  REDACTION_VERSION,
@@ -360,15 +365,17 @@ import {
360
365
  applyToolSpanOtlpAttributes,
361
366
  asString,
362
367
  buildTraceAnalystTools,
368
+ classifyOtlpSpanRole,
363
369
  extractOtlpAttributes,
364
370
  firstStringAttr,
365
371
  inferOtlpKind,
372
+ isOtlpModelCall,
366
373
  projectOtlpFlatLine,
367
374
  readOtlpStatus,
368
375
  stringField,
369
376
  traceAnalystFunctionGroup,
370
377
  traceSpanKindToOpenInferenceKind
371
- } from "./chunk-IR3KBHOY.js";
378
+ } from "./chunk-P6FYH6K4.js";
372
379
  import {
373
380
  RunIntegrityError,
374
381
  assertRunCaptured,
@@ -394,18 +401,23 @@ import {
394
401
  assertRunAgentProfileCell,
395
402
  buildAgentInterfaceProfileCell,
396
403
  buildAgentProfileCell,
404
+ canonicalize,
405
+ evaluateHypothesis,
397
406
  groupRunsByAgentProfileCell,
407
+ hashJson,
398
408
  isRunRecord,
399
409
  modelHasSnapshot,
400
410
  parseRunRecordSafe,
401
411
  requireAgentProfileCell,
402
- resolveRunCostProvenance,
403
412
  roundTripRunRecord,
413
+ runTaskScore,
414
+ signManifest,
404
415
  toAgentProfileJson,
405
416
  validateAgentProfileCell,
406
417
  validateRunRecord,
407
- verifyAgentProfileCell
408
- } from "./chunk-IILEIWGW.js";
418
+ verifyAgentProfileCell,
419
+ verifyManifest
420
+ } from "./chunk-2JX3CFMB.js";
409
421
  import {
410
422
  FAILURE_CLASSES,
411
423
  TRACE_SCHEMA_VERSION,
@@ -415,13 +427,6 @@ import {
415
427
  isSandboxSpan,
416
428
  isToolSpan
417
429
  } from "./chunk-MA6HLL3S.js";
418
- import {
419
- canonicalize,
420
- evaluateHypothesis,
421
- hashJson,
422
- signManifest,
423
- verifyManifest
424
- } from "./chunk-VSMTAMNK.js";
425
430
  import {
426
431
  AgentEvalError,
427
432
  CaptureIntegrityError,
@@ -918,8 +923,8 @@ async function executeScenario(tc, scenario, config) {
918
923
  console.log(` ${judgeName} retry ${attempt}/2 (waiting ${wait / 1e3}s)`);
919
924
  await sleep2(wait);
920
925
  }
921
- const scores2 = await judge(tc, judgeInput);
922
- judgeResults.push(scores2);
926
+ const scores = await judge(tc, judgeInput);
927
+ judgeResults.push(scores);
923
928
  await sleep2(3e3);
924
929
  lastError = void 0;
925
930
  break;
@@ -1536,19 +1541,19 @@ function projectSelectedRows(rows, options) {
1536
1541
  const arm = options.arm(original);
1537
1542
  assertNonEmptyString("arm", arm);
1538
1543
  if (arm !== options.baselineArm && arm !== options.treatmentArm) continue;
1539
- const pairKey2 = options.pairKey(original);
1544
+ const pairKey = options.pairKey(original);
1540
1545
  const clusterKey = options.clusterKey(original);
1541
1546
  const pass = options.pass(original);
1542
1547
  const repKey = options.repKey?.(original);
1543
- assertNonEmptyString("pairKey", pairKey2);
1548
+ assertNonEmptyString("pairKey", pairKey);
1544
1549
  assertNonEmptyString("clusterKey", clusterKey);
1545
1550
  if (typeof pass !== "boolean") {
1546
1551
  throw new ValidationError(
1547
- `clusteredPairedBinary: pass accessor must return boolean for pairKey '${pairKey2}'`
1552
+ `clusteredPairedBinary: pass accessor must return boolean for pairKey '${pairKey}'`
1548
1553
  );
1549
1554
  }
1550
1555
  if (repKey !== void 0) assertNonEmptyString("repKey", repKey);
1551
- projected.push({ pairKey: pairKey2, clusterKey, arm, pass, repKey, original });
1556
+ projected.push({ pairKey, clusterKey, arm, pass, repKey, original });
1552
1557
  }
1553
1558
  return projected;
1554
1559
  }
@@ -2941,9 +2946,9 @@ function scorePrReviewComments(auditCase, comments, source, weights = {}) {
2941
2946
  })
2942
2947
  };
2943
2948
  }
2944
- function summarizePrReviewBenchmark(scores2) {
2949
+ function summarizePrReviewBenchmark(scores) {
2945
2950
  const bySource = /* @__PURE__ */ new Map();
2946
- for (const score of scores2) {
2951
+ for (const score of scores) {
2947
2952
  bySource.set(score.source, [...bySource.get(score.source) ?? [], score]);
2948
2953
  }
2949
2954
  return [...bySource.entries()].map(([source, sourceScores]) => ({
@@ -3688,12 +3693,12 @@ function runJudgesTool(judges) {
3688
3693
  }
3689
3694
  const signal = ctx?.signal ?? new AbortController().signal;
3690
3695
  const scenario = a.scenario;
3691
- const scores2 = {};
3696
+ const scores = {};
3692
3697
  for (const judge of selected) {
3693
3698
  if (scenario !== void 0 && judge.appliesTo && !judge.appliesTo(scenario)) continue;
3694
- scores2[judge.name] = await judge.score({ artifact: a.artifact, scenario, signal });
3699
+ scores[judge.name] = await judge.score({ artifact: a.artifact, scenario, signal });
3695
3700
  }
3696
- return { scores: scores2 };
3701
+ return { scores };
3697
3702
  }
3698
3703
  };
3699
3704
  }
@@ -3807,14 +3812,14 @@ async function runHarnessExperiment(config) {
3807
3812
  const score = config.score ?? ((trace) => critic.scoreTrace(trace));
3808
3813
  const results = await mapLimit(jobs, config.parallelism ?? 1, async (request) => {
3809
3814
  const trace = await config.adapter.run(request);
3810
- const runScore4 = await score(trace, request);
3815
+ const runScore3 = await score(trace, request);
3811
3816
  const result = {
3812
3817
  variant: request.variant,
3813
3818
  scenario: request.scenario,
3814
3819
  trialIndex: request.trialIndex,
3815
3820
  trace,
3816
- score: runScore4,
3817
- aggregate: aggregateRunScore(runScore4, config.weights)
3821
+ score: runScore3,
3822
+ aggregate: aggregateRunScore(runScore3, config.weights)
3818
3823
  };
3819
3824
  await config.onResult?.(result);
3820
3825
  return result;
@@ -3884,20 +3889,20 @@ async function mapLimit(items, limit, fn) {
3884
3889
  function mean3(values) {
3885
3890
  return values.length ? values.reduce((sum4, value) => sum4 + value, 0) / values.length : 0;
3886
3891
  }
3887
- function meanRunScore(scores2) {
3892
+ function meanRunScore(scores) {
3888
3893
  return {
3889
- success: mean3(scores2.map((s) => s.success)),
3890
- goalProgress: mean3(scores2.map((s) => s.goalProgress)),
3891
- repoGroundedness: mean3(scores2.map((s) => s.repoGroundedness)),
3892
- driftPenalty: mean3(scores2.map((s) => s.driftPenalty)),
3893
- toolUseQuality: mean3(scores2.map((s) => s.toolUseQuality)),
3894
- patchQuality: mean3(scores2.map((s) => s.patchQuality)),
3895
- testReality: mean3(scores2.map((s) => s.testReality)),
3896
- finalGate: mean3(scores2.map((s) => s.finalGate)),
3897
- reviewerBlockers: mean3(scores2.map((s) => s.reviewerBlockers)),
3898
- costUsd: mean3(scores2.map((s) => s.costUsd)),
3899
- wallSeconds: mean3(scores2.map((s) => s.wallSeconds)),
3900
- notes: scores2.flatMap((s) => s.notes ?? [])
3894
+ success: mean3(scores.map((s) => s.success)),
3895
+ goalProgress: mean3(scores.map((s) => s.goalProgress)),
3896
+ repoGroundedness: mean3(scores.map((s) => s.repoGroundedness)),
3897
+ driftPenalty: mean3(scores.map((s) => s.driftPenalty)),
3898
+ toolUseQuality: mean3(scores.map((s) => s.toolUseQuality)),
3899
+ patchQuality: mean3(scores.map((s) => s.patchQuality)),
3900
+ testReality: mean3(scores.map((s) => s.testReality)),
3901
+ finalGate: mean3(scores.map((s) => s.finalGate)),
3902
+ reviewerBlockers: mean3(scores.map((s) => s.reviewerBlockers)),
3903
+ costUsd: mean3(scores.map((s) => s.costUsd)),
3904
+ wallSeconds: mean3(scores.map((s) => s.wallSeconds)),
3905
+ notes: scores.flatMap((s) => s.notes ?? [])
3901
3906
  };
3902
3907
  }
3903
3908
 
@@ -4837,12 +4842,7 @@ function assertNonNegative(n, name) {
4837
4842
 
4838
4843
  // src/eval-trace-store.ts
4839
4844
  function runScore(record) {
4840
- const { holdoutScore, searchScore } = record.outcome;
4841
- if (typeof holdoutScore === "number") return holdoutScore;
4842
- if (typeof searchScore === "number") return searchScore;
4843
- throw new ValidationError(
4844
- `EvalTraceStore: run ${record.runId} has neither holdoutScore nor searchScore`
4845
- );
4845
+ return runTaskScore(record);
4846
4846
  }
4847
4847
  function matches(record, f) {
4848
4848
  if (f.experimentId && record.experimentId !== f.experimentId) return false;
@@ -4850,8 +4850,12 @@ function matches(record, f) {
4850
4850
  if (f.scenarioId && record.scenarioId !== f.scenarioId) return false;
4851
4851
  if (f.model && record.model !== f.model) return false;
4852
4852
  if (f.splitTag && record.splitTag !== f.splitTag) return false;
4853
- if (f.minScore !== void 0 && runScore(record) < f.minScore) return false;
4854
- if (f.maxScore !== void 0 && runScore(record) > f.maxScore) return false;
4853
+ if (f.minScore !== void 0 || f.maxScore !== void 0) {
4854
+ const score = runScore(record);
4855
+ if (score === void 0) return false;
4856
+ if (f.minScore !== void 0 && score < f.minScore) return false;
4857
+ if (f.maxScore !== void 0 && score > f.maxScore) return false;
4858
+ }
4855
4859
  if (f.rawEquals && record.outcome.raw[f.rawEquals.key] !== f.rawEquals.value) return false;
4856
4860
  if (f.where && !f.where(record)) return false;
4857
4861
  return true;
@@ -4941,13 +4945,17 @@ var EvalTraceStore = class {
4941
4945
  candidateId: opts.candidateId,
4942
4946
  splitTag: opts.splitTag
4943
4947
  });
4944
- if (rows.length === 0) return null;
4945
- let best = rows[0];
4946
- let bestScore = runScore(best);
4947
- for (let i = 1; i < rows.length; i++) {
4948
- const s = runScore(rows[i]);
4948
+ const scored = rows.flatMap((record) => {
4949
+ const score = runScore(record);
4950
+ return score === void 0 ? [] : [{ record, score }];
4951
+ });
4952
+ if (scored.length === 0) return null;
4953
+ let best = scored[0].record;
4954
+ let bestScore = scored[0].score;
4955
+ for (let i = 1; i < scored.length; i++) {
4956
+ const s = scored[i].score;
4949
4957
  if (s > bestScore) {
4950
- best = rows[i];
4958
+ best = scored[i].record;
4951
4959
  bestScore = s;
4952
4960
  }
4953
4961
  }
@@ -4973,6 +4981,7 @@ var EvalTraceStore = class {
4973
4981
  const sid = r.scenarioId;
4974
4982
  if (!sid) continue;
4975
4983
  const s = runScore(r);
4984
+ if (s === void 0) continue;
4976
4985
  const prev = m.get(sid);
4977
4986
  if (prev === void 0 || s > prev) m.set(sid, s);
4978
4987
  }
@@ -5050,9 +5059,9 @@ function median(sorted) {
5050
5059
  const mid = Math.floor(n / 2);
5051
5060
  return n % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
5052
5061
  }
5053
- function stddev(values, mean7) {
5062
+ function stddev(values, mean6) {
5054
5063
  if (values.length < 2) return 0;
5055
- const variance = values.reduce((acc, v) => acc + (v - mean7) ** 2, 0) / values.length;
5064
+ const variance = values.reduce((acc, v) => acc + (v - mean6) ** 2, 0) / values.length;
5056
5065
  return Math.sqrt(variance);
5057
5066
  }
5058
5067
  function computeExperimentStats(reps, thresholds) {
@@ -5071,22 +5080,22 @@ function computeExperimentStats(reps, thresholds) {
5071
5080
  stable: false
5072
5081
  };
5073
5082
  }
5074
- const scores2 = reps.map((r) => {
5083
+ const scores = reps.map((r) => {
5075
5084
  if (!Number.isFinite(r.score)) {
5076
5085
  throw new ValidationError(`experiment-tracker: rep ${r.rep} has non-finite score ${r.score}`);
5077
5086
  }
5078
5087
  return r.score;
5079
5088
  });
5080
- const sorted = [...scores2].sort((a, b) => a - b);
5081
- const mean7 = scores2.reduce((s, v) => s + v, 0) / n;
5082
- const sd = stddev(scores2, mean7);
5083
- const spread = iqr(scores2);
5089
+ const sorted = [...scores].sort((a, b) => a - b);
5090
+ const mean6 = scores.reduce((s, v) => s + v, 0) / n;
5091
+ const sd = stddev(scores, mean6);
5092
+ const spread = iqr(scores);
5084
5093
  const rated = reps.filter((r) => typeof r.passed === "boolean");
5085
5094
  const passRate = rated.length === 0 ? null : rated.filter((r) => r.passed).length / rated.length;
5086
5095
  const stable = spread < t.iqrUnstableAbove && sd < t.stddevUnstableAbove;
5087
5096
  return {
5088
5097
  median: median(sorted),
5089
- mean: mean7,
5098
+ mean: mean6,
5090
5099
  min: sorted[0],
5091
5100
  max: sorted[n - 1],
5092
5101
  iqr: spread,
@@ -5275,6 +5284,7 @@ function leaderboard(records, opts) {
5275
5284
  const profile = rs[0].agentProfile;
5276
5285
  const harness = profile?.harness?.id;
5277
5286
  const model = profile?.model;
5287
+ const knownCosts = rs.map((r) => r.costUsd).filter(isFiniteNumber);
5278
5288
  return {
5279
5289
  key,
5280
5290
  label: harness && model ? `${harness} \xB7 ${model}` : key,
@@ -5284,7 +5294,7 @@ function leaderboard(records, opts) {
5284
5294
  n: rs.length,
5285
5295
  passRate: ci.estimate,
5286
5296
  passRateCi95: [ci.lower, ci.upper],
5287
- meanCostUsd: mean4(rs.map((r) => r.costUsd)),
5297
+ meanCostUsd: knownCosts.length === rs.length ? mean4(knownCosts) : null,
5288
5298
  meanTokensIn: mean4(rs.map((r) => r.tokenUsage.input)),
5289
5299
  meanTokensOut: mean4(rs.map((r) => r.tokenUsage.output)),
5290
5300
  meanWallMs: mean4(rs.map((r) => r.wallMs))
@@ -5298,6 +5308,9 @@ function leaderboard(records, opts) {
5298
5308
  });
5299
5309
  return rows;
5300
5310
  }
5311
+ function isFiniteNumber(value) {
5312
+ return typeof value === "number" && Number.isFinite(value);
5313
+ }
5301
5314
 
5302
5315
  // src/muffled-gate-scanner.ts
5303
5316
  import { existsSync, readdirSync, readFileSync, statSync } from "fs";
@@ -5687,9 +5700,6 @@ function median2(xs) {
5687
5700
  const mid = Math.floor(sorted.length / 2);
5688
5701
  return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
5689
5702
  }
5690
- function runScore2(run) {
5691
- return run.outcome.holdoutScore ?? run.outcome.searchScore;
5692
- }
5693
5703
  function aggregatePerDimension(runs) {
5694
5704
  const sums = /* @__PURE__ */ new Map();
5695
5705
  for (const run of runs) {
@@ -5722,14 +5732,14 @@ function recordRuns(runs, opts) {
5722
5732
  }
5723
5733
  const lines = [];
5724
5734
  for (const [scenarioId, scenarioRuns] of byScenario) {
5725
- const scored = scenarioRuns.map((run) => ({ run, score: runScore2(run) })).filter((s) => s.score !== void 0);
5735
+ const scored = scenarioRuns.map((run) => ({ run, score: runTaskScore(run) })).filter((s) => s.score !== void 0);
5726
5736
  if (scored.length === 0) continue;
5727
- const scores2 = scored.map((s) => s.score);
5737
+ const scores = scored.map((s) => s.score);
5728
5738
  const entry = {
5729
5739
  commitSha: opts.commitSha,
5730
5740
  timestamp,
5731
- scores: scores2,
5732
- composite: median2(scores2),
5741
+ scores,
5742
+ composite: median2(scores),
5733
5743
  runIds: scored.map((s) => s.run.runId)
5734
5744
  };
5735
5745
  const perDimension = aggregatePerDimension(scenarioRuns);
@@ -6317,20 +6327,20 @@ function checkRule(rule, ordered) {
6317
6327
  function evaluateTraceContract(contract, spans) {
6318
6328
  assertContract(contract);
6319
6329
  const ordered = orderSpans(spans);
6320
- const scores2 = {};
6330
+ const scores = {};
6321
6331
  const violations = [];
6322
6332
  for (const rule of contract.rules) {
6323
6333
  const ruleViolations = checkRule(rule, ordered);
6324
- scores2[rule.label] = ruleViolations.length === 0 ? 1 : 0;
6334
+ scores[rule.label] = ruleViolations.length === 0 ? 1 : 0;
6325
6335
  violations.push(...ruleViolations);
6326
6336
  }
6327
6337
  const ruleCount = contract.rules.length;
6328
- const passCount = Object.values(scores2).filter((s) => s === 1).length;
6338
+ const passCount = Object.values(scores).filter((s) => s === 1).length;
6329
6339
  return {
6330
6340
  contract: contract.name,
6331
6341
  valid: passCount === ruleCount,
6332
6342
  score: passCount / ruleCount,
6333
- scores: scores2,
6343
+ scores,
6334
6344
  violations,
6335
6345
  notes: `${passCount}/${ruleCount} rules passed`
6336
6346
  };
@@ -6658,12 +6668,12 @@ function renderMarkdownReport(reports) {
6658
6668
  async function aggregateRunMetrics(runs, store) {
6659
6669
  if (runs.length === 0) return {};
6660
6670
  const durations = [];
6661
- const scores2 = [];
6671
+ const scores = [];
6662
6672
  const passes = [];
6663
6673
  const costs = [];
6664
6674
  for (const r of runs) {
6665
6675
  if (r.endedAt) durations.push(r.endedAt - r.startedAt);
6666
- if (r.outcome?.score !== void 0) scores2.push(r.outcome.score);
6676
+ if (r.outcome?.score !== void 0) scores.push(r.outcome.score);
6667
6677
  passes.push(r.outcome?.pass === true ? 1 : 0);
6668
6678
  const llm = await llmSpans(store, r.runId);
6669
6679
  costs.push(aggregateLlm(llm).costUsd);
@@ -6672,7 +6682,7 @@ async function aggregateRunMetrics(runs, store) {
6672
6682
  provisionMs: average(durations),
6673
6683
  firstTokenMs: average(durations),
6674
6684
  wallMs: average(durations),
6675
- overallScore: average(scores2),
6685
+ overallScore: average(scores),
6676
6686
  passRate: average(passes),
6677
6687
  costUsd: average(costs)
6678
6688
  };
@@ -6735,7 +6745,7 @@ async function toLangfuseEnvelope(store, runId) {
6735
6745
  },
6736
6746
  metadata: { finishReason: s.finishReason, cachedTokens: s.cachedTokens }
6737
6747
  }));
6738
- const scores2 = judges.map((j) => ({
6748
+ const scores = judges.map((j) => ({
6739
6749
  id: j.spanId,
6740
6750
  traceId: run.runId,
6741
6751
  observationId: j.targetSpanId,
@@ -6743,7 +6753,7 @@ async function toLangfuseEnvelope(store, runId) {
6743
6753
  value: j.score,
6744
6754
  comment: j.rationale
6745
6755
  }));
6746
- return { traceId: run.runId, generations, scores: scores2 };
6756
+ return { traceId: run.runId, generations, scores };
6747
6757
  }
6748
6758
  async function toPrometheusText(store) {
6749
6759
  const runs = await store.listRuns();
@@ -6844,12 +6854,12 @@ async function paraphraseRobustness(prompt, mutators, scoreFn, options = {}) {
6844
6854
  variantScores.push({ mutator: id, score, mutated });
6845
6855
  all.push(score);
6846
6856
  }
6847
- const mean7 = all.reduce((a, b) => a + b, 0) / all.length;
6848
- const variance = all.reduce((a, v) => a + (v - mean7) ** 2, 0) / all.length;
6857
+ const mean6 = all.reduce((a, b) => a + b, 0) / all.length;
6858
+ const variance = all.reduce((a, v) => a + (v - mean6) ** 2, 0) / all.length;
6849
6859
  const stdDev = Math.sqrt(variance);
6850
- const ref = Math.abs(mean7) > 1e-9 ? Math.abs(mean7) : 1;
6860
+ const ref = Math.abs(mean6) > 1e-9 ? Math.abs(mean6) : 1;
6851
6861
  const robustness = Math.max(0, 1 - stdDev / ref);
6852
- return { originalScore, variantScores, meanScore: mean7, stdDev, robustness };
6862
+ return { originalScore, variantScores, meanScore: mean6, stdDev, robustness };
6853
6863
  }
6854
6864
  var lowercaseMutator = (p) => p.toLowerCase();
6855
6865
  var sentenceReorderMutator = (p, seed) => {
@@ -6906,18 +6916,18 @@ async function paraphraseRobustnessScenarios(args) {
6906
6916
  const deltas = {};
6907
6917
  const paraphrasedAll = [];
6908
6918
  for (const m of args.mutators) {
6909
- const scores2 = [];
6919
+ const scores = [];
6910
6920
  for (let r = 0; r < reps; r++) {
6911
6921
  const mutatedTurns = scenario.userTurns.map((t) => m.mutator(t));
6912
6922
  const out = await args.runScenario({
6913
6923
  id: scenario.id,
6914
6924
  userTurns: mutatedTurns
6915
6925
  });
6916
- scores2.push(out.score);
6926
+ scores.push(out.score);
6917
6927
  }
6918
- const mean7 = scores2.reduce((a, b) => a + b, 0) / scores2.length;
6919
- deltas[m.name] = mean7 - originalScore;
6920
- paraphrasedAll.push(...scores2);
6928
+ const mean6 = scores.reduce((a, b) => a + b, 0) / scores.length;
6929
+ deltas[m.name] = mean6 - originalScore;
6930
+ paraphrasedAll.push(...scores);
6921
6931
  }
6922
6932
  const paraphrasedMean = paraphrasedAll.length === 0 ? originalScore : paraphrasedAll.reduce((a, b) => a + b, 0) / paraphrasedAll.length;
6923
6933
  perScenario.push({ id: scenario.id, originalScore, paraphrasedMean, deltas });
@@ -7332,10 +7342,10 @@ async function proposeSynthesisTargets(dataset, traceStore, options = {}) {
7332
7342
  }
7333
7343
  for (const s of scenarios) {
7334
7344
  const sRuns = runs.filter((r) => r.scenarioId === s.id);
7335
- const scores2 = sRuns.map((r) => r.outcome?.score).filter((x) => typeof x === "number");
7336
- if (scores2.length < 3) continue;
7337
- const mean7 = scores2.reduce((a, b) => a + b, 0) / scores2.length;
7338
- const variance = scores2.reduce((a, b) => a + (b - mean7) ** 2, 0) / scores2.length;
7345
+ const scores = sRuns.map((r) => r.outcome?.score).filter((x) => typeof x === "number");
7346
+ if (scores.length < 3) continue;
7347
+ const mean6 = scores.reduce((a, b) => a + b, 0) / scores.length;
7348
+ const variance = scores.reduce((a, b) => a + (b - mean6) ** 2, 0) / scores.length;
7339
7349
  if (variance > varianceThreshold) {
7340
7350
  targets.push({
7341
7351
  reason: "high-variance",
@@ -7570,15 +7580,15 @@ async function runSelfPlay(proposer, scorer, targets, options = {}) {
7570
7580
  const rejected = [];
7571
7581
  const surviving = [];
7572
7582
  for (const candidate of proposed) {
7573
- const scores2 = await scorer.scoreCandidate(candidate, targets);
7574
- if (scores2.length < 2) {
7583
+ const scores = await scorer.scoreCandidate(candidate, targets);
7584
+ if (scores.length < 2) {
7575
7585
  rejected.push({ candidate, reason: "scorer returned <2 results" });
7576
7586
  continue;
7577
7587
  }
7578
- const values = scores2.map((s) => s.score);
7588
+ const values = scores.map((s) => s.score);
7579
7589
  const spread = Math.max(...values) - Math.min(...values);
7580
7590
  const maxScore = Math.max(...values);
7581
- scored.push({ candidate, scores: scores2, spread });
7591
+ scored.push({ candidate, scores, spread });
7582
7592
  if (maxScore < floor) {
7583
7593
  rejected.push({
7584
7594
  candidate,
@@ -8406,13 +8416,32 @@ var MultiLayerVerifier = class {
8406
8416
  ) : null;
8407
8417
  const layerStart = Date.now();
8408
8418
  let result;
8419
+ let rejectOnAbort;
8420
+ const abortPromise = new Promise((_resolve, reject) => {
8421
+ const rejectWithReason = () => {
8422
+ const reason = mergedSignal.reason;
8423
+ reject(reason instanceof Error ? reason : new Error(String(reason ?? "aborted")));
8424
+ };
8425
+ if (mergedSignal.aborted) {
8426
+ rejectWithReason();
8427
+ return;
8428
+ }
8429
+ mergedSignal.addEventListener("abort", rejectWithReason, { once: true });
8430
+ rejectOnAbort = () => mergedSignal.removeEventListener("abort", rejectWithReason);
8431
+ });
8409
8432
  try {
8410
- result = await layer.run({ env: opts.env, prior: { ...byName }, signal: mergedSignal });
8433
+ result = await Promise.race([
8434
+ Promise.resolve(
8435
+ layer.run({ env: opts.env, prior: { ...byName }, signal: mergedSignal })
8436
+ ),
8437
+ abortPromise
8438
+ ]);
8411
8439
  } catch (err) {
8412
8440
  const aborted = mergedSignal.aborted;
8413
8441
  result = {
8414
8442
  layer: layer.name,
8415
8443
  status: aborted ? "timeout" : "error",
8444
+ errorSource: layer.errorSource ?? "execution",
8416
8445
  durationMs: Date.now() - layerStart,
8417
8446
  findings: [
8418
8447
  {
@@ -8424,8 +8453,12 @@ var MultiLayerVerifier = class {
8424
8453
  reason: err instanceof Error ? err.message : String(err)
8425
8454
  };
8426
8455
  } finally {
8456
+ rejectOnAbort?.();
8427
8457
  if (layerTimer) clearTimeout(layerTimer);
8428
8458
  }
8459
+ if (result.status === "error" || result.status === "timeout") {
8460
+ result.errorSource ??= layer.errorSource ?? "execution";
8461
+ }
8429
8462
  result.findings = result.findings.map((f) => ({ ...f, layer: f.layer ?? layer.name }));
8430
8463
  results.push(result);
8431
8464
  byName[layer.name] = result;
@@ -8452,8 +8485,6 @@ function aggregate(layers, results, startedAt, startedAtMs) {
8452
8485
  let errorCount = 0;
8453
8486
  let scoredWeightSum = 0;
8454
8487
  let scoredWeightedTotal = 0;
8455
- let ranAnyScoredLayer = false;
8456
- let anyScoredLayerFailed = false;
8457
8488
  for (const r of results) {
8458
8489
  const weight = weightByName.get(r.layer) ?? 1;
8459
8490
  const failContrib = failContribByName.get(r.layer) ?? false;
@@ -8461,26 +8492,32 @@ function aggregate(layers, results, startedAt, startedAtMs) {
8461
8492
  else if (r.status === "fail") failCount++;
8462
8493
  else if (r.status === "skipped") skippedCount++;
8463
8494
  else errorCount++;
8464
- if (r.score != null && weight > 0) {
8495
+ if (isValidTaskMeasurement(r) && weight > 0) {
8465
8496
  if (r.status === "pass") {
8466
- ranAnyScoredLayer = true;
8467
8497
  scoredWeightSum += weight;
8468
8498
  scoredWeightedTotal += weight * r.score;
8469
8499
  } else if (r.status === "fail") {
8470
8500
  if (failContrib) {
8471
- ranAnyScoredLayer = true;
8472
8501
  scoredWeightSum += weight;
8473
8502
  scoredWeightedTotal += weight * r.score;
8474
8503
  }
8475
- anyScoredLayerFailed = true;
8476
8504
  }
8477
- } else if (r.status === "fail") {
8478
- anyScoredLayerFailed = true;
8479
8505
  }
8480
8506
  }
8481
8507
  const finishedAtMs = Date.now();
8482
- const allPass = ranAnyScoredLayer && !anyScoredLayerFailed && failCount === 0 && errorCount === 0;
8483
8508
  const blendedScore = scoredWeightSum > 0 ? scoredWeightedTotal / scoredWeightSum : 0;
8509
+ const resultByName = new Map(results.map((result) => [result.layer, result]));
8510
+ const completeTaskMeasurement = scoredWeightSum > 0 && results.length === layers.length && layers.every((layer) => {
8511
+ const result = resultByName.get(layer.name);
8512
+ if (!result) return false;
8513
+ if ((layer.weight ?? 1) <= 0) return true;
8514
+ if (result.status === "pass") {
8515
+ return result.score === void 0 || isValidTaskMeasurement(result);
8516
+ }
8517
+ return result.status === "fail" && layer.failContributesToScore === true && isValidTaskMeasurement(result);
8518
+ });
8519
+ const taskScore = completeTaskMeasurement && scoredWeightSum > 0 && Number.isFinite(blendedScore) ? blendedScore : void 0;
8520
+ const allPass = taskScore !== void 0 && results.length === layers.length && failCount === 0 && skippedCount === 0 && errorCount === 0;
8484
8521
  return {
8485
8522
  layers: results,
8486
8523
  passCount,
@@ -8489,13 +8526,17 @@ function aggregate(layers, results, startedAt, startedAtMs) {
8489
8526
  errorCount,
8490
8527
  allPass,
8491
8528
  blendedScore,
8529
+ ...taskScore === void 0 ? {} : { taskScore },
8492
8530
  valid: allPass,
8493
- score: blendedScore,
8531
+ score: taskScore ?? 0,
8494
8532
  durationMs: finishedAtMs - startedAtMs,
8495
8533
  startedAt,
8496
8534
  finishedAt: new Date(finishedAtMs).toISOString()
8497
8535
  };
8498
8536
  }
8537
+ function isValidTaskMeasurement(result) {
8538
+ return (result.status === "pass" || result.status === "fail") && typeof result.score === "number" && Number.isFinite(result.score) && result.score >= 0 && result.score <= 1;
8539
+ }
8499
8540
  function mergeSignals(a, b) {
8500
8541
  if (a.aborted) return a;
8501
8542
  if (b.aborted) return b;
@@ -8797,15 +8838,15 @@ function scoreReferenceReplay(scenarios, options = {}) {
8797
8838
  const threshold = options.matchThreshold ?? DEFAULT_MATCH_THRESHOLD;
8798
8839
  const matchStrategy = options.matchStrategy ?? "reference-order";
8799
8840
  const allowedSplits = new Set(options.splits ?? ALL_SPLITS);
8800
- const scores2 = scenarios.filter((scenario) => {
8841
+ const scores = scenarios.filter((scenario) => {
8801
8842
  const split = scenario.split ?? "train";
8802
8843
  if (split === "holdout" && !options.includeHoldout) return false;
8803
8844
  return allowedSplits.has(split);
8804
8845
  }).map((scenario) => scoreScenario(scenario, matcher, threshold, matchStrategy));
8805
8846
  return {
8806
- scenarios: scores2,
8807
- aggregate: aggregateScenarioScores(scores2),
8808
- bySplit: aggregateBySplit(scores2)
8847
+ scenarios: scores,
8848
+ aggregate: aggregateScenarioScores(scores),
8849
+ bySplit: aggregateBySplit(scores)
8809
8850
  };
8810
8851
  }
8811
8852
  function compareReferenceReplay(baseline, candidate) {
@@ -9028,20 +9069,20 @@ function buildScenarioScore(scenario, matches2, falsePositives) {
9028
9069
  matches: matches2
9029
9070
  };
9030
9071
  }
9031
- function aggregateBySplit(scores2) {
9072
+ function aggregateBySplit(scores) {
9032
9073
  const out = {};
9033
9074
  for (const split of ALL_SPLITS) {
9034
- const scoped = scores2.filter((score) => score.split === split);
9075
+ const scoped = scores.filter((score) => score.split === split);
9035
9076
  if (scoped.length > 0) out[split] = aggregateScenarioScores(scoped);
9036
9077
  }
9037
9078
  return out;
9038
9079
  }
9039
- function aggregateScenarioScores(scores2) {
9040
- const matched = sum2(scores2.map((score) => score.matched));
9041
- const total = sum2(scores2.map((score) => score.total));
9042
- const falsePositives = sum2(scores2.map((score) => score.falsePositives));
9043
- const matchedWeight = sum2(scores2.map((score) => score.matchedWeight));
9044
- const totalWeight = sum2(scores2.map((score) => score.totalWeight));
9080
+ function aggregateScenarioScores(scores) {
9081
+ const matched = sum2(scores.map((score) => score.matched));
9082
+ const total = sum2(scores.map((score) => score.total));
9083
+ const falsePositives = sum2(scores.map((score) => score.falsePositives));
9084
+ const matchedWeight = sum2(scores.map((score) => score.matchedWeight));
9085
+ const totalWeight = sum2(scores.map((score) => score.totalWeight));
9045
9086
  const precision2 = ratio(matched, matched + falsePositives);
9046
9087
  const recall = ratio(matched, total);
9047
9088
  return {
@@ -9305,18 +9346,16 @@ function createDefaultReviewer(options) {
9305
9346
 
9306
9347
  // src/description-length-gate.ts
9307
9348
  import { gzipSync } from "zlib";
9308
- function runScore3(run) {
9309
- const o = run.outcome;
9310
- const s = o.holdoutScore ?? o.searchScore ?? o.raw?.score;
9311
- return typeof s === "number" && Number.isFinite(s) ? s : void 0;
9349
+ function runScore2(run) {
9350
+ return runTaskScore(run);
9312
9351
  }
9313
9352
  function taskKey(run) {
9314
- return run.scenarioId ?? run.experimentId;
9353
+ return run.scenarioId;
9315
9354
  }
9316
9355
  function perTaskMeanScore(runs) {
9317
9356
  const acc = /* @__PURE__ */ new Map();
9318
9357
  for (const run of runs) {
9319
- const s = runScore3(run);
9358
+ const s = runScore2(run);
9320
9359
  if (s === void 0) continue;
9321
9360
  const key = taskKey(run);
9322
9361
  const cur = acc.get(key) ?? { sum: 0, n: 0 };
@@ -9564,55 +9603,82 @@ var HeldOutGate = class {
9564
9603
  }
9565
9604
  this.costPerTaskCeiling = config.costPerTaskCeiling;
9566
9605
  }
9567
- /** Decide whether `candidate` should replace `baseline`. Pairing
9568
- * is by (experimentId, seed) — identical experiment + seed pairs
9569
- * the candidate run with the matching baseline run. Pairs without
9570
- * a holdout score on both sides are dropped. */
9606
+ /** Decide whether `candidate` should replace `baseline`.
9607
+ * Pairing is by `(experimentId, scenarioId, seed)`.
9608
+ * Missing or duplicate identities throw instead of comparing by position. */
9571
9609
  evaluate(candidate, baseline) {
9572
9610
  const candidateId = inferCandidateId2(candidate, this.baselineKey);
9573
9611
  const baselineId = this.baselineKey;
9574
- const baselineHoldoutByKey = indexHoldoutByKey(baseline);
9575
- const beforeHoldout = [];
9576
- const afterHoldout = [];
9577
- for (const run of candidate) {
9578
- if (run.splitTag !== "holdout") continue;
9579
- if (run.outcome.holdoutScore === void 0) continue;
9580
- const key = pairKey(run);
9581
- const counterpart = baselineHoldoutByKey.get(key);
9582
- if (counterpart === void 0) continue;
9583
- beforeHoldout.push(counterpart);
9584
- afterHoldout.push(run.outcome.holdoutScore);
9585
- }
9612
+ assertScenarioIdentities([...candidate, ...baseline]);
9613
+ const candidateSearch = scoredRuns(candidate, "searchScore", "search");
9614
+ const baselineSearch = scoredRuns(baseline, "searchScore", "search");
9615
+ const candidateHoldout = scoredRuns(candidate, "holdoutScore", "holdout");
9616
+ const baselineHoldout = scoredRuns(baseline, "holdoutScore", "holdout");
9617
+ const searchPairing = pairRunRecords(baselineSearch, candidateSearch);
9618
+ const holdoutPairing = pairRunRecords(baselineHoldout, candidateHoldout);
9619
+ const beforeSearch = searchPairing.pairs.map((pair) => pair.baseline.outcome.searchScore);
9620
+ const afterSearch = searchPairing.pairs.map((pair) => pair.treatment.outcome.searchScore);
9621
+ const beforeHoldout = holdoutPairing.pairs.map((pair) => pair.baseline.outcome.holdoutScore);
9622
+ const afterHoldout = holdoutPairing.pairs.map((pair) => pair.treatment.outcome.holdoutScore);
9586
9623
  const productiveRuns = beforeHoldout.length;
9587
- const candidateSearchMean = mean6(scores(candidate, "searchScore", "search"));
9588
- const candidateHoldoutMean = mean6(scores(candidate, "holdoutScore", "holdout"));
9589
- const baselineSearchMean = mean6(scores(baseline, "searchScore", "search"));
9590
- const baselineHoldoutMean = mean6(scores(baseline, "holdoutScore", "holdout"));
9591
- const overfitGap = safeDiff(candidateSearchMean, candidateHoldoutMean);
9592
- const baselineOverfitGap = safeDiff(baselineSearchMean, baselineHoldoutMean);
9593
- const medianCandidateCost = medianFinite(candidate.map((r) => r.costUsd));
9594
- const medianBaselineCost = medianFinite(baseline.map((r) => r.costUsd));
9624
+ const candidateSearchMean = meanOrNull(afterSearch);
9625
+ const candidateHoldoutMean = meanOrNull(afterHoldout);
9626
+ const baselineSearchMean = meanOrNull(beforeSearch);
9627
+ const baselineHoldoutMean = meanOrNull(beforeHoldout);
9628
+ const overfitGap = diffOrNull(candidateSearchMean, candidateHoldoutMean);
9629
+ const baselineOverfitGap = diffOrNull(baselineSearchMean, baselineHoldoutMean);
9630
+ const medianCandidateCost = completeCostMedian(candidate);
9631
+ const medianBaselineCost = completeCostMedian(baseline);
9632
+ const commonEvidence = {
9633
+ productiveRuns,
9634
+ unpairedCandidateRuns: holdoutPairing.unpairedTreatment.length,
9635
+ unpairedBaselineRuns: holdoutPairing.unpairedBaseline.length,
9636
+ searchScore: candidateSearchMean,
9637
+ holdoutScore: candidateHoldoutMean,
9638
+ overfitGap,
9639
+ baselineOverfitGap,
9640
+ medianCandidateCost,
9641
+ medianBaselineCost
9642
+ };
9643
+ const missingSplitScores = [
9644
+ candidateSearch.length === 0 ? "candidate search" : null,
9645
+ candidateHoldout.length === 0 ? "candidate holdout" : null,
9646
+ baselineSearch.length === 0 ? "baseline search" : null,
9647
+ baselineHoldout.length === 0 ? "baseline holdout" : null
9648
+ ].filter((label) => label !== null);
9649
+ if (missingSplitScores.length > 0) {
9650
+ return {
9651
+ promote: false,
9652
+ candidateId,
9653
+ baselineId,
9654
+ evidence: {
9655
+ ...commonEvidence,
9656
+ medianPairedDelta: productiveRuns > 0 ? medianDelta(beforeHoldout, afterHoldout) : null,
9657
+ pairedCI: null,
9658
+ pairedPValue: null
9659
+ },
9660
+ reason: `missing_split_scores: ${missingSplitScores.join(", ")} score evidence is absent`,
9661
+ rejectionCode: "missing_split_scores"
9662
+ };
9663
+ }
9595
9664
  if (productiveRuns < this.minProductiveRuns) {
9596
9665
  return {
9597
9666
  promote: false,
9598
9667
  candidateId,
9599
9668
  baselineId,
9600
9669
  evidence: {
9601
- productiveRuns,
9602
- medianPairedDelta: productiveRuns > 0 ? medianDelta(beforeHoldout, afterHoldout) : 0,
9603
- pairedCI: { low: 0, high: 0 },
9604
- pairedPValue: 1,
9605
- searchScore: candidateSearchMean,
9606
- holdoutScore: candidateHoldoutMean,
9607
- overfitGap,
9608
- baselineOverfitGap,
9609
- medianCandidateCost,
9610
- medianBaselineCost
9670
+ ...commonEvidence,
9671
+ medianPairedDelta: productiveRuns > 0 ? medianDelta(beforeHoldout, afterHoldout) : null,
9672
+ pairedCI: null,
9673
+ pairedPValue: null
9611
9674
  },
9612
9675
  reason: `few_runs: ${productiveRuns} paired holdout observation(s) < min ${this.minProductiveRuns}`,
9613
9676
  rejectionCode: "few_runs"
9614
9677
  };
9615
9678
  }
9679
+ if (overfitGap === null || baselineOverfitGap === null) {
9680
+ throw new Error("HeldOutGate: complete split scores did not produce overfit gaps");
9681
+ }
9616
9682
  const ci = pairedBootstrap(beforeHoldout, afterHoldout, {
9617
9683
  confidence: this.confidence,
9618
9684
  resamples: this.resamples,
@@ -9621,16 +9687,10 @@ var HeldOutGate = class {
9621
9687
  });
9622
9688
  const wilcoxon = wilcoxonSignedRank(beforeHoldout, afterHoldout);
9623
9689
  const evidence = {
9624
- productiveRuns,
9690
+ ...commonEvidence,
9625
9691
  medianPairedDelta: ci.median,
9626
9692
  pairedCI: { low: ci.low, high: ci.high },
9627
- pairedPValue: wilcoxon.p,
9628
- searchScore: candidateSearchMean,
9629
- holdoutScore: candidateHoldoutMean,
9630
- overfitGap,
9631
- baselineOverfitGap,
9632
- medianCandidateCost,
9633
- medianBaselineCost
9693
+ pairedPValue: wilcoxon.p
9634
9694
  };
9635
9695
  if (!(ci.low > this.pairedDeltaThreshold)) {
9636
9696
  return {
@@ -9642,7 +9702,7 @@ var HeldOutGate = class {
9642
9702
  rejectionCode: "negative_delta"
9643
9703
  };
9644
9704
  }
9645
- if (Number.isFinite(overfitGap) && Number.isFinite(baselineOverfitGap) && overfitGap > baselineOverfitGap + this.overfitGapThreshold) {
9705
+ if (overfitGap > baselineOverfitGap + this.overfitGapThreshold) {
9646
9706
  return {
9647
9707
  promote: false,
9648
9708
  candidateId,
@@ -9652,7 +9712,17 @@ var HeldOutGate = class {
9652
9712
  rejectionCode: "overfit_gap"
9653
9713
  };
9654
9714
  }
9655
- if (this.costPerTaskCeiling !== void 0 && Number.isFinite(medianCandidateCost) && medianCandidateCost > this.costPerTaskCeiling) {
9715
+ if (this.costPerTaskCeiling !== void 0 && medianCandidateCost === null) {
9716
+ return {
9717
+ promote: false,
9718
+ candidateId,
9719
+ baselineId,
9720
+ evidence,
9721
+ reason: "missing_cost: candidate cost evidence is incomplete",
9722
+ rejectionCode: "missing_cost"
9723
+ };
9724
+ }
9725
+ if (this.costPerTaskCeiling !== void 0 && medianCandidateCost !== null && medianCandidateCost > this.costPerTaskCeiling) {
9656
9726
  return {
9657
9727
  promote: false,
9658
9728
  candidateId,
@@ -9678,49 +9748,50 @@ function inferCandidateId2(candidate, baselineKey) {
9678
9748
  }
9679
9749
  return candidate[0]?.candidateId ?? "(unknown candidate)";
9680
9750
  }
9681
- function indexHoldoutByKey(runs) {
9682
- const out = /* @__PURE__ */ new Map();
9683
- for (const r of runs) {
9684
- if (r.splitTag !== "holdout") continue;
9685
- if (r.outcome.holdoutScore === void 0) continue;
9686
- out.set(pairKey(r), r.outcome.holdoutScore);
9751
+ function assertScenarioIdentities(runs) {
9752
+ for (const run of runs) {
9753
+ if (typeof run.scenarioId !== "string" || run.scenarioId.trim() === "") {
9754
+ throw new Error(`HeldOutGate: run ${run.runId} is missing scenarioId`);
9755
+ }
9687
9756
  }
9688
- return out;
9689
- }
9690
- function pairKey(r) {
9691
- return `${r.experimentId}::${r.seed}`;
9692
9757
  }
9693
- function scores(runs, field, splitFilter) {
9694
- const out = [];
9695
- for (const r of runs) {
9696
- if (r.splitTag !== splitFilter) continue;
9697
- const v = r.outcome[field];
9698
- if (typeof v === "number" && Number.isFinite(v)) out.push(v);
9699
- }
9700
- return out;
9758
+ function scoredRuns(runs, field, splitFilter) {
9759
+ return runs.filter(
9760
+ (run) => run.splitTag === splitFilter && typeof run.outcome[field] === "number" && Number.isFinite(run.outcome[field])
9761
+ );
9701
9762
  }
9702
- function mean6(xs) {
9703
- if (xs.length === 0) return Number.NaN;
9763
+ function meanOrNull(xs) {
9764
+ if (xs.length === 0) return null;
9704
9765
  return xs.reduce((s, x) => s + x, 0) / xs.length;
9705
9766
  }
9706
- function safeDiff(a, b) {
9707
- if (!Number.isFinite(a) || !Number.isFinite(b)) return Number.NaN;
9767
+ function diffOrNull(a, b) {
9768
+ if (a === null || b === null) return null;
9708
9769
  return a - b;
9709
9770
  }
9710
9771
  function medianDelta(before, after) {
9711
9772
  const ds = before.map((b, i) => after[i] - b).sort((x, y) => x - y);
9712
- if (ds.length === 0) return 0;
9773
+ if (ds.length === 0) throw new Error("HeldOutGate: median delta requires at least one pair");
9713
9774
  const mid = Math.floor(ds.length / 2);
9714
9775
  return ds.length % 2 === 0 ? (ds[mid - 1] + ds[mid]) / 2 : ds[mid];
9715
9776
  }
9716
9777
  function medianFinite(xs) {
9717
9778
  const ys = xs.filter((x) => Number.isFinite(x)).sort((x, y) => x - y);
9718
- if (ys.length === 0) return Number.NaN;
9779
+ if (ys.length === 0) return null;
9719
9780
  const mid = Math.floor(ys.length / 2);
9720
9781
  return ys.length % 2 === 0 ? (ys[mid - 1] + ys[mid]) / 2 : ys[mid];
9721
9782
  }
9783
+ function completeCostMedian(runs) {
9784
+ if (runs.length === 0) return null;
9785
+ const costs = [];
9786
+ for (const run of runs) {
9787
+ const provenance = run.costProvenance;
9788
+ if (provenance.kind === "uncaptured") return null;
9789
+ costs.push(provenance.usd);
9790
+ }
9791
+ return medianFinite(costs);
9792
+ }
9722
9793
  function fmt(x) {
9723
- if (!Number.isFinite(x)) return String(x);
9794
+ if (x === null) return "n/a";
9724
9795
  return x.toFixed(4);
9725
9796
  }
9726
9797
 
@@ -9739,9 +9810,9 @@ function passOrthogonality(input) {
9739
9810
  sims.push(cosineSimilarity(vectors[i], vectors[j]));
9740
9811
  }
9741
9812
  }
9742
- const mean7 = sims.length === 0 ? 0 : sims.reduce((a, b) => a + b, 0) / sims.length;
9813
+ const mean6 = sims.length === 0 ? 0 : sims.reduce((a, b) => a + b, 0) / sims.length;
9743
9814
  return {
9744
- orthogonality: Math.max(0, Math.min(1, 1 - mean7)),
9815
+ orthogonality: Math.max(0, Math.min(1, 1 - mean6)),
9745
9816
  passCount: passes.length,
9746
9817
  similarities: sims
9747
9818
  };
@@ -10255,17 +10326,17 @@ function traceJudge(judge, judgeName, opts) {
10255
10326
  }
10256
10327
  });
10257
10328
  try {
10258
- const scores2 = await judge(tc, input);
10259
- const composite = scores2.length > 0 ? scores2.reduce((sum4, s) => sum4 + s.score, 0) / scores2.length : 0;
10329
+ const scores = await judge(tc, input);
10330
+ const composite = scores.length > 0 ? scores.reduce((sum4, s) => sum4 + s.score, 0) / scores.length : 0;
10260
10331
  await span.end({
10261
10332
  attributes: {
10262
10333
  "judge.name": judgeName,
10263
10334
  "judge.composite_score": composite,
10264
- "judge.dimension_count": scores2.length,
10335
+ "judge.dimension_count": scores.length,
10265
10336
  "eval.phase": "judge"
10266
10337
  }
10267
10338
  });
10268
- return scores2;
10339
+ return scores;
10269
10340
  } catch (err) {
10270
10341
  await span.fail(err instanceof Error ? err : String(err));
10271
10342
  throw err;
@@ -10294,8 +10365,8 @@ function traceJudgeEnsemble(judges, judgeNames, opts) {
10294
10365
  parentSpanId: ensembleSpan.span.spanId
10295
10366
  });
10296
10367
  try {
10297
- const scores2 = await tracedFn(tc, input);
10298
- allScores.push(...scores2);
10368
+ const scores = await tracedFn(tc, input);
10369
+ allScores.push(...scores);
10299
10370
  } catch (err) {
10300
10371
  if (!(err instanceof JudgeParseError)) throw err;
10301
10372
  failedJudges++;
@@ -10556,10 +10627,16 @@ function splitOf(record, opts) {
10556
10627
  return "practice";
10557
10628
  }
10558
10629
  function scoreOf(record) {
10559
- const score = record.outcome.holdoutScore ?? record.outcome.searchScore;
10560
- if (typeof score === "number" && Number.isFinite(score)) return clamp014(score);
10561
- const rawScore = record.outcome.raw.score ?? record.outcome.raw.composite;
10562
- return typeof rawScore === "number" && Number.isFinite(rawScore) ? clamp014(rawScore) : 0;
10630
+ const score = runTaskScore(record);
10631
+ if (score !== void 0) return clamp014(score);
10632
+ if ((record.outcome.judgeScores?.failedJudges?.length ?? 0) > 0 || (record.outcome.raw.judge_error_count ?? 0) > 0) {
10633
+ throw new ValidationError(
10634
+ `Run ${record.runId} has incomplete judge evidence; product benchmark rows require a complete task score`
10635
+ );
10636
+ }
10637
+ throw new ValidationError(
10638
+ `Run ${record.runId} has no task score; product benchmark rows require an explicit score`
10639
+ );
10563
10640
  }
10564
10641
  function rawPassOf(record) {
10565
10642
  const rawPass = record.outcome.raw.pass;
@@ -10569,17 +10646,28 @@ function rawPassOf(record) {
10569
10646
  }
10570
10647
  function passOf(record, score, threshold) {
10571
10648
  const rawPass = rawPassOf(record);
10572
- if (rawPass !== null) return rawPass && !record.failureMode;
10573
- return score >= threshold && !record.failureMode;
10649
+ const hasTaskFailure = record.failureClass !== void 0 && record.failureClass !== "success";
10650
+ if (rawPass !== null) return rawPass && !hasTaskFailure;
10651
+ return score >= threshold && !hasTaskFailure;
10574
10652
  }
10575
- function failureModeOf(record, score, threshold) {
10576
- if (record.failureMode) return record.failureMode;
10653
+ function failureOf(record, score, threshold) {
10654
+ if (record.failureClass !== void 0 && record.failureClass !== "success") {
10655
+ return {
10656
+ failureClass: record.failureClass,
10657
+ failureDetail: record.failureMode ?? null
10658
+ };
10659
+ }
10577
10660
  const belowThreshold = `quality-below-threshold: ${Math.round(score * 100)}% < ${Math.round(threshold * 100)}%`;
10578
10661
  if (rawPassOf(record) === false) {
10579
- return score < threshold ? belowThreshold : "product-pass-failed";
10662
+ return {
10663
+ failureClass: "unknown",
10664
+ failureDetail: score < threshold ? belowThreshold : "product-pass-failed"
10665
+ };
10580
10666
  }
10581
- if (passOf(record, score, threshold)) return null;
10582
- return belowThreshold;
10667
+ if (passOf(record, score, threshold)) {
10668
+ return { failureClass: null, failureDetail: null };
10669
+ }
10670
+ return { failureClass: "unknown", failureDetail: belowThreshold };
10583
10671
  }
10584
10672
  function numericDimensions(record) {
10585
10673
  const dimensions = {};
@@ -10696,18 +10784,25 @@ function runRecordToProductBenchmarkRecord(record, runDir, artifactRoot, artifac
10696
10784
  const opts = resolveOptions(options);
10697
10785
  const runtime = runtimeResolution(record, opts);
10698
10786
  const score = scoreOf(record);
10787
+ const failure = failureOf(record, score, opts.passThreshold);
10699
10788
  const armId = armIdOf(record);
10700
10789
  const inputTokens = record.tokenUsage.input;
10701
10790
  const outputTokens = record.tokenUsage.output;
10702
10791
  const toolCallCount = toolCallsOf(record, runDir, opts);
10703
10792
  const dimensions = numericDimensions(record);
10793
+ if (record.costUsd === null) {
10794
+ throw new ValidationError(
10795
+ `run '${record.runId}' has no USD cost; product benchmark exports require priced runs`
10796
+ );
10797
+ }
10798
+ const costUsd = record.costUsd;
10704
10799
  if (!("tool_calls" in dimensions)) dimensions.tool_calls = toolCallCount;
10705
10800
  const product = {
10706
10801
  schemaVersion: 1,
10707
10802
  projectId: opts.projectId,
10708
10803
  benchmarkId: opts.benchmarkId,
10709
10804
  runId: record.runId,
10710
- scenarioId: record.scenarioId ?? record.experimentId,
10805
+ scenarioId: record.scenarioId,
10711
10806
  split: splitOf(record, opts),
10712
10807
  armId,
10713
10808
  rep: Number(record.seed ?? 0) + 1,
@@ -10724,12 +10819,12 @@ function runRecordToProductBenchmarkRecord(record, runDir, artifactRoot, artifac
10724
10819
  pass: passOf(record, score, opts.passThreshold),
10725
10820
  score,
10726
10821
  dimensions,
10727
- failureMode: failureModeOf(record, score, opts.passThreshold)
10822
+ ...failure
10728
10823
  },
10729
10824
  usage: {
10730
10825
  inputTokens,
10731
10826
  outputTokens,
10732
- costUsd: record.costUsd,
10827
+ costUsd,
10733
10828
  // Rounded: the bundle contract requires integer milliseconds.
10734
10829
  wallMs: Math.round(record.wallMs),
10735
10830
  toolCalls: toolCallCount
@@ -10739,7 +10834,7 @@ function runRecordToProductBenchmarkRecord(record, runDir, artifactRoot, artifac
10739
10834
  rawCapture: existsArtifact(artifactRoot, artifacts.raws),
10740
10835
  traceCapture: existsArtifact(artifactRoot, artifacts.traces),
10741
10836
  noStubRows: inputTokens + outputTokens > 0,
10742
- priced: record.costUsd > 0,
10837
+ priced: costUsd > 0,
10743
10838
  profileMaterialized: Boolean(record.agentProfile?.cellId)
10744
10839
  },
10745
10840
  artifacts
@@ -10939,6 +11034,13 @@ function expectString(value, path) {
10939
11034
  fail(path, "must be a non-empty string");
10940
11035
  return value;
10941
11036
  }
11037
+ function expectFailureClass(value, path) {
11038
+ const parsed = expectString(value, path);
11039
+ if (parsed === "success" || !FAILURE_CLASSES.includes(parsed)) {
11040
+ fail(path, `expected a non-success FailureClass, received ${JSON.stringify(parsed)}`);
11041
+ }
11042
+ return parsed;
11043
+ }
10942
11044
  function expectBoolean(value, path) {
10943
11045
  if (typeof value !== "boolean") fail(path, "must be a boolean");
10944
11046
  return value;
@@ -11081,6 +11183,18 @@ function validateProductBenchmarkRecord(value) {
11081
11183
  const usage = expectObject(obj.usage, "record.usage");
11082
11184
  const integrity = expectObject(obj.integrity, "record.integrity");
11083
11185
  const artifacts = expectObject(obj.artifacts, "record.artifacts");
11186
+ const outcomePass = expectBoolean(outcome.pass, "record.outcome.pass");
11187
+ const failureClass = outcome.failureClass === null ? null : expectFailureClass(outcome.failureClass, "record.outcome.failureClass");
11188
+ const failureDetail = outcome.failureDetail === null ? null : expectString(outcome.failureDetail, "record.outcome.failureDetail");
11189
+ if (outcomePass && (failureClass !== null || failureDetail !== null)) {
11190
+ fail("record.outcome", "a passing row cannot carry failure evidence");
11191
+ }
11192
+ if (!outcomePass && failureClass === null) {
11193
+ fail("record.outcome.failureClass", "a failed row requires a failure class");
11194
+ }
11195
+ if (failureClass === null && failureDetail !== null) {
11196
+ fail("record.outcome.failureDetail", "failure detail requires a failure class");
11197
+ }
11084
11198
  const record = {
11085
11199
  schemaVersion: 1,
11086
11200
  projectId: expectString(obj.projectId, "record.projectId"),
@@ -11106,10 +11220,11 @@ function validateProductBenchmarkRecord(value) {
11106
11220
  version: expectString(backend.version, "record.backend.version")
11107
11221
  },
11108
11222
  outcome: {
11109
- pass: expectBoolean(outcome.pass, "record.outcome.pass"),
11223
+ pass: outcomePass,
11110
11224
  score: expectNumber(outcome.score, "record.outcome.score", { min: 0, max: 1 }),
11111
11225
  dimensions: expectDimensions(outcome.dimensions, "record.outcome.dimensions"),
11112
- failureMode: outcome.failureMode === null ? null : expectString(outcome.failureMode, "record.outcome.failureMode")
11226
+ failureClass,
11227
+ failureDetail
11113
11228
  },
11114
11229
  usage: {
11115
11230
  inputTokens: expectNumber(usage.inputTokens, "record.usage.inputTokens", {
@@ -11379,7 +11494,6 @@ export {
11379
11494
  REFERENCE_EQUIVALENCE_INPUT_LIMITS,
11380
11495
  REFERENCE_EQUIVALENCE_JUDGE_VERSION,
11381
11496
  RESEARCH_REPORT_HARD_PAIR_FLOOR,
11382
- ROLLOUT_FORMAT,
11383
11497
  ROLLOUT_SCHEMA,
11384
11498
  RUN_COST_ATTR_KEYS,
11385
11499
  ReplayCache,
@@ -11498,6 +11612,7 @@ export {
11498
11612
  checkTraceContracts,
11499
11613
  clamp01,
11500
11614
  classifyFailure,
11615
+ classifyOtlpSpanRole,
11501
11616
  classifyTreatment,
11502
11617
  claudeCodeSupervisorRunReader,
11503
11618
  cliffsDelta,
@@ -11661,6 +11776,7 @@ export {
11661
11776
  isLlmSpan,
11662
11777
  isModelPriced,
11663
11778
  isOtelConfigured,
11779
+ isOtlpModelCall,
11664
11780
  isRetrievalSpan,
11665
11781
  isRolloutLine,
11666
11782
  isRunRecord,
@@ -11719,7 +11835,9 @@ export {
11719
11835
  otlpToRunRecords,
11720
11836
  otlpToTraceRunRecords,
11721
11837
  pairArms,
11838
+ pairRunRecords,
11722
11839
  pairedBootstrap,
11840
+ pairedCohensDz,
11723
11841
  pairedEvalueSequence,
11724
11842
  pairedMde,
11725
11843
  pairedRiskDifference,
@@ -11788,10 +11906,10 @@ export {
11788
11906
  replayScorerOverCorpus,
11789
11907
  replayTraceThroughJudge,
11790
11908
  requireAgentProfileCell,
11909
+ requiredPairedSampleSize,
11791
11910
  requiredSampleSize,
11792
11911
  researchReport,
11793
11912
  resolveModelPricing,
11794
- resolveRunCostProvenance,
11795
11913
  resolveSeat,
11796
11914
  rolloutReward,
11797
11915
  rollupSupervisorRuns,
@@ -11822,6 +11940,7 @@ export {
11822
11940
  runScore,
11823
11941
  runSelfPlay,
11824
11942
  runSemanticConceptJudge,
11943
+ runTaskScore,
11825
11944
  runTestGradedScenario,
11826
11945
  runsForScenario,
11827
11946
  scalarScore,