@tangle-network/agent-eval 0.121.0 → 0.122.1

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.
@@ -5,8 +5,9 @@ import {
5
5
  fromOpenCodeSession,
6
6
  fromPiSession,
7
7
  fromPigraphSession,
8
+ observeCodeAgentSession,
8
9
  parseCodeAgentJsonl
9
- } from "../chunk-HKUCJ437.js";
10
+ } from "../chunk-7VYEGHWF.js";
10
11
  import {
11
12
  createHostedClient
12
13
  } from "../chunk-ZZUXHH3R.js";
@@ -18,10 +19,6 @@ import {
18
19
  REFERENCE_EQUIVALENCE_INPUT_LIMITS,
19
20
  REFERENCE_EQUIVALENCE_JUDGE_VERSION,
20
21
  buildEvidenceVector,
21
- buildLoopProvenanceRecord,
22
- campaignBreakdown,
23
- campaignMeanComposite,
24
- canonicalDigest,
25
22
  composeGate,
26
23
  createReferenceEquivalenceJudge,
27
24
  defaultProductionGate,
@@ -29,23 +26,18 @@ import {
29
26
  evolutionaryProposer,
30
27
  gepaProposer,
31
28
  heldOutGate,
29
+ heldoutSignificance,
32
30
  loopProvenanceArgsFromResult,
33
- pairArms,
34
31
  paretoPolicy,
35
32
  paretoSignificanceGate,
36
33
  powerPreflight,
37
- renderSurfaceDiff,
38
34
  runEval,
39
35
  runImprovementLoop,
40
36
  runReferenceEquivalenceJudge,
41
37
  surfaceContentHash,
42
- surfaceHash,
43
- verifyLoopProvenanceRecord
44
- } from "../chunk-32BZXMSO.js";
38
+ surfaceHash
39
+ } from "../chunk-VZ6VKOJT.js";
45
40
  import {
46
- assertCampaignSplitIdentity,
47
- assertRealAgentReceipts,
48
- campaignCoverage,
49
41
  campaignSplitDigest,
50
42
  createRunCostLedger,
51
43
  fsCampaignStorage,
@@ -69,9 +61,7 @@ import "../chunk-DPZAEKA6.js";
69
61
  import {
70
62
  pairedBootstrap
71
63
  } from "../chunk-PJQFMIOX.js";
72
- import {
73
- CostLedger
74
- } from "../chunk-JHOJHHU7.js";
64
+ import "../chunk-JHOJHHU7.js";
75
65
  import "../chunk-VI2UW6B6.js";
76
66
  import {
77
67
  recordAggregateMeasurements,
@@ -548,554 +538,605 @@ function requirePositiveInteger(value, field) {
548
538
 
549
539
  // src/contract/measured-comparison.ts
550
540
  import {
551
- agentImprovementMeasuredComparisonSchema
541
+ agentCandidateBenchmarkSuiteSchema,
542
+ agentCandidateBenchmarkTaskSchema,
543
+ agentCandidateBundleSchema,
544
+ agentCandidateExperimentSchema,
545
+ agentImprovementMeasuredComparisonSchema,
546
+ candidateExecutionEvidenceSchema,
547
+ canonicalCandidateDigest,
548
+ omitTopLevelDigest
552
549
  } from "@tangle-network/agent-interface";
553
- function measuredComparisonFromSelfImproveResult(options) {
554
- const { result } = options;
555
- verifyLoopProvenanceRecord(result.provenance);
556
- const power = result.power;
557
- if (!power) throw new Error("agent improvement comparison requires heldout power analysis");
558
- if (result.provenance.gate.reasons.length === 0) {
559
- throw new Error("agent improvement comparison requires measured decision reasons");
560
- }
561
- const receiptLedger = new CostLedger({ receipts: result.receipts });
562
- const receiptCost = receiptLedger.summary();
563
- const receipts = receiptLedger.list();
564
- assertRealAgentReceipts(receipts, { allowMixed: false });
565
- const receiptsById = new Map(receipts.map((receipt) => [receipt.callId, receipt]));
566
- const receiptIdsByCell = indexReceiptIdsByCell(receipts);
567
- assertCompleteMeasuredCampaign(
568
- result.raw.baselineOnHoldout,
569
- "heldout baseline",
570
- receiptsById,
571
- receiptIdsByCell
572
- );
573
- assertCompleteMeasuredCampaign(
574
- result.raw.winnerOnHoldout,
575
- "heldout candidate",
576
- receiptsById,
577
- receiptIdsByCell
550
+ function sealCandidateBenchmarkTask(material) {
551
+ return agentCandidateBenchmarkTaskSchema.parse({
552
+ ...material,
553
+ digest: canonicalCandidateDigest(material)
554
+ });
555
+ }
556
+ function sealCandidateBenchmarkSuite(options) {
557
+ for (const task of options.tasks) verifyCandidateBenchmarkTask(task);
558
+ const material = {
559
+ kind: "agent-candidate-benchmark-suite",
560
+ digestAlgorithm: "rfc8785-sha256",
561
+ taskDigests: options.tasks.map((task) => task.digest),
562
+ reps: options.reps,
563
+ seeds: options.seeds
564
+ };
565
+ const suite = agentCandidateBenchmarkSuiteSchema.parse({
566
+ ...material,
567
+ digest: canonicalCandidateDigest(material)
568
+ });
569
+ return { suite, tasks: options.tasks };
570
+ }
571
+ function sealCandidateExperiment(material) {
572
+ const parsed = agentCandidateExperimentSchema.parse({
573
+ ...material,
574
+ digest: canonicalCandidateDigest(material)
575
+ });
576
+ return verifyCandidateExperiment(parsed);
577
+ }
578
+ function verifyCandidateExperiment(input) {
579
+ const experiment = agentCandidateExperimentSchema.parse(input);
580
+ verifySelfAddressed(experiment, "candidate experiment");
581
+ verifyBundle(experiment.baseline, "baseline bundle");
582
+ verifyBundle(experiment.candidate, "candidate bundle");
583
+ if (experiment.baseline.digest === experiment.candidate.digest) {
584
+ throw new Error("candidate experiment baseline and candidate bundles are identical");
585
+ }
586
+ verifyCandidateBenchmarkSuiteInputs(experiment.benchmark);
587
+ return experiment;
588
+ }
589
+ async function runCandidateExperiment(options) {
590
+ const experiment = verifyCandidateExperiment(options.experiment);
591
+ const { suite, tasks } = experiment.benchmark;
592
+ const maxConcurrency = options.maxConcurrency ?? 2;
593
+ if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1) {
594
+ throw new Error("candidate experiment maxConcurrency must be a positive integer");
595
+ }
596
+ const measurements = new Array(
597
+ suite.taskDigests.length * suite.reps
578
598
  );
579
- const pairs = pairMeasuredCells(
580
- result.raw.baselineOnHoldout.cells,
581
- result.raw.winnerOnHoldout.cells
599
+ let nextIndex = 0;
600
+ const lanes = Array.from({ length: Math.min(maxConcurrency, measurements.length) }, async () => {
601
+ while (true) {
602
+ if (options.signal?.aborted) throw abortError(options.signal);
603
+ const index = nextIndex;
604
+ nextIndex += 1;
605
+ if (index >= measurements.length) return;
606
+ const taskIndex = Math.floor(index / suite.reps);
607
+ const repetition = index % suite.reps;
608
+ const task = tasks[taskIndex];
609
+ const seed = suite.seeds[index];
610
+ if (!task || seed === void 0) {
611
+ throw new Error(`candidate experiment cell ${index} has no signed task or seed`);
612
+ }
613
+ const benchmarkCell = {
614
+ suiteDigest: suite.digest,
615
+ taskIndex,
616
+ repetition
617
+ };
618
+ const [baseline, candidate] = await Promise.all([
619
+ options.execute({
620
+ experiment,
621
+ arm: "baseline",
622
+ bundle: experiment.baseline,
623
+ task,
624
+ benchmarkCell,
625
+ seed,
626
+ ...options.signal ? { signal: options.signal } : {}
627
+ }),
628
+ options.execute({
629
+ experiment,
630
+ arm: "candidate",
631
+ bundle: experiment.candidate,
632
+ task,
633
+ benchmarkCell,
634
+ seed,
635
+ ...options.signal ? { signal: options.signal } : {}
636
+ })
637
+ ]);
638
+ const measurement = { baseline, candidate };
639
+ verifyMeasurement(experiment, measurement, index);
640
+ measurements[index] = measurement;
641
+ }
642
+ });
643
+ await Promise.all(lanes);
644
+ return measurements;
645
+ }
646
+ function measuredComparisonFromCandidateExperiment(options) {
647
+ const experiment = verifyCandidateExperiment(options.experiment);
648
+ const measurements = options.measurements.map(
649
+ (measurement, index) => verifyMeasurement(experiment, measurement, index)
582
650
  );
583
- const composite = measuredObjective(
651
+ const expectedN = experiment.benchmark.suite.taskDigests.length * experiment.benchmark.suite.reps;
652
+ if (measurements.length !== expectedN) {
653
+ throw new Error(
654
+ `candidate experiment is incomplete (${measurements.length}/${expectedN} paired cells)`
655
+ );
656
+ }
657
+ verifyStableProfileMaterialization(measurements);
658
+ if (!options.runId.trim()) throw new Error("candidate experiment runId is required");
659
+ const {
660
+ confidenceLevel: confidence,
661
+ resamples,
662
+ bootstrapSeed,
663
+ deltaThreshold,
664
+ minProductiveRuns,
665
+ budgetUsd,
666
+ criticalDimensions,
667
+ regressionTolerance
668
+ } = experiment.policy;
669
+ const baselineScores = measurements.map(scoreOf("baseline"));
670
+ const candidateScores = measurements.map(scoreOf("candidate"));
671
+ const overall = measuredEstimate(baselineScores, candidateScores, {
672
+ confidence,
673
+ resamples,
674
+ seed: bootstrapSeed
675
+ });
676
+ const dimensions = sharedDimensions(measurements);
677
+ const objectives = [
584
678
  {
585
679
  kind: "objective",
586
- name: "composite",
680
+ name: "benchmark-score",
587
681
  direction: "higher-is-better",
588
- unit: "score"
682
+ unit: "score",
683
+ availability: "measured",
684
+ ...overall
589
685
  },
590
- pairs,
591
- measuredComposite
686
+ ...dimensions.map((name, index) => ({
687
+ kind: "dimension",
688
+ objective: "benchmark-score",
689
+ name,
690
+ direction: "higher-is-better",
691
+ unit: "score",
692
+ availability: "measured",
693
+ ...measuredEstimate(
694
+ measurements.map(dimensionOf("baseline", name)),
695
+ measurements.map(dimensionOf("candidate", name)),
696
+ { confidence, resamples, seed: bootstrapSeed + index + 1 }
697
+ )
698
+ }))
699
+ ];
700
+ const cost = measuredEstimate(
701
+ measurements.map(costOf("baseline")),
702
+ measurements.map(costOf("candidate")),
703
+ { confidence, resamples, seed: bootstrapSeed + dimensions.length + 1 }
592
704
  );
593
- assertMeasuredNumber(result.lift, composite.delta, "heldout lift");
594
- assertMeasuredNumber(result.baseline.compositeMean, composite.baseline, "heldout baseline");
595
- assertMeasuredNumber(result.winner.compositeMean, composite.candidate, "heldout candidate");
596
- const baselineContentHash = surfaceContentHash(options.baselineSurface);
597
- const candidateContentHash = surfaceContentHash(result.winner.surface);
598
- const canonicalDiff = baselineContentHash === candidateContentHash ? "" : renderSurfaceDiff(result.raw.winnerSurface, options.baselineSurface);
599
- assertMeasuredIdentity(result.gateDecision, result.raw.gateResult.decision, "raw decision");
600
- assertMeasuredIdentity(
601
- candidateContentHash,
602
- surfaceContentHash(result.raw.winnerSurface),
603
- "raw winner surface"
705
+ const latency = measuredEstimate(
706
+ measurements.map(latencyOf("baseline")),
707
+ measurements.map(latencyOf("candidate")),
708
+ { confidence, resamples, seed: bootstrapSeed + dimensions.length + 2 }
604
709
  );
605
- assertMeasuredIdentity(result.diff, canonicalDiff, "surface diff");
606
- assertMeasuredIdentity(result.raw.promotedDiff, canonicalDiff, "raw surface diff");
607
- if (result.gateDecision === "ship" && (baselineContentHash === candidateContentHash || result.diff.trim().length === 0)) {
608
- throw new Error("a shipped improvement requires a changed surface and non-empty diff");
609
- }
610
- assertMeasuredIdentity(
611
- canonicalDigest(power),
612
- canonicalDigest(
613
- powerPreflight({
614
- baselineComposites: pairs.map(([cell]) => measuredComposite(cell)),
615
- sharedScorerChannel: true
616
- })
617
- ),
618
- "power analysis"
619
- );
620
- assertMeasuredNumber(power.n, composite.n, "power sample size");
621
- assertMeasuredNumber(power.confidence, 0.95, "power confidence");
622
- assertCompleteMeasuredCampaign(
623
- result.raw.baselineCampaign,
624
- "search baseline",
625
- receiptsById,
626
- receiptIdsByCell
710
+ objectives.push(
711
+ {
712
+ kind: "cost",
713
+ name: "cost",
714
+ direction: "lower-is-better",
715
+ unit: "usd",
716
+ availability: "measured",
717
+ ...cost
718
+ },
719
+ {
720
+ kind: "latency",
721
+ name: "latency",
722
+ direction: "lower-is-better",
723
+ unit: "milliseconds",
724
+ availability: "measured",
725
+ ...latency
726
+ }
627
727
  );
628
- assertGenerationMeasurements(result.raw.generations, receiptsById, receiptIdsByCell);
629
- if (result.raw.neutralizedOnHoldout === void 0 !== (result.raw.neutralizedSurface === void 0)) {
630
- throw new Error("neutralized surface and campaign must be supplied together");
631
- }
632
- if (result.raw.neutralizedOnHoldout) {
633
- assertCompleteMeasuredCampaign(
634
- result.raw.neutralizedOnHoldout,
635
- "heldout neutralized",
636
- receiptsById,
637
- receiptIdsByCell
638
- );
639
- pairMeasuredCells(result.raw.baselineOnHoldout.cells, result.raw.neutralizedOnHoldout.cells);
640
- }
641
- const rebuiltProvenance = buildLoopProvenanceRecord(
642
- loopProvenanceArgsFromResult({
643
- runId: result.provenance.runId,
644
- runDir: result.provenance.runDir,
645
- timestamp: result.provenance.timestamp,
646
- baselineSurface: options.baselineSurface,
647
- result: result.raw,
648
- costReceipts: receipts,
649
- totalCostUsd: result.totalCostUsd,
650
- totalDurationMs: result.durationMs
651
- })
728
+ const significance = heldoutSignificance(
729
+ { before: baselineScores, after: candidateScores, cellIds: cellIds(experiment) },
730
+ {
731
+ confidence,
732
+ resamples,
733
+ seed: bootstrapSeed,
734
+ statistic: "mean",
735
+ deltaThreshold,
736
+ minProductiveRuns
737
+ }
652
738
  );
653
- assertMeasuredIdentity(
654
- result.provenance.recordDigest,
655
- rebuiltProvenance.recordDigest,
656
- "provenance record"
739
+ const power = baselineScores.length >= 3 ? powerPreflight({
740
+ baselineComposites: baselineScores,
741
+ pairedN: baselineScores.length,
742
+ deltaThreshold,
743
+ confidence,
744
+ sharedScorerChannel: true
745
+ }) : void 0;
746
+ const powerSufficient = baselineScores.length >= minProductiveRuns && power !== void 0 && !power.underpowered;
747
+ const guardedDimensions = new Set(criticalDimensions);
748
+ const missingCriticalDimensions = criticalDimensions.filter(
749
+ (dimension) => !dimensions.includes(dimension)
657
750
  );
658
- assertMeasuredIdentity(
659
- options.benchmark.splitDigest,
660
- rebuiltProvenance.evidence.holdout.splitDigest,
661
- "benchmark heldout split"
751
+ const regressions = objectives.filter(
752
+ (objective) => objective.kind === "dimension" && guardedDimensions.has(objective.name) && objective.availability === "measured" && objective.confidenceInterval.lower < -regressionTolerance
662
753
  );
663
- const generationsExplored = result.raw.generations.length;
664
- assertMeasuredNumber(result.generationsExplored, generationsExplored, "generation count");
665
- assertMeasuredNumber(result.totalCostUsd, result.cost.totalCostUsd, "cost summary");
666
- assertMeasuredIdentity(
667
- canonicalDigest(result.cost),
668
- canonicalDigest(result.raw.cost),
669
- "raw cost summary"
754
+ const executionCostUsd = measurements.reduce(
755
+ (sum, measurement) => sum + costFromEvidence(measurement.baseline) + costFromEvidence(measurement.candidate),
756
+ 0
670
757
  );
671
- if (!result.cost.accountingComplete) {
672
- throw new Error(
673
- `cost accounting is incomplete: ${result.cost.incompleteReasons.join("; ") || "unknown reason"}`
674
- );
675
- }
676
- if (!receiptCost.accountingComplete) {
677
- throw new Error(
678
- `cost accounting is incomplete: ${receiptCost.incompleteReasons.join("; ") || "unknown reason"}`
679
- );
680
- }
681
- assertMeasuredIdentity(
682
- canonicalDigest(result.cost),
683
- canonicalDigest(receiptCost),
684
- "cost receipt summary"
758
+ const searchCostUsd = options.searchCostUsd ?? 0;
759
+ const totalCostUsd = executionCostUsd + searchCostUsd;
760
+ const budgetPassed = budgetUsd === void 0 || totalCostUsd <= budgetUsd;
761
+ const completedRuns = measurements.flatMap((measurement) => [
762
+ measurement.baseline,
763
+ measurement.candidate
764
+ ]);
765
+ const incompleteRuns = completedRuns.filter((evidence) => !completedSuccessfully(evidence));
766
+ const failedCandidateResults = measurements.filter(
767
+ (measurement) => !measurement.candidate.receipt.benchmarkResult.material.passed
685
768
  );
686
- assertMeasuredNumber(result.totalCostUsd, receiptCost.totalCostUsd, "cost receipts");
687
- assertMeasuredOptional(result.winner.label, result.raw.winnerLabel, "raw winner label");
688
- assertMeasuredOptional(
689
- result.winner.rationale,
690
- result.raw.winnerRationale,
691
- "raw winner rationale"
769
+ const checks = [
770
+ { name: "paired-significance", passed: significance.significant },
771
+ { name: "statistical-power", passed: powerSufficient },
772
+ { name: "all-runs-completed", passed: incompleteRuns.length === 0 },
773
+ { name: "candidate-task-pass", passed: failedCandidateResults.length === 0 },
774
+ {
775
+ name: "critical-dimensions",
776
+ passed: regressions.length === 0 && missingCriticalDimensions.length === 0
777
+ },
778
+ { name: "budget", passed: budgetPassed }
779
+ ];
780
+ const shipped = checks.every((check) => check.passed);
781
+ const reasons = [
782
+ ...significance.significant ? [] : [
783
+ significance.fewRuns ? `only ${significance.n} paired runs; ${minProductiveRuns} required` : `paired interval lower bound ${significance.bootstrap.low} did not clear ${deltaThreshold}`
784
+ ],
785
+ ...powerSufficient ? [] : [power?.recommendation ?? `need at least ${Math.max(3, minProductiveRuns)} paired runs`],
786
+ ...regressions.length === 0 ? [] : [`critical dimensions regressed: ${regressions.map((entry) => entry.name).join(", ")}`],
787
+ ...missingCriticalDimensions.length === 0 ? [] : [`critical dimensions missing: ${missingCriticalDimensions.join(", ")}`],
788
+ ...incompleteRuns.length === 0 ? [] : [`${incompleteRuns.length} benchmark executions did not exit successfully`],
789
+ ...failedCandidateResults.length === 0 ? [] : [`candidate failed ${failedCandidateResults.length} benchmark tasks`],
790
+ ...budgetPassed ? [] : [`total cost ${totalCostUsd} exceeded budget ${budgetUsd}`]
791
+ ];
792
+ const diff = deriveCandidateBundleDiff(experiment);
793
+ const executionDurationMs = measurements.reduce(
794
+ (sum, measurement) => sum + latencyFromEvidence(measurement.baseline) + latencyFromEvidence(measurement.candidate),
795
+ 0
692
796
  );
693
- return agentImprovementMeasuredComparisonSchema.parse({
797
+ const searchDurationMs = options.searchDurationMs ?? 0;
798
+ const durationMs = executionDurationMs + searchDurationMs;
799
+ const provisional = agentImprovementMeasuredComparisonSchema.parse({
694
800
  kind: "agent-improvement-measured-comparison",
695
- benchmark: options.benchmark,
696
- baselineProfileDigest: options.baselineProfileDigest,
697
- candidateBundleDigest: options.candidateBundleDigest,
801
+ experiment,
802
+ measurements,
698
803
  overall: {
699
804
  name: "composite",
700
- baseline: composite.baseline,
701
- candidate: composite.candidate,
702
- delta: composite.delta,
703
- confidenceInterval: composite.confidenceInterval,
704
- n: composite.n,
705
805
  direction: "higher-is-better",
706
- unit: "score"
806
+ unit: "score",
807
+ ...overall
707
808
  },
708
- objectives: measuredObjectives(pairs),
709
- ...result.winner.label || result.winner.rationale ? {
710
- candidate: {
711
- ...result.winner.label ? { label: result.winner.label } : {},
712
- ...result.winner.rationale ? { rationale: result.winner.rationale } : {}
713
- }
714
- } : {},
809
+ objectives,
810
+ ...options.candidate ? { candidate: options.candidate } : {},
715
811
  decision: {
716
- outcome: result.gateDecision,
717
- reasons: rebuiltProvenance.gate.reasons,
718
- contributingChecks: rebuiltProvenance.gate.contributingGates.map((check) => ({
719
- name: check.name,
720
- passed: check.passed
721
- }))
812
+ outcome: shipped ? "ship" : significance.fewRuns || !powerSufficient ? "need_more_work" : "hold",
813
+ reasons: reasons.length > 0 ? reasons : ["all measured checks passed"],
814
+ contributingChecks: checks
722
815
  },
723
816
  power: {
724
- sufficient: power.scaleAssumed && !power.underpowered,
725
- n: power.n,
726
- minimumDetectableDelta: power.mde,
727
- confidenceLevel: power.confidence,
728
- scaleAssumed: power.scaleAssumed,
729
- sharedScorerChannel: power.sharedChannelCaveat !== void 0,
730
- reason: power.recommendation
817
+ sufficient: powerSufficient,
818
+ n: baselineScores.length,
819
+ minimumDetectableDelta: power?.mde ?? 1,
820
+ confidenceLevel: confidence,
821
+ scaleAssumed: power?.scaleAssumed ?? true,
822
+ sharedScorerChannel: true,
823
+ reason: power?.recommendation ?? `need at least ${Math.max(3, minProductiveRuns)} paired runs`
731
824
  },
732
825
  provenance: {
733
826
  kind: "agent-eval-loop",
734
- schema: rebuiltProvenance.schema,
735
- runId: rebuiltProvenance.runId,
736
- recordDigest: rebuiltProvenance.recordDigest,
737
- baselineContentHash,
738
- candidateContentHash
827
+ schema: "agent-candidate-experiment",
828
+ runId: options.runId,
829
+ recordDigest: canonicalCandidateDigest({}),
830
+ baselineContentHash: experiment.baseline.digest,
831
+ candidateContentHash: experiment.candidate.digest
739
832
  },
740
- diff: canonicalDiff,
833
+ diff,
741
834
  evaluation: {
742
- generationsExplored,
743
- durationMs: result.durationMs,
744
- totalCostUsd: result.totalCostUsd
835
+ generationsExplored: options.generationsExplored ?? 0,
836
+ searchDurationMs,
837
+ executionDurationMs,
838
+ durationMs,
839
+ searchCostUsd,
840
+ executionCostUsd,
841
+ totalCostUsd
842
+ },
843
+ ...options.metadata ? { metadata: options.metadata } : {}
844
+ });
845
+ const { recordDigest: _recordDigest, ...provenance } = provisional.provenance;
846
+ return agentImprovementMeasuredComparisonSchema.parse({
847
+ ...provisional,
848
+ provenance: {
849
+ ...provenance,
850
+ recordDigest: canonicalCandidateDigest({ ...provisional, provenance })
745
851
  }
746
852
  });
747
853
  }
748
- function assertGenerationMeasurements(generations, receiptsById, receiptIdsByCell) {
749
- for (const generation of generations) {
750
- for (const measured of generation.surfaces) {
751
- const candidate = generation.record.candidates.find(
752
- (entry) => entry.surfaceHash === measured.surfaceHash
753
- );
754
- if (!candidate) {
755
- throw new Error(
756
- `generation ${generation.record.generationIndex} is missing candidate ${measured.surfaceHash}`
757
- );
758
- }
759
- const composite = campaignMeanComposite(measured.campaign);
760
- const breakdown = campaignBreakdown(measured.campaign);
761
- const coverage = campaignCoverage(
762
- measured.campaign.cells,
763
- measured.campaign.scenarios,
764
- measured.campaign.reps,
765
- true
766
- );
767
- assertScorableMeasuredCells(
768
- measured.campaign,
769
- coverage.scorableCellIds,
770
- receiptsById,
771
- receiptIdsByCell
772
- );
773
- assertMeasuredNumber(
774
- candidate.composite,
775
- composite,
776
- `candidate ${measured.surfaceHash} composite`
777
- );
778
- if (!candidate.coverage) {
779
- throw new Error(`candidate ${measured.surfaceHash} does not report campaign coverage`);
780
- }
781
- assertMeasuredIdentity(
782
- canonicalDigest(candidate.coverage),
783
- canonicalDigest({
784
- expectedCells: coverage.expectedCellIds.length,
785
- scorableCells: coverage.scorableCellIds.length,
786
- unscorableCells: coverage.unscorableCells
787
- }),
788
- `candidate ${measured.surfaceHash} coverage`
789
- );
790
- assertMeasuredIdentity(
791
- String(candidate.eligibleForPromotion),
792
- String(coverage.complete),
793
- `candidate ${measured.surfaceHash} eligibility`
794
- );
795
- assertMeasuredIdentity(
796
- canonicalDigest(candidate.dimensions),
797
- canonicalDigest(breakdown.dimensions),
798
- `candidate ${measured.surfaceHash} dimensions`
799
- );
800
- assertMeasuredIdentity(
801
- canonicalDigest(candidate.scenarios),
802
- canonicalDigest(breakdown.scenarios),
803
- `candidate ${measured.surfaceHash} scenarios`
804
- );
805
- }
854
+ function verifyCandidateExperimentComparison(input) {
855
+ const comparison = agentImprovementMeasuredComparisonSchema.parse(input);
856
+ const recomputed = measuredComparisonFromCandidateExperiment({
857
+ experiment: comparison.experiment,
858
+ measurements: comparison.measurements,
859
+ runId: comparison.provenance.runId,
860
+ ...comparison.candidate ? { candidate: comparison.candidate } : {},
861
+ generationsExplored: comparison.evaluation.generationsExplored,
862
+ searchDurationMs: comparison.evaluation.searchDurationMs,
863
+ searchCostUsd: comparison.evaluation.searchCostUsd,
864
+ ...comparison.metadata ? { metadata: comparison.metadata } : {}
865
+ });
866
+ if (canonicalCandidateDigest(recomputed) !== canonicalCandidateDigest(comparison)) {
867
+ throw new Error("candidate experiment comparison does not match its Runtime receipts");
868
+ }
869
+ return comparison;
870
+ }
871
+ function deriveCandidateBundleDiff(experiment) {
872
+ const surfaces = ["profile", "code", "execution", "knowledge", "memory"];
873
+ const changed = surfaces.flatMap((surface) => {
874
+ const baseline = experiment.baseline[surface] ?? null;
875
+ const candidate = experiment.candidate[surface] ?? null;
876
+ const baselineDigest = canonicalCandidateDigest(baseline);
877
+ const candidateDigest = canonicalCandidateDigest(candidate);
878
+ if (baselineDigest === candidateDigest) return [];
879
+ return [
880
+ [
881
+ `--- baseline/${surface} (${baselineDigest})`,
882
+ `+++ candidate/${surface} (${candidateDigest})`,
883
+ JSON.stringify({ baseline, candidate }, null, 2)
884
+ ].join("\n")
885
+ ];
886
+ });
887
+ if (changed.length === 0) {
888
+ throw new Error("candidate experiment has no changed candidate surface");
806
889
  }
890
+ return changed.join("\n\n");
807
891
  }
808
- function assertCompleteMeasuredCampaign(campaign, name, receiptsById, receiptIdsByCell) {
809
- assertCampaignSplitIdentity(campaign.scenarios, campaign.reps, campaign.splitDigest);
810
- const coverage = campaignCoverage(campaign.cells, campaign.scenarios, campaign.reps, true);
811
- if (!coverage.complete) {
812
- throw new Error(
813
- `${name} is incomplete (${coverage.scorableCellIds.length}/${coverage.expectedCellIds.length} designed cells scorable)`
814
- );
892
+ function verifyCandidateBenchmarkTask(input) {
893
+ const task = agentCandidateBenchmarkTaskSchema.parse(input);
894
+ verifySelfAddressed(task, "candidate benchmark task");
895
+ return task;
896
+ }
897
+ function verifyCandidateBenchmarkSuiteInputs(input) {
898
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
899
+ throw new Error("candidate benchmark suite inputs must be an object");
815
900
  }
816
- assertScorableMeasuredCells(campaign, coverage.scorableCellIds, receiptsById, receiptIdsByCell);
817
- }
818
- function assertScorableMeasuredCells(campaign, scorableCellIds, receiptsById, receiptIdsByCell) {
819
- const cellsById = new Map(campaign.cells.map((cell) => [cell.cellId, cell]));
820
- for (const cellId of scorableCellIds) {
821
- const cell = cellsById.get(cellId);
822
- if (!cell) throw new Error(`measured campaign is missing scorable cell ${cellId}`);
823
- assertMeasuredCell(cell, receiptsById, receiptIdsByCell, campaign.runDir);
901
+ const candidate = input;
902
+ const suite = verifyCandidateBenchmarkSuite(candidate.suite);
903
+ if (!Array.isArray(candidate.tasks) || candidate.tasks.length !== suite.taskDigests.length) {
904
+ throw new Error("candidate benchmark suite task count does not match its signed digests");
824
905
  }
825
- }
826
- function measuredObjectives(pairs) {
827
- const qualityColumns = /* @__PURE__ */ new Map();
828
- for (const [baseline, candidate] of pairs) {
829
- for (const cell of [baseline, candidate]) {
830
- for (const [objective, score] of Object.entries(cell.judgeScores)) {
831
- if (score.failed) continue;
832
- qualityColumns.set(`objective:${objective}`, {
833
- kind: "objective",
834
- name: objective,
835
- direction: "higher-is-better",
836
- unit: "score"
837
- });
838
- for (const name of Object.keys(score.dimensions)) {
839
- qualityColumns.set(`dimension:${objective}:${name}`, {
840
- kind: "dimension",
841
- objective,
842
- name,
843
- direction: "higher-is-better",
844
- unit: "score"
845
- });
846
- }
847
- }
906
+ candidate.tasks.forEach((task, index) => {
907
+ const verified = verifyCandidateBenchmarkTask(task);
908
+ if (verified.digest !== suite.taskDigests[index]) {
909
+ throw new Error(`candidate benchmark task ${index} does not match the signed suite`);
848
910
  }
849
- }
850
- return [
851
- ...[...qualityColumns.entries()].sort(([left], [right]) => left.localeCompare(right)).map(
852
- ([, column]) => measuredObjective(column, pairs, (cell) => measuredQuality(cell, column))
853
- ),
854
- measuredCostObjective(pairs),
855
- measuredObjective(
856
- {
857
- kind: "latency",
858
- name: "latency",
859
- direction: "lower-is-better",
860
- unit: "milliseconds"
861
- },
862
- pairs,
863
- (cell) => cell.durationMs
864
- )
865
- ];
866
- }
867
- function measuredCostObjective(pairs) {
868
- const cells = pairs.flat();
869
- for (const cell of cells) finiteMeasuredValue(cell.costUsd, "cost:cost");
870
- return measuredObjective(
871
- {
872
- kind: "cost",
873
- name: "cost",
874
- direction: "lower-is-better",
875
- unit: "usd"
876
- },
877
- pairs,
878
- (cell) => cell.costUsd
911
+ });
912
+ return { suite, tasks: candidate.tasks };
913
+ }
914
+ function verifyCandidateBenchmarkSuite(input) {
915
+ const suite = agentCandidateBenchmarkSuiteSchema.parse(input);
916
+ verifySelfAddressed(suite, "candidate benchmark suite");
917
+ return suite;
918
+ }
919
+ function verifyBundle(input, label) {
920
+ const bundle = agentCandidateBundleSchema.parse(input);
921
+ verifySelfAddressed(bundle, label);
922
+ return bundle;
923
+ }
924
+ function verifyMeasurement(experiment, input, index) {
925
+ const suite = experiment.benchmark.suite;
926
+ const taskIndex = Math.floor(index / suite.reps);
927
+ const repetition = index % suite.reps;
928
+ const task = experiment.benchmark.tasks[taskIndex];
929
+ const seed = suite.seeds[index];
930
+ if (!task || seed === void 0) {
931
+ throw new Error(`candidate experiment measurement ${index} is outside the signed suite`);
932
+ }
933
+ const baseline = verifyExecutionEvidence(input.baseline);
934
+ const candidate = verifyExecutionEvidence(input.candidate);
935
+ for (const [arm, evidence] of [
936
+ ["baseline", baseline],
937
+ ["candidate", candidate]
938
+ ]) {
939
+ const bundle = experiment[arm];
940
+ const materialization = evidence.materializationReceipt;
941
+ const plan = materialization.executionPlan.material;
942
+ const runCell = plan.runCell;
943
+ verifySelfAddressed(runCell, "candidate run cell");
944
+ if (runCell.experimentDigest !== experiment.digest || runCell.arm !== arm || runCell.bundleDigest !== bundle.digest || runCell.suiteDigest !== suite.digest || runCell.taskDigest !== task.digest || runCell.taskIndex !== taskIndex || runCell.repetition !== repetition || runCell.seed !== seed || runCell.attempt > task.attempt.maxAttempts || materialization.bundleDigest !== bundle.digest || materialization.benchmark.suite.digest !== suite.digest || materialization.benchmark.task.digest !== task.digest || materialization.codeKind !== bundle.code.kind || materialization.profileActivation.profilePlan.material.sourceProfileDigest !== canonicalCandidateDigest(bundle.profile) || evidence.receipt.runCellDigest !== runCell.digest || JSON.stringify(materialization.resolvedModel) !== JSON.stringify(task.model)) {
945
+ throw new Error(`candidate experiment measurement ${index} substituted its ${arm} arm`);
946
+ }
947
+ verifyTaskOutcome(task, evidence, index, arm);
948
+ }
949
+ const baselinePlan = baseline.materializationReceipt.executionPlan.material;
950
+ const candidatePlan = candidate.materializationReceipt.executionPlan.material;
951
+ if (baselinePlan.executionId === candidatePlan.executionId || baselinePlan.runCell.digest === candidatePlan.runCell.digest || baseline.materializationReceipt.digest === candidate.materializationReceipt.digest || baseline.receipt.digest === candidate.receipt.digest || baseline.digest === candidate.digest) {
952
+ throw new Error(`candidate experiment measurement ${index} reused one execution across arms`);
953
+ }
954
+ return { baseline, candidate };
955
+ }
956
+ function verifyExecutionEvidence(input) {
957
+ const evidence = candidateExecutionEvidenceSchema.parse(input);
958
+ verifySelfAddressed(evidence, "candidate execution evidence");
959
+ verifySelfAddressed(evidence.materializationReceipt, "candidate materialization receipt");
960
+ verifySelfAddressed(
961
+ evidence.materializationReceipt.profileActivation,
962
+ "candidate profile activation"
879
963
  );
880
- }
881
- function measuredComposite(cell) {
882
- const values = Object.values(cell.judgeScores).filter((score) => !score.failed).map((score) => score.composite).filter(Number.isFinite);
883
- if (values.length === 0) {
884
- throw new Error(`heldout cell ${measuredCellKey(cell)} has no successful composite score`);
885
- }
886
- return measuredMean(values);
887
- }
888
- function pairMeasuredCells(baselineCells, candidateCells) {
889
- const cells = [...baselineCells, ...candidateCells];
890
- const errors = cells.filter((cell) => cell.error);
891
- if (errors.length > 0) {
892
- throw new Error(
893
- `measured objectives cannot publish ${errors.length} errored heldout cells: ${errors.map((cell) => measuredCellKey(cell)).join(", ")}`
894
- );
895
- }
896
- const rows = [
897
- ...baselineCells.map((cell) => ({
898
- pairKey: cell.scenarioId,
899
- repKey: String(cell.rep),
900
- arm: "baseline",
901
- cell
902
- })),
903
- ...candidateCells.map((cell) => ({
904
- pairKey: cell.scenarioId,
905
- repKey: String(cell.rep),
906
- arm: "candidate",
907
- cell
908
- }))
964
+ verifyMaterialAddressed(
965
+ evidence.materializationReceipt.profileActivation.profilePlan,
966
+ "candidate profile plan"
967
+ );
968
+ verifyMaterialAddressed(evidence.materializationReceipt.executionPlan, "candidate execution plan");
969
+ verifySelfAddressed(evidence.receipt, "candidate run receipt");
970
+ verifyMaterialAddressed(evidence.receipt.modelSettlement, "candidate model settlement");
971
+ verifyMaterialAddressed(evidence.receipt.taskOutcome, "candidate task outcome");
972
+ verifyMaterialAddressed(evidence.receipt.benchmarkResult, "candidate benchmark result");
973
+ return evidence;
974
+ }
975
+ function verifySelfAddressed(document, label) {
976
+ if (canonicalCandidateDigest(omitTopLevelDigest(document)) !== document.digest) {
977
+ throw new Error(`${label} digest is invalid`);
978
+ }
979
+ }
980
+ function verifyTaskOutcome(task, evidence, index, arm) {
981
+ const outcome = evidence.receipt.taskOutcome.material.outcome;
982
+ const result = evidence.receipt.benchmarkResult.material;
983
+ const prefix = `candidate experiment measurement ${index} ${arm}`;
984
+ if (result.evidence.sha256 === task.grader.artifact.sha256) {
985
+ throw new Error(`${prefix} reused grader bytes as grading evidence`);
986
+ }
987
+ const usage = combinedUsage(evidence);
988
+ const usageChecks = [
989
+ [usage.modelCalls, task.limits.maxModelCalls, "model calls"],
990
+ [usage.inputTokens, task.limits.maxInputTokens, "input tokens"],
991
+ [usage.outputTokens, task.limits.maxOutputTokens, "output tokens"],
992
+ [usage.costUsdNanos, Math.round(task.limits.maxCostUsd * 1e9), "cost"]
909
993
  ];
910
- const paired = pairArms(rows, { baselineArm: "baseline", treatmentArm: "candidate" });
911
- if (paired.pairs.length === 0 || paired.unpairedBaseline.length > 0 || paired.unpairedTreatment.length > 0) {
912
- throw new Error("measured objectives require the same non-empty paired heldout cells");
913
- }
914
- return paired.pairs.map((pair) => {
915
- const baseline = pair.baseline.cell;
916
- const candidate = pair.treatment.cell;
917
- if (baseline.seed !== candidate.seed) {
918
- throw new Error(`heldout cell ${baseline.cellId} does not share one paired seed`);
994
+ for (const [actual, maximum, label] of usageChecks) {
995
+ if (actual > maximum) {
996
+ throw new Error(`${prefix} ${label} ${actual} exceeds the signed limit ${maximum}`);
919
997
  }
920
- return [baseline, candidate];
921
- });
922
- }
923
- function assertMeasuredCell(cell, receiptsById, receiptIdsByCell, runDir) {
924
- if (!cell.scenarioId.trim() || !Number.isSafeInteger(cell.rep) || cell.rep < 0 || !Number.isSafeInteger(cell.seed) || cell.cellId !== measuredCellKey(cell)) {
925
- throw new Error(`heldout cell '${cell.cellId}' has an invalid scenario/rep identity`);
926
998
  }
927
- nonnegativeMeasuredValue(cell.costUsd, `heldout cell ${cell.cellId} cost`);
928
- nonnegativeMeasuredValue(cell.durationMs, `heldout cell ${cell.cellId} latency`);
929
- if (!cell.tokenUsage) {
930
- throw new Error(`heldout cell ${cell.cellId} does not report token usage`);
999
+ if (outcome.kind !== task.outcome.kind) {
1000
+ throw new Error(`${prefix} returned an outcome outside the signed task contract`);
931
1001
  }
932
- nonnegativeMeasuredInteger(cell.tokenUsage.input, `heldout cell ${cell.cellId} input tokens`);
933
- nonnegativeMeasuredInteger(cell.tokenUsage.output, `heldout cell ${cell.cellId} output tokens`);
934
- if (cell.tokenUsage.cached !== void 0) {
935
- nonnegativeMeasuredInteger(cell.tokenUsage.cached, `heldout cell ${cell.cellId} cached tokens`);
936
- }
937
- if (!Array.isArray(cell.costCallIds)) {
938
- throw new Error(`heldout cell ${cell.cellId} does not identify its cost receipts`);
1002
+ if (task.outcome.kind === "output") {
1003
+ if (outcome.kind !== "output" || outcome.spec.mediaType !== task.outcome.mediaType || outcome.spec.maxBytes !== task.outcome.maxBytes) {
1004
+ throw new Error(`${prefix} changed the signed output contract`);
1005
+ }
1006
+ return;
939
1007
  }
940
- if (new Set(cell.costCallIds).size !== cell.costCallIds.length) {
941
- throw new Error(`heldout cell ${cell.cellId} repeats a cost receipt`);
1008
+ const repository = task.repository;
1009
+ if (outcome.kind !== "workspace" || repository === void 0 || outcome.baseRepository.identity !== repository.identity || outcome.baseRepository.rootIdentity !== repository.rootIdentity || outcome.baseRepository.commit !== repository.baseCommit || outcome.baseRepository.tree !== repository.baseTree) {
1010
+ throw new Error(`${prefix} did not start from the signed repository state`);
942
1011
  }
943
- const linkedReceipts = cell.costCallIds.map((callId) => {
944
- const receipt = receiptsById.get(callId);
945
- if (!receipt)
946
- throw new Error(`heldout cell ${cell.cellId} references missing cost receipt ${callId}`);
947
- if (receipt.tags?.runDir !== runDir || receipt.tags.cellId !== cell.cellId || receipt.tags.scenarioId !== cell.scenarioId || receipt.tags.rep !== String(cell.rep)) {
948
- throw new Error(`heldout cell ${cell.cellId} references a cost receipt from another cell`);
1012
+ }
1013
+ function verifyStableProfileMaterialization(measurements) {
1014
+ for (const arm of ["baseline", "candidate"]) {
1015
+ const expected = measurements[0]?.[arm].materializationReceipt.profileActivation;
1016
+ if (!expected) throw new Error("candidate experiment contains no profile materialization");
1017
+ const expectedDigest = canonicalCandidateDigest({
1018
+ profilePlanDigest: expected.profilePlan.digest,
1019
+ files: expected.files
1020
+ });
1021
+ for (const [index, measurement] of measurements.entries()) {
1022
+ const activation = measurement[arm].materializationReceipt.profileActivation;
1023
+ if (canonicalCandidateDigest({
1024
+ profilePlanDigest: activation.profilePlan.digest,
1025
+ files: activation.files
1026
+ }) !== expectedDigest) {
1027
+ throw new Error(
1028
+ `candidate experiment measurement ${index} ${arm} materialized a different profile`
1029
+ );
1030
+ }
949
1031
  }
950
- return receipt;
951
- });
952
- assertMeasuredIdentity(
953
- canonicalDigest([...cell.costCallIds].sort()),
954
- canonicalDigest(
955
- [
956
- ...receiptIdsByCell.get(receiptCellKey(runDir, cell.cellId, cell.scenarioId, cell.rep)) ?? []
957
- ].sort()
958
- ),
959
- `heldout cell ${cell.cellId} cost receipt IDs`
960
- );
961
- const agentReceipts = linkedReceipts.filter((receipt) => receipt.channel === "agent");
962
- if (agentReceipts.some((receipt) => receipt.error)) {
963
- throw new Error(`measured cell ${cell.cellId} links a failed agent receipt`);
964
1032
  }
965
- assertRealAgentReceipts(agentReceipts, { allowMixed: false });
966
- assertMeasuredNumber(
967
- cell.costUsd,
968
- agentReceipts.reduce((total, receipt) => total + receipt.costUsd, 0),
969
- `heldout cell ${cell.cellId} cost receipts`
970
- );
971
- assertMeasuredNumber(
972
- cell.tokenUsage.input,
973
- agentReceipts.reduce((total, receipt) => total + receipt.inputTokens, 0),
974
- `heldout cell ${cell.cellId} input receipts`
975
- );
976
- assertMeasuredNumber(
977
- cell.tokenUsage.output,
978
- agentReceipts.reduce((total, receipt) => total + receipt.outputTokens, 0),
979
- `heldout cell ${cell.cellId} output receipts`
980
- );
981
- assertMeasuredNumber(
982
- cell.tokenUsage.cached ?? 0,
983
- agentReceipts.reduce((total, receipt) => total + (receipt.cachedTokens ?? 0), 0),
984
- `heldout cell ${cell.cellId} cached receipts`
985
- );
986
- for (const [judge, score] of Object.entries(cell.judgeScores)) {
987
- if (score.failed) {
988
- throw new Error(`heldout cell ${cell.cellId} contains failed judge '${judge}'`);
989
- }
990
- finiteMeasuredValue(score.composite, `objective:${judge}`);
991
- for (const [dimension, value] of Object.entries(score.dimensions)) {
992
- finiteMeasuredValue(value, `dimension:${judge}:${dimension}`);
993
- }
1033
+ }
1034
+ function completedSuccessfully(evidence) {
1035
+ const termination = evidence.receipt.termination;
1036
+ return termination.kind === "exit" && termination.exitCode === 0;
1037
+ }
1038
+ function verifyMaterialAddressed(evidence, label) {
1039
+ if (canonicalCandidateDigest(evidence.material) !== evidence.digest) {
1040
+ throw new Error(`${label} digest is invalid`);
994
1041
  }
995
- measuredComposite(cell);
996
- }
997
- function measuredObjective(identity, pairs, value) {
998
- const key = measuredObjectiveKey(identity);
999
- const baseline = pairs.map(([cell]) => finiteMeasuredValue(value(cell), key));
1000
- const candidate = pairs.map(([, cell]) => finiteMeasuredValue(value(cell), key));
1001
- const interval = pairedBootstrap(baseline, candidate, {
1002
- confidence: 0.95,
1003
- resamples: 2e3,
1042
+ }
1043
+ function measuredEstimate(baseline, candidate, options) {
1044
+ const bootstrap = pairedBootstrap(baseline, candidate, {
1045
+ confidence: options.confidence,
1046
+ resamples: options.resamples,
1004
1047
  statistic: "mean",
1005
- seed: measuredSeed(key)
1048
+ seed: options.seed
1006
1049
  });
1007
- const baselineMean = measuredMean(baseline);
1008
- const candidateMean = measuredMean(candidate);
1050
+ const baselineMean = mean(baseline);
1051
+ const candidateMean = mean(candidate);
1052
+ const delta = candidateMean - baselineMean;
1009
1053
  return {
1010
- ...identity,
1011
- availability: "measured",
1012
1054
  baseline: baselineMean,
1013
1055
  candidate: candidateMean,
1014
- delta: candidateMean - baselineMean,
1056
+ delta,
1015
1057
  confidenceInterval: {
1016
- level: interval.confidence,
1017
- lower: interval.low,
1018
- upper: interval.high,
1058
+ level: bootstrap.confidence,
1059
+ lower: Math.min(bootstrap.low, delta),
1060
+ upper: Math.max(bootstrap.high, delta),
1019
1061
  method: "paired-bootstrap",
1020
1062
  statistic: "mean",
1021
- resamples: interval.resamples
1063
+ resamples: bootstrap.resamples
1022
1064
  },
1023
- n: interval.n
1065
+ n: bootstrap.n
1024
1066
  };
1025
1067
  }
1026
- function measuredQuality(cell, column) {
1027
- const objective = column.kind === "objective" ? column.name : column.objective;
1028
- const score = cell.judgeScores[objective];
1029
- if (!score || score.failed) {
1030
- throw new Error(
1031
- `heldout cell ${measuredCellKey(cell)} is missing measured objective '${objective}'`
1032
- );
1033
- }
1034
- const value = column.kind === "objective" ? score.composite : score.dimensions[column.name];
1035
- if (value === void 0) {
1036
- throw new Error(
1037
- `heldout cell ${measuredCellKey(cell)} is missing '${objective}' dimension '${column.name}'`
1038
- );
1068
+ function sharedDimensions(measurements) {
1069
+ const expected = dimensionNames(measurements[0]?.baseline);
1070
+ for (const [index, measurement] of measurements.entries()) {
1071
+ for (const [arm, evidence] of [
1072
+ ["baseline", measurement.baseline],
1073
+ ["candidate", measurement.candidate]
1074
+ ]) {
1075
+ const actual = dimensionNames(evidence);
1076
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
1077
+ throw new Error(
1078
+ `candidate experiment measurement ${index} ${arm} dimensions do not match the suite`
1079
+ );
1080
+ }
1081
+ }
1039
1082
  }
1040
- return finiteMeasuredValue(value, measuredObjectiveKey(column));
1083
+ return expected;
1041
1084
  }
1042
- function measuredObjectiveKey(identity) {
1043
- return identity.kind === "dimension" ? `${identity.kind}:${identity.objective}:${identity.name}` : `${identity.kind}:${identity.name}`;
1085
+ function dimensionNames(evidence) {
1086
+ return (evidence?.receipt.benchmarkResult.material.dimensions ?? []).map(
1087
+ (dimension) => dimension.name
1088
+ );
1044
1089
  }
1045
- function measuredCellKey(cell) {
1046
- return `${cell.scenarioId}:${cell.rep}`;
1090
+ function scoreOf(arm) {
1091
+ return (measurement) => measurement[arm].receipt.benchmarkResult.material.score;
1047
1092
  }
1048
- function finiteMeasuredValue(value, name) {
1049
- if (!Number.isFinite(value)) throw new Error(`measured objective '${name}' is not finite`);
1050
- return value;
1093
+ function dimensionOf(arm, name) {
1094
+ return (measurement) => {
1095
+ const dimension = measurement[arm].receipt.benchmarkResult.material.dimensions.find(
1096
+ (entry) => entry.name === name
1097
+ );
1098
+ if (!dimension) throw new Error(`candidate experiment is missing dimension '${name}'`);
1099
+ return dimension.score;
1100
+ };
1051
1101
  }
1052
- function nonnegativeMeasuredValue(value, name) {
1053
- if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be non-negative`);
1054
- return value;
1102
+ function costOf(arm) {
1103
+ return (measurement) => costFromEvidence(measurement[arm]);
1055
1104
  }
1056
- function nonnegativeMeasuredInteger(value, name) {
1057
- if (!Number.isSafeInteger(value) || value < 0) {
1058
- throw new Error(`${name} must be a non-negative safe integer`);
1059
- }
1060
- return value;
1105
+ function latencyOf(arm) {
1106
+ return (measurement) => latencyFromEvidence(measurement[arm]);
1061
1107
  }
1062
- function measuredMean(values) {
1063
- if (values.length === 0) throw new Error("measured objective has no paired values");
1064
- return values.reduce((sum, value) => sum + value, 0) / values.length;
1108
+ function costFromEvidence(evidence) {
1109
+ return combinedUsage(evidence).costUsdNanos / 1e9;
1065
1110
  }
1066
- function measuredSeed(value) {
1067
- let seed = 2166136261;
1068
- for (const byte of Buffer.from(value, "utf8")) {
1069
- seed = Math.imul(seed ^ byte, 16777619) >>> 0;
1070
- }
1071
- return seed;
1111
+ function latencyFromEvidence(evidence) {
1112
+ return evidence.receipt.timing.durationMs + evidence.receipt.benchmarkResult.material.grading.timing.durationMs;
1072
1113
  }
1073
- function assertMeasuredNumber(actual, expected, name) {
1074
- const tolerance = Number.EPSILON * Math.max(1, Math.abs(actual), Math.abs(expected)) * 8;
1075
- if (!Number.isFinite(actual) || !Number.isFinite(expected) || Math.abs(actual - expected) > tolerance) {
1076
- throw new Error(`${name} does not agree across the measured comparison`);
1077
- }
1114
+ function combinedUsage(evidence) {
1115
+ const candidate = evidence.receipt.modelSettlement.material.usage;
1116
+ const grader = evidence.receipt.benchmarkResult.material.grading.usage;
1117
+ return {
1118
+ inputTokens: candidate.inputTokens + grader.inputTokens,
1119
+ outputTokens: candidate.outputTokens + grader.outputTokens,
1120
+ cachedInputTokens: candidate.cachedInputTokens + grader.cachedInputTokens,
1121
+ reasoningTokens: candidate.reasoningTokens + grader.reasoningTokens,
1122
+ modelCalls: candidate.modelCalls + grader.modelCalls,
1123
+ costUsdNanos: candidate.costUsdNanos + grader.costUsdNanos
1124
+ };
1078
1125
  }
1079
- function assertMeasuredIdentity(actual, expected, name) {
1080
- if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
1081
- }
1082
- function assertMeasuredOptional(actual, expected, name) {
1083
- if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
1084
- }
1085
- function indexReceiptIdsByCell(receipts) {
1086
- const idsByCell = /* @__PURE__ */ new Map();
1087
- for (const receipt of receipts) {
1088
- const tags = receipt.tags;
1089
- if (!tags?.runDir || !tags.cellId || !tags.scenarioId || tags.rep === void 0) continue;
1090
- const key = receiptCellKey(tags.runDir, tags.cellId, tags.scenarioId, tags.rep);
1091
- const ids = idsByCell.get(key) ?? [];
1092
- ids.push(receipt.callId);
1093
- idsByCell.set(key, ids);
1094
- }
1095
- return idsByCell;
1126
+ function cellIds(experiment) {
1127
+ const { suite, tasks } = experiment.benchmark;
1128
+ return suite.seeds.map((_, index) => {
1129
+ const taskIndex = Math.floor(index / suite.reps);
1130
+ const repetition = index % suite.reps;
1131
+ return `${tasks[taskIndex]?.scenario.id ?? taskIndex}:${repetition}`;
1132
+ });
1133
+ }
1134
+ function mean(values) {
1135
+ if (values.length === 0) throw new Error("candidate experiment requires measured values");
1136
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
1096
1137
  }
1097
- function receiptCellKey(runDir, cellId, scenarioId, rep) {
1098
- return JSON.stringify([runDir, cellId, scenarioId, String(rep)]);
1138
+ function abortError(signal) {
1139
+ return signal.reason instanceof Error ? signal.reason : new Error("candidate experiment aborted");
1099
1140
  }
1100
1141
 
1101
1142
  // src/contract/eval-reporting-suite.ts
@@ -1637,17 +1678,27 @@ export {
1637
1678
  gepaProposer,
1638
1679
  heldOutGate,
1639
1680
  inMemoryCampaignStorage,
1640
- measuredComparisonFromSelfImproveResult,
1681
+ measuredComparisonFromCandidateExperiment,
1682
+ observeCodeAgentSession,
1641
1683
  paretoPolicy,
1642
1684
  paretoSignificanceGate,
1643
1685
  parseAgentTrace,
1644
1686
  parseCodeAgentJsonl,
1645
1687
  partitionRunsByAuthoringModel,
1646
1688
  runCampaign,
1689
+ runCandidateExperiment,
1647
1690
  runEval,
1648
1691
  runImprovementLoop,
1649
1692
  runReferenceEquivalenceJudge,
1693
+ sealCandidateBenchmarkSuite,
1694
+ sealCandidateBenchmarkTask,
1695
+ sealCandidateExperiment,
1650
1696
  selfImprove,
1651
- summarizeExecution
1697
+ summarizeExecution,
1698
+ verifyCandidateBenchmarkSuite,
1699
+ verifyCandidateBenchmarkSuiteInputs,
1700
+ verifyCandidateBenchmarkTask,
1701
+ verifyCandidateExperiment,
1702
+ verifyCandidateExperimentComparison
1652
1703
  };
1653
1704
  //# sourceMappingURL=index.js.map