@tangle-network/agent-eval 0.217.1 → 0.218.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.
Files changed (172) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/dist/adapters/http.d.ts +2 -2
  3. package/dist/analyst/index.d.ts +1 -1
  4. package/dist/analyst/index.d.ts.map +1 -1
  5. package/dist/analyst/index.js +9 -9
  6. package/dist/analyst/index.js.map +1 -1
  7. package/dist/{analyze-runs-DyeNcenW.d.ts → analyze-runs-DjlZOVXx.d.ts} +2 -2
  8. package/dist/{analyze-runs-DyeNcenW.d.ts.map → analyze-runs-DjlZOVXx.d.ts.map} +1 -1
  9. package/dist/{benchmark-command-xQK8nOI6.js → benchmark-command-BupdHRyf.js} +661 -562
  10. package/dist/benchmark-command-BupdHRyf.js.map +1 -0
  11. package/dist/benchmarks/index.d.ts +4 -4
  12. package/dist/benchmarks/index.js +3 -3
  13. package/dist/campaign/index.d.ts +4 -4
  14. package/dist/campaign/index.js +10 -10
  15. package/dist/{campaign-CJGkxAQ6.js → campaign-CGvUTnmc.js} +312 -281
  16. package/dist/{campaign-CJGkxAQ6.js.map → campaign-CGvUTnmc.js.map} +1 -1
  17. package/dist/{campaign-evidence-CQYw8_0H.js → campaign-evidence-DunzcJGv.js} +2 -2
  18. package/dist/{campaign-evidence-CQYw8_0H.js.map → campaign-evidence-DunzcJGv.js.map} +1 -1
  19. package/dist/{chat-client-ldqxYYTN.js → chat-client-B2pXlPdx.js} +2 -2
  20. package/dist/{chat-client-ldqxYYTN.js.map → chat-client-B2pXlPdx.js.map} +1 -1
  21. package/dist/{chat-json-call-DavzexSp.js → chat-json-call-Dm9c-b41.js} +2 -2
  22. package/dist/{chat-json-call-DavzexSp.js.map → chat-json-call-Dm9c-b41.js.map} +1 -1
  23. package/dist/cli.js +10 -10
  24. package/dist/{completion-verifier-B_jw6C9p.js → completion-verifier-DbLVf3i8.js} +4 -4
  25. package/dist/{completion-verifier-B_jw6C9p.js.map → completion-verifier-DbLVf3i8.js.map} +1 -1
  26. package/dist/contract/index.d.ts +7 -7
  27. package/dist/contract/index.js +90 -10
  28. package/dist/contract/index.js.map +1 -1
  29. package/dist/{default-registry-ClYjq3QE.js → default-registry-BJ-Pq6yL.js} +309 -277
  30. package/dist/{default-registry-ClYjq3QE.js.map → default-registry-BJ-Pq6yL.js.map} +1 -1
  31. package/dist/{define-agent-eval-BDxNlP3D.js → define-agent-eval-BGdFKlrc.js} +165 -105
  32. package/dist/define-agent-eval-BGdFKlrc.js.map +1 -0
  33. package/dist/{define-agent-eval-BIkH_gAR.d.ts → define-agent-eval-BlH10zEw.d.ts} +6 -6
  34. package/dist/{define-agent-eval-BIkH_gAR.d.ts.map → define-agent-eval-BlH10zEw.d.ts.map} +1 -1
  35. package/dist/diagnosis/index.d.ts +1 -1
  36. package/dist/{dspy-rlm-engine-DhdCr1Dc.js → dspy-rlm-engine-CSMwudLO.js} +2 -2
  37. package/dist/{dspy-rlm-engine-DhdCr1Dc.js.map → dspy-rlm-engine-CSMwudLO.js.map} +1 -1
  38. package/dist/eval-campaign-BUMIxKTE.js +393 -0
  39. package/dist/eval-campaign-BUMIxKTE.js.map +1 -0
  40. package/dist/{evaluation-jTjC_QQD.d.ts → evaluation-BOZ38vOH.d.ts} +2 -2
  41. package/dist/{evaluation-jTjC_QQD.d.ts.map → evaluation-BOZ38vOH.d.ts.map} +1 -1
  42. package/dist/evaluation.d.ts +1 -1
  43. package/dist/experiment/index.d.ts +1 -1
  44. package/dist/experiment/index.d.ts.map +1 -1
  45. package/dist/experiment/index.js +110 -71
  46. package/dist/experiment/index.js.map +1 -1
  47. package/dist/{exporters-Cl6IeNAu.js → exporters-DWvM16LE.js} +2 -2
  48. package/dist/{exporters-Cl6IeNAu.js.map → exporters-DWvM16LE.js.map} +1 -1
  49. package/dist/{external-optimizer-process-KVFGvYX1.js → external-optimizer-process-C3Wm7Vhn.js} +2 -2
  50. package/dist/{external-optimizer-process-KVFGvYX1.js.map → external-optimizer-process-C3Wm7Vhn.js.map} +1 -1
  51. package/dist/{external-optimizer-subprocess-XIHBOSKt.js → external-optimizer-subprocess-B5gdLRZv.js} +3 -3
  52. package/dist/{external-optimizer-subprocess-XIHBOSKt.js.map → external-optimizer-subprocess-B5gdLRZv.js.map} +1 -1
  53. package/dist/{findings-store-BMVQtEsm.js → findings-store-BtTzPL0d.js} +5 -5
  54. package/dist/{findings-store-BMVQtEsm.js.map → findings-store-BtTzPL0d.js.map} +1 -1
  55. package/dist/{hf-dataset-D9f7-oX7.js → hf-dataset-CKxIJQba.js} +3 -3
  56. package/dist/{hf-dataset-D9f7-oX7.js.map → hf-dataset-CKxIJQba.js.map} +1 -1
  57. package/dist/hosted/index.js +1 -1
  58. package/dist/{index-ZuWx52WG.d.ts → index-B16FNdW5.d.ts} +34 -17
  59. package/dist/index-B16FNdW5.d.ts.map +1 -0
  60. package/dist/{index-9jJ2fKSX.d.ts → index-CalAsXMR.d.ts} +7 -5
  61. package/dist/index-CalAsXMR.d.ts.map +1 -0
  62. package/dist/{index-BiT6RtNH.d.ts → index-DbMOXYnP.d.ts} +6 -6
  63. package/dist/{index-BiT6RtNH.d.ts.map → index-DbMOXYnP.d.ts.map} +1 -1
  64. package/dist/index.d.ts +9 -9
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +611 -688
  67. package/dist/index.js.map +1 -1
  68. package/dist/{integrity-DOd9Q86O.js → integrity-C8oUeB0g.js} +322 -197
  69. package/dist/integrity-C8oUeB0g.js.map +1 -0
  70. package/dist/{journal-CR4QikYp.js → journal-C7om8Gum.js} +48 -38
  71. package/dist/journal-C7om8Gum.js.map +1 -0
  72. package/dist/ledger-core/index.d.ts +1 -1
  73. package/dist/ledger-core/index.js +2 -2
  74. package/dist/ledger-core/lock-holder.d.ts +2 -0
  75. package/dist/ledger-core/lock-holder.js +181 -0
  76. package/dist/ledger-core/lock-holder.js.map +1 -0
  77. package/dist/{ledger-core-DrJhbms9.js → ledger-core-CAxUdDk0.js} +2 -2
  78. package/dist/{ledger-core-DrJhbms9.js.map → ledger-core-CAxUdDk0.js.map} +1 -1
  79. package/dist/{llm-client-BshCREn6.js → llm-client-_Y6ic-tf.js} +238 -248
  80. package/dist/llm-client-_Y6ic-tf.js.map +1 -0
  81. package/dist/{llm-judge-C8jMTNNG.js → llm-judge-Djxh-Uhs.js} +646 -458
  82. package/dist/llm-judge-Djxh-Uhs.js.map +1 -0
  83. package/dist/lock-holder-DaJbfpci.d.ts +86 -0
  84. package/dist/lock-holder-DaJbfpci.d.ts.map +1 -0
  85. package/dist/{matrix-DHe-dwVJ.d.ts → matrix-CCSBna4q.d.ts} +2 -2
  86. package/dist/{matrix-DHe-dwVJ.d.ts.map → matrix-CCSBna4q.d.ts.map} +1 -1
  87. package/dist/{mint-zszyYlpA.js → mint-CbdwjTPW.js} +2 -2
  88. package/dist/{mint-zszyYlpA.js.map → mint-CbdwjTPW.js.map} +1 -1
  89. package/dist/multishot/golden/index.d.ts +1 -1
  90. package/dist/multishot/index.d.ts +2 -2
  91. package/dist/openapi.json +1 -1
  92. package/dist/{pareto-BzsxidFD.js → pareto-CTv8vozZ.js} +4 -4
  93. package/dist/{pareto-BzsxidFD.js.map → pareto-CTv8vozZ.js.map} +1 -1
  94. package/dist/{promotion-gate-DaY95yU6.d.ts → promotion-gate-CxEHRsZW.d.ts} +2 -2
  95. package/dist/{promotion-gate-DaY95yU6.d.ts.map → promotion-gate-CxEHRsZW.d.ts.map} +1 -1
  96. package/dist/{proposal-findings-BbWidvUk.js → proposal-findings-n-5WBbEx.js} +2 -2
  97. package/dist/{proposal-findings-BbWidvUk.js.map → proposal-findings-n-5WBbEx.js.map} +1 -1
  98. package/dist/{render-C-w2EZ6p.js → render-Cs34y0iX.js} +404 -208
  99. package/dist/render-Cs34y0iX.js.map +1 -0
  100. package/dist/reporting.d.ts +2 -2
  101. package/dist/{researcher-0ABVIdGa.d.ts → researcher-GU1tV4dn.d.ts} +2 -2
  102. package/dist/{researcher-0ABVIdGa.d.ts.map → researcher-GU1tV4dn.d.ts.map} +1 -1
  103. package/dist/rl.d.ts +2 -2
  104. package/dist/rl.js +4 -4
  105. package/dist/rollout/index.js +5 -5
  106. package/dist/{rollout-D6C50mfA.js → rollout-Dd1ucT_M.js} +5 -5
  107. package/dist/{rollout-D6C50mfA.js.map → rollout-Dd1ucT_M.js.map} +1 -1
  108. package/dist/schema-C-ziyJD7.d.ts.map +1 -1
  109. package/dist/{schema-DxcKTzbi.js → schema-CvhfLjtZ.js} +77 -47
  110. package/dist/schema-CvhfLjtZ.js.map +1 -0
  111. package/dist/search/index.d.ts +2 -2
  112. package/dist/search/index.d.ts.map +1 -1
  113. package/dist/search/index.js +2 -2
  114. package/dist/{search-kernel-DxqjBxA-.d.ts → search-kernel-p9JB3SmL.d.ts} +3 -3
  115. package/dist/{search-kernel-DxqjBxA-.d.ts.map → search-kernel-p9JB3SmL.d.ts.map} +1 -1
  116. package/dist/{search-ledger-TUvB5gWH.js → search-ledger-ClpN4I3e.js} +4 -4
  117. package/dist/{search-ledger-TUvB5gWH.js.map → search-ledger-ClpN4I3e.js.map} +1 -1
  118. package/dist/{search-ledger-file-UKCO2r8a.js → search-ledger-file-Bd5Tioq6.js} +3 -3
  119. package/dist/{search-ledger-file-UKCO2r8a.js.map → search-ledger-file-Bd5Tioq6.js.map} +1 -1
  120. package/dist/{search-shipper-Cw2z3-td.js → search-shipper-Bg2HW3Uv.js} +3 -3
  121. package/dist/{search-shipper-Cw2z3-td.js.map → search-shipper-Bg2HW3Uv.js.map} +1 -1
  122. package/dist/{search-summary-DQIvJsSB.js → search-summary-CrBuvVnf.js} +3 -3
  123. package/dist/{search-summary-DQIvJsSB.js.map → search-summary-CrBuvVnf.js.map} +1 -1
  124. package/dist/{server-DoVbqqtJ.js → server-C6M0qR8h.js} +2 -2
  125. package/dist/{server-DoVbqqtJ.js.map → server-C6M0qR8h.js.map} +1 -1
  126. package/dist/{single-run-lock-U58yf8N6.js → single-run-lock-Dekd8_Wv.js} +7 -7
  127. package/dist/single-run-lock-Dekd8_Wv.js.map +1 -0
  128. package/dist/{skillopt-optimization-method-kyL94-ri.js → skillopt-optimization-method-5dlex7MJ.js} +1339 -1059
  129. package/dist/skillopt-optimization-method-5dlex7MJ.js.map +1 -0
  130. package/dist/{statistical-heldout-C9AbgREN.d.ts → statistical-heldout-BHfLZHrm.d.ts} +2 -2
  131. package/dist/{statistical-heldout-C9AbgREN.d.ts.map → statistical-heldout-BHfLZHrm.d.ts.map} +1 -1
  132. package/dist/{summary-report-DM7tlml1.d.ts → summary-report-CkbRcSuu.d.ts} +16 -1
  133. package/dist/summary-report-CkbRcSuu.d.ts.map +1 -0
  134. package/dist/supervisor-run/index.d.ts.map +1 -1
  135. package/dist/supervisor-run/index.js +3 -3
  136. package/dist/{systemone-Bp_Ac0p1.d.ts → systemone-BMzDJb1i.d.ts} +3 -3
  137. package/dist/{systemone-Bp_Ac0p1.d.ts.map → systemone-BMzDJb1i.d.ts.map} +1 -1
  138. package/dist/systemone.d.ts +2 -2
  139. package/dist/{terminal-record-78g6E4vh.js → terminal-record-D4Rv-j1W.js} +241 -193
  140. package/dist/{terminal-record-78g6E4vh.js.map → terminal-record-D4Rv-j1W.js.map} +1 -1
  141. package/dist/trajectory-replay/index.d.ts +4 -1
  142. package/dist/trajectory-replay/index.d.ts.map +1 -1
  143. package/dist/trajectory-replay/index.js +834 -824
  144. package/dist/trajectory-replay/index.js.map +1 -1
  145. package/dist/{tree-BGRglkKd.js → tree-B5e1uAeL.js} +122 -14
  146. package/dist/tree-B5e1uAeL.js.map +1 -0
  147. package/dist/{types-DtMbFw6U.d.ts → types-BKCzzmit.d.ts} +2 -2
  148. package/dist/{types-DtMbFw6U.d.ts.map → types-BKCzzmit.d.ts.map} +1 -1
  149. package/dist/{types-D8GhJoHk.d.ts → types-DtkO1G9L.d.ts} +2 -2
  150. package/dist/{types-D8GhJoHk.d.ts.map → types-DtkO1G9L.d.ts.map} +1 -1
  151. package/dist/wire/index.js +1 -1
  152. package/docs/design/statistics-decisions.md +1 -1
  153. package/docs/eval-surface-map.md +1 -0
  154. package/docs/profile-json-identity.md +1 -2
  155. package/docs/public-api.md +27 -16
  156. package/package.json +7 -2
  157. package/dist/benchmark-command-xQK8nOI6.js.map +0 -1
  158. package/dist/define-agent-eval-BDxNlP3D.js.map +0 -1
  159. package/dist/eval-campaign-BLEfBGbA.js +0 -343
  160. package/dist/eval-campaign-BLEfBGbA.js.map +0 -1
  161. package/dist/index-9jJ2fKSX.d.ts.map +0 -1
  162. package/dist/index-ZuWx52WG.d.ts.map +0 -1
  163. package/dist/integrity-DOd9Q86O.js.map +0 -1
  164. package/dist/journal-CR4QikYp.js.map +0 -1
  165. package/dist/llm-client-BshCREn6.js.map +0 -1
  166. package/dist/llm-judge-C8jMTNNG.js.map +0 -1
  167. package/dist/render-C-w2EZ6p.js.map +0 -1
  168. package/dist/schema-DxcKTzbi.js.map +0 -1
  169. package/dist/single-run-lock-U58yf8N6.js.map +0 -1
  170. package/dist/skillopt-optimization-method-kyL94-ri.js.map +0 -1
  171. package/dist/summary-report-DM7tlml1.d.ts.map +0 -1
  172. package/dist/tree-BGRglkKd.js.map +0 -1
@@ -8,18 +8,18 @@ import { a as heldoutSignificance, i as dimensionRegressions, o as pairHoldout }
8
8
  import { n as contentHash } from "./verdict-cache-CL_7tePD.js";
9
9
  import { r as projectCampaignCellQuality, t as campaignCellCostProvenance } from "./run-record-CysKLB93.js";
10
10
  import { n as CostAccountingIncompleteError, t as CostLedger } from "./cost-ledger-D0352Z4d.js";
11
- import { A as surfaceHashMatches, D as surfaceContentHash, E as renderSurfaceDiff, F as assertCampaignSplitIdentity, I as assertCompleteCampaign, L as campaignCoverage, O as surfaceDispatchRef, P as assertCampaignDesign, R as campaignScenarioIdentity, V as formatCoverageFailures, b as FinalEvidenceConflictError, f as campaignBreakdown, j as BackendIntegrityError, k as surfaceHash, m as campaignMeanCompositeOrNull, p as campaignMeanComposite, x as FinalEvidenceError, z as campaignSplitDigest } from "./campaign-evidence-CQYw8_0H.js";
11
+ import { A as surfaceHashMatches, D as surfaceContentHash, E as renderSurfaceDiff, F as assertCampaignSplitIdentity, I as assertCompleteCampaign, L as campaignCoverage, O as surfaceDispatchRef, P as assertCampaignDesign, R as campaignScenarioIdentity, V as formatCoverageFailures, b as FinalEvidenceConflictError, f as campaignBreakdown, j as BackendIntegrityError, k as surfaceHash, m as campaignMeanCompositeOrNull, p as campaignMeanComposite, x as FinalEvidenceError, z as campaignSplitDigest } from "./campaign-evidence-DunzcJGv.js";
12
12
  import { t as deepFreezeCanonicalJson } from "./deep-freeze-CurcOQg3.js";
13
- import { M as fsCampaignStorage, h as isExternalTextCandidate, j as createRunCostLedger } from "./external-optimizer-subprocess-XIHBOSKt.js";
13
+ import { M as fsCampaignStorage, h as isExternalTextCandidate, j as createRunCostLedger } from "./external-optimizer-subprocess-B5gdLRZv.js";
14
14
  import { r as summarizeEvaluationUnits, t as defineEvaluationClaim } from "./claim-BPVuyHuy.js";
15
15
  import { d as detectPersonalData, s as redactText, t as REDACTION_VERSION, u as detectCredential } from "./redact-mtkTiJVk.js";
16
16
  import { t as detectRewardHacking } from "./reward-hacking-ByuETaIb.js";
17
- import { t as assertProposalFindings } from "./proposal-findings-BbWidvUk.js";
18
- import { i as runSearch, m as searchExpansionIndex, n as paretoFrontier, o as searchPolicyView, p as SEARCH_KERNEL_SOURCE, r as paretoFrontierWithCrowding, s as EXPANSION_OPERATORS, w as estimateNode } from "./pareto-BzsxidFD.js";
19
- import { a as validateSearchLedgerEvent, d as searchNodeId, f as searchTaskSetDigest, i as replaySearchLedgerText, n as openSearchLedger, u as searchCellId } from "./search-ledger-TUvB5gWH.js";
20
- import { n as searchProposerView, t as renderSearchSummary } from "./search-summary-DQIvJsSB.js";
17
+ import { t as assertProposalFindings } from "./proposal-findings-n-5WBbEx.js";
18
+ import { i as runSearch, m as searchExpansionIndex, n as paretoFrontier, o as searchPolicyView, p as SEARCH_KERNEL_SOURCE, r as paretoFrontierWithCrowding, s as EXPANSION_OPERATORS, w as estimateNode } from "./pareto-CTv8vozZ.js";
19
+ import { a as validateSearchLedgerEvent, d as searchNodeId, f as searchTaskSetDigest, i as replaySearchLedgerText, n as openSearchLedger, u as searchCellId } from "./search-ledger-ClpN4I3e.js";
20
+ import { n as searchProposerView, t as renderSearchSummary } from "./search-summary-CrBuvVnf.js";
21
21
  import { i as stripFencedJson, n as isRecord } from "./json-BhdsJaOQ.js";
22
- import { a as maximumChargeForLlmRequest, i as costReceiptFromLlmError, r as costReceiptFromLlm } from "./llm-client-BshCREn6.js";
22
+ import { a as maximumChargeForLlmRequest, i as costReceiptFromLlmError, r as costReceiptFromLlm } from "./llm-client-_Y6ic-tf.js";
23
23
  import { createHash } from "node:crypto";
24
24
  import { writeFileSync } from "node:fs";
25
25
  import { z } from "zod";
@@ -337,52 +337,41 @@ function hasLlmCall(value) {
337
337
  * persisted cell result. `runCampaign` owns the schedule; this module owns
338
338
  * everything that happens inside a single cell.
339
339
  */
340
- async function executeCell(args) {
341
- const storage = args.storage;
342
- const cellDir = cellDirectory(args.opts.runDir, args.slot.cellId);
343
- storage.ensureDir(cellDir);
344
- const stableCostTags = stableCostTagsFor(args.opts, args.slot);
345
- const costTags = {
346
- ...stableCostTags,
347
- runAttemptId: args.runAttemptId
348
- };
349
- const attemptCostTags = {
350
- ...costTags,
351
- attemptNumber: String(args.attempt)
352
- };
353
- const cachePath = join(cellDir, "cached-result.json");
354
- if (args.resumable) {
355
- const cached = readCachedCell({
356
- storage,
357
- cachePath,
358
- cellId: args.slot.cellId,
359
- manifestHash: args.manifestHash
360
- });
361
- if (cached.status === "miss" && cacheIssueRequiresExplicitRerun(cached.reason) && !args.opts.rerunInvalidCachedCells) throw invalidCachedCellsError([{
340
+ /** A cached cell is reused only when its receipts still match the ledger; invalid cache entries need an explicit rerun. */
341
+ function reuseCachedCell(args, cachePath, stableCostTags) {
342
+ const cached = readCachedCell({
343
+ storage: args.storage,
344
+ cachePath,
345
+ cellId: args.slot.cellId,
346
+ manifestHash: args.manifestHash
347
+ });
348
+ if (cached.status === "miss" && cacheIssueRequiresExplicitRerun(cached.reason) && !args.opts.rerunInvalidCachedCells) throw invalidCachedCellsError([{
349
+ cellId: args.slot.cellId,
350
+ reason: cached.reason
351
+ }]);
352
+ if (cached.status === "hit" && (!cached.cell.error || args.opts.reuseFailedCells)) {
353
+ const receiptProblem = cachedCellReceiptProblem(cached.cell, args.costLedger, stableCostTags);
354
+ if (receiptProblem === void 0) {
355
+ const cell = withCurrentAgentModelEvidence(cached.cell, args.costLedger, stableCostTags);
356
+ enforceDispatchUsage(cell, args.opts.expectUsage ?? "warn");
357
+ return {
358
+ cell: {
359
+ ...cell,
360
+ cached: true
361
+ },
362
+ artifactsByPath: {}
363
+ };
364
+ }
365
+ if (!args.opts.rerunInvalidCachedCells) throw invalidCachedCellsError([{
362
366
  cellId: args.slot.cellId,
363
- reason: cached.reason
367
+ reason: "invalid-cost-receipts",
368
+ detail: receiptProblem
364
369
  }]);
365
- if (cached.status === "hit" && (!cached.cell.error || args.opts.reuseFailedCells)) {
366
- const receiptProblem = cachedCellReceiptProblem(cached.cell, args.costLedger, stableCostTags);
367
- if (receiptProblem === void 0) {
368
- const cell = withCurrentAgentModelEvidence(cached.cell, args.costLedger, stableCostTags);
369
- enforceDispatchUsage(cell, args.opts.expectUsage ?? "warn");
370
- return {
371
- cell: {
372
- ...cell,
373
- cached: true
374
- },
375
- artifactsByPath: {}
376
- };
377
- }
378
- if (!args.opts.rerunInvalidCachedCells) throw invalidCachedCellsError([{
379
- cellId: args.slot.cellId,
380
- reason: "invalid-cost-receipts",
381
- detail: receiptProblem
382
- }]);
383
- }
384
370
  }
385
- const startMs = Date.now();
371
+ }
372
+ /** The directories, writers, abort chain and dispatch context of one execution attempt. */
373
+ function openAttempt(args, cellDir, attemptCostTags) {
374
+ const { storage } = args;
386
375
  const attempt = {
387
376
  runAttemptId: args.runAttemptId,
388
377
  number: args.attempt
@@ -407,7 +396,7 @@ async function executeCell(args) {
407
396
  artifactsByPath[`${args.slot.cellId}/${attemptPrefix}/identity.json`] = identityPath;
408
397
  storage.write(join(cellDir, "latest-attempt.json"), JSON.stringify(attempt));
409
398
  const trace = args.buildTraceWriter(args.slot.cellId, traceDir);
410
- let paidCallStarted = false;
399
+ const paid = { started: false };
411
400
  const artifacts = {
412
401
  async write(path, content) {
413
402
  const fullPath = resolve(artifactDir, path);
@@ -422,8 +411,10 @@ async function executeCell(args) {
422
411
  return artifacts.write(path, JSON.stringify(value, null, 2));
423
412
  }
424
413
  };
414
+ const cellAbort = new AbortController();
415
+ const onCampaignAbort = () => cellAbort.abort(args.signal.reason);
425
416
  const cost = { async runPaidCall(input) {
426
- paidCallStarted = true;
417
+ paid.started = true;
427
418
  const result = await args.costLedger.runPaidCall({
428
419
  ...input,
429
420
  channel: input.channel ?? "agent",
@@ -439,25 +430,36 @@ async function executeCell(args) {
439
430
  scenario: args.slot.scenario,
440
431
  rep: args.slot.rep
441
432
  });
442
- const cellAbort = new AbortController();
443
- const onCampaignAbort = () => cellAbort.abort(args.signal.reason);
444
433
  if (args.signal.aborted) cellAbort.abort(args.signal.reason);
445
434
  else args.signal.addEventListener("abort", onCampaignAbort, { once: true });
446
- const ctx = {
447
- cellId: args.slot.cellId,
448
- runAttemptId: args.runAttemptId,
449
- rep: args.slot.rep,
450
- seed: args.slot.cellSeed,
451
- signal: cellAbort.signal,
435
+ return {
436
+ attempt,
437
+ attemptDir,
438
+ attemptPrefix,
439
+ artifactsByPath,
452
440
  trace,
453
- artifacts,
454
- cost,
455
- placement
441
+ paid,
442
+ cellAbort,
443
+ onCampaignAbort,
444
+ ctx: {
445
+ cellId: args.slot.cellId,
446
+ runAttemptId: args.runAttemptId,
447
+ rep: args.slot.rep,
448
+ seed: args.slot.cellSeed,
449
+ signal: cellAbort.signal,
450
+ trace,
451
+ artifacts,
452
+ cost,
453
+ placement
454
+ }
456
455
  };
456
+ }
457
+ /** Dispatch the scenario under the per-cell abort and the dispatch deadline. A failure is recorded, not thrown. */
458
+ async function dispatchCell(args, run) {
459
+ const { cellAbort, onCampaignAbort, ctx } = run;
457
460
  let artifact;
458
461
  let errorMessage;
459
462
  let failure;
460
- let fatalCellError;
461
463
  let dispatched;
462
464
  const timeoutMs = args.dispatchTimeoutMs;
463
465
  let timeoutTimer;
@@ -500,29 +502,41 @@ async function executeCell(args) {
500
502
  removeAbortListener();
501
503
  args.signal.removeEventListener("abort", onCampaignAbort);
502
504
  }
505
+ return {
506
+ artifact,
507
+ errorMessage,
508
+ failure,
509
+ dispatched
510
+ };
511
+ }
512
+ /** A dispatch that ignored cancellation, or paid calls that never settled, mean no trustworthy cell result exists. */
513
+ async function awaitCellShutdown(args, run, dispatched, costTags) {
514
+ const { trace, paid } = run;
503
515
  if (dispatched) {
504
516
  if (!await settlesWithin(dispatched, args.dispatchShutdownTimeoutMs)) {
505
517
  await trace.flush();
506
518
  throw new CostAccountingIncompleteError(`dispatch for cell '${args.slot.cellId}' ignored cancellation and did not stop within ${args.dispatchShutdownTimeoutMs}ms; no campaign result was produced`);
507
519
  }
508
520
  }
509
- if (paidCallStarted) {
510
- if (typeof args.costLedger.waitForIdle !== "function") {
511
- await trace.flush();
512
- throw new CostAccountingIncompleteError(`cost ledger for cell '${args.slot.cellId}' cannot prove that paid calls stopped`);
513
- }
514
- if (!await args.costLedger.waitForIdle({
515
- timeoutMs: args.dispatchShutdownTimeoutMs,
516
- filter: {
517
- channel: "agent",
518
- phase: args.costPhase,
519
- tags: costTags
520
- }
521
- })) {
522
- await trace.flush();
523
- throw new CostAccountingIncompleteError(`paid calls for cell '${args.slot.cellId}' did not settle within ${args.dispatchShutdownTimeoutMs}ms; no campaign result was produced`);
521
+ if (!paid.started) return;
522
+ if (typeof args.costLedger.waitForIdle !== "function") {
523
+ await trace.flush();
524
+ throw new CostAccountingIncompleteError(`cost ledger for cell '${args.slot.cellId}' cannot prove that paid calls stopped`);
525
+ }
526
+ if (!await args.costLedger.waitForIdle({
527
+ timeoutMs: args.dispatchShutdownTimeoutMs,
528
+ filter: {
529
+ channel: "agent",
530
+ phase: args.costPhase,
531
+ tags: costTags
524
532
  }
533
+ })) {
534
+ await trace.flush();
535
+ throw new CostAccountingIncompleteError(`paid calls for cell '${args.slot.cellId}' did not settle within ${args.dispatchShutdownTimeoutMs}ms; no campaign result was produced`);
525
536
  }
537
+ }
538
+ /** The agent spend of this attempt: cost, token usage and the models that served it. */
539
+ function measureAgentUsage(args, costTags) {
526
540
  const agentReceipts = args.costLedger.list({
527
541
  channel: "agent",
528
542
  tags: costTags
@@ -531,29 +545,29 @@ async function executeCell(args) {
531
545
  channel: "agent",
532
546
  tags: costTags
533
547
  });
534
- const tokenUsage = {
535
- input: agentCost.inputTokens,
536
- output: agentCost.outputTokens,
537
- ...agentCost.usageComplete ? {} : { tokensKnown: false },
538
- ...agentCost.reasoningTokens !== void 0 && agentCost.reasoningTokens > 0 ? { reasoning: agentCost.reasoningTokens } : {},
539
- ...agentCost.cachedTokens > 0 ? { cached: agentCost.cachedTokens } : {},
540
- ...agentCost.cacheWriteTokens !== void 0 && agentCost.cacheWriteTokens > 0 ? { cacheWrite: agentCost.cacheWriteTokens } : {}
541
- };
542
- const agentModelEvidence = modelEvidenceFromReceipts(agentReceipts);
543
- const dispatchResult = {
544
- cellId: args.slot.cellId,
545
- artifact,
546
- error: errorMessage,
547
- costUsd: agentCost.totalCostUsd,
548
- tokenUsage
548
+ return {
549
+ agentCost,
550
+ tokenUsage: {
551
+ input: agentCost.inputTokens,
552
+ output: agentCost.outputTokens,
553
+ ...agentCost.usageComplete ? {} : { tokensKnown: false },
554
+ ...agentCost.reasoningTokens !== void 0 && agentCost.reasoningTokens > 0 ? { reasoning: agentCost.reasoningTokens } : {},
555
+ ...agentCost.cachedTokens > 0 ? { cached: agentCost.cachedTokens } : {},
556
+ ...agentCost.cacheWriteTokens !== void 0 && agentCost.cacheWriteTokens > 0 ? { cacheWrite: agentCost.cacheWriteTokens } : {}
557
+ },
558
+ agentModelEvidence: modelEvidenceFromReceipts(agentReceipts)
549
559
  };
550
- try {
551
- enforceDispatchUsage(dispatchResult, args.opts.expectUsage ?? "warn");
552
- } catch (error) {
553
- await trace.flush();
554
- throw error;
555
- }
560
+ }
561
+ /**
562
+ * Run judges (only if we have an artifact). A judge that throws invalidates
563
+ * the cell — recorded as `error`, NOT folded into a fake composite:0 (a fake
564
+ * zero is indistinguishable from a real zero and poisons every aggregate).
565
+ */
566
+ async function judgeArtifact(args, artifact, attemptCostTags) {
556
567
  const judgeScores = {};
568
+ let errorMessage;
569
+ let failure;
570
+ let fatalCellError;
557
571
  if (artifact !== void 0) for (const judge of args.opts.judges ?? []) {
558
572
  if (judge.appliesTo && !judge.appliesTo(args.slot.scenario)) continue;
559
573
  try {
@@ -577,6 +591,79 @@ async function executeCell(args) {
577
591
  break;
578
592
  }
579
593
  }
594
+ return {
595
+ judgeScores,
596
+ errorMessage,
597
+ failure,
598
+ fatalCellError
599
+ };
600
+ }
601
+ /** Write the failure receipt and decide whether `cellRetry` dispatches this slot again. */
602
+ function recordCellFailure(args, run, failure, cell, costTags, fatalCellError) {
603
+ const { attemptDir, attemptPrefix, artifactsByPath } = run;
604
+ const receipt = {
605
+ schemaVersion: 1,
606
+ runAttemptId: args.runAttemptId,
607
+ recordedAt: args.now().toISOString(),
608
+ failure: {
609
+ stage: failure.stage,
610
+ ...failure.judge ? { judge: failure.judge } : {},
611
+ error: serializeCellError(failure.cause)
612
+ },
613
+ cell,
614
+ cost: args.costLedger.summary({
615
+ phase: args.costPhase,
616
+ tags: costTags
617
+ })
618
+ };
619
+ const retry = args.cellRetry !== void 0 && args.attempt < args.cellRetry.attempts && !args.signal.aborted && fatalCellError === void 0 && args.cellRetry.retryable(receipt.failure);
620
+ const failurePath = join(attemptDir, "failure-receipt.json");
621
+ args.storage.write(failurePath, JSON.stringify(receipt, null, 2));
622
+ artifactsByPath[`${args.slot.cellId}/${attemptPrefix}/failure-receipt.json`] = failurePath;
623
+ if (!retry) args.onFailure?.(failure);
624
+ return retry;
625
+ }
626
+ async function executeCell(args) {
627
+ const storage = args.storage;
628
+ const cellDir = cellDirectory(args.opts.runDir, args.slot.cellId);
629
+ storage.ensureDir(cellDir);
630
+ const stableCostTags = stableCostTagsFor(args.opts, args.slot);
631
+ const costTags = {
632
+ ...stableCostTags,
633
+ runAttemptId: args.runAttemptId
634
+ };
635
+ const attemptCostTags = {
636
+ ...costTags,
637
+ attemptNumber: String(args.attempt)
638
+ };
639
+ const cachePath = join(cellDir, "cached-result.json");
640
+ if (args.resumable) {
641
+ const reused = reuseCachedCell(args, cachePath, stableCostTags);
642
+ if (reused) return reused;
643
+ }
644
+ const startMs = Date.now();
645
+ const run = openAttempt(args, cellDir, attemptCostTags);
646
+ const { attempt, attemptDir, attemptPrefix, artifactsByPath, trace } = run;
647
+ const dispatch = await dispatchCell(args, run);
648
+ await awaitCellShutdown(args, run, dispatch.dispatched, costTags);
649
+ const { artifact } = dispatch;
650
+ const { agentCost, tokenUsage, agentModelEvidence } = measureAgentUsage(args, costTags);
651
+ try {
652
+ enforceDispatchUsage({
653
+ cellId: args.slot.cellId,
654
+ artifact,
655
+ error: dispatch.errorMessage,
656
+ costUsd: agentCost.totalCostUsd,
657
+ tokenUsage
658
+ }, args.opts.expectUsage ?? "warn");
659
+ } catch (error) {
660
+ await trace.flush();
661
+ throw error;
662
+ }
663
+ const judged = await judgeArtifact(args, artifact, attemptCostTags);
664
+ const failure = judged.failure ?? dispatch.failure;
665
+ const errorMessage = judged.failure ? judged.errorMessage : dispatch.errorMessage;
666
+ const { judgeScores, fatalCellError } = judged;
580
667
  if (failure) await waitForFailedCellCostSettlement({
581
668
  costLedger: args.costLedger,
582
669
  costPhase: args.costPhase,
@@ -606,29 +693,7 @@ async function executeCell(args) {
606
693
  ...failure?.judge ? { errorJudge: failure.judge } : {},
607
694
  error: errorMessage
608
695
  };
609
- let retry = false;
610
- if (failure) {
611
- const receipt = {
612
- schemaVersion: 1,
613
- runAttemptId: args.runAttemptId,
614
- recordedAt: args.now().toISOString(),
615
- failure: {
616
- stage: failure.stage,
617
- ...failure.judge ? { judge: failure.judge } : {},
618
- error: serializeCellError(failure.cause)
619
- },
620
- cell,
621
- cost: args.costLedger.summary({
622
- phase: args.costPhase,
623
- tags: costTags
624
- })
625
- };
626
- retry = args.cellRetry !== void 0 && args.attempt < args.cellRetry.attempts && !args.signal.aborted && fatalCellError === void 0 && args.cellRetry.retryable(receipt.failure);
627
- const failurePath = join(attemptDir, "failure-receipt.json");
628
- storage.write(failurePath, JSON.stringify(receipt, null, 2));
629
- artifactsByPath[`${args.slot.cellId}/${attemptPrefix}/failure-receipt.json`] = failurePath;
630
- if (!retry) args.onFailure?.(failure);
631
- }
696
+ const retry = failure ? recordCellFailure(args, run, failure, cell, costTags, fatalCellError) : false;
632
697
  await trace.flush();
633
698
  const resultPath = join(attemptDir, "result.json");
634
699
  storage.write(resultPath, JSON.stringify(cell));
@@ -1890,261 +1955,269 @@ function excerptAt(source, at, needleLength) {
1890
1955
  return (start > 0 ? "…" : "") + source.slice(start, end) + (end < source.length ? "…" : "");
1891
1956
  }
1892
1957
  //#endregion
1893
- //#region src/campaign/gates/default-production-gate.ts
1894
- /**
1895
- * Opinionated production gate composing held-out significance, red-team, reward-hacking, and canary checks into a single `Gate.decide` decision.
1896
- */
1897
- function defaultProductionGate(options) {
1898
- const deltaThreshold = options.deltaThreshold ?? 0;
1899
- const confidence = options.confidence ?? .95;
1900
- const resamples = options.bootstrapResamples ?? 2e3;
1901
- const seed = options.bootstrapSeed ?? 1337;
1902
- const minProductiveRuns = options.minProductiveRuns ?? 3;
1903
- const heldoutStatistic = options.heldoutStatistic ?? "mean";
1904
- const explicitlyRequired = new Set(options.requiredChecks ?? []);
1905
- const scenarioIds = new Set(options.holdoutScenarios.map((scenario) => scenario.id));
1906
- const independentUnitByScenarioId = options.independentUnitByScenarioId === void 0 ? void 0 : new Map(options.independentUnitByScenarioId);
1907
- if (independentUnitByScenarioId !== void 0) for (const id of scenarioIds) {
1908
- const unit = independentUnitByScenarioId.get(id);
1909
- if (typeof unit !== "string" || unit.length === 0 || unit.trim() !== unit) throw new Error(`defaultProductionGate: missing independent unit for holdout scenario '${id}'`);
1910
- }
1958
+ //#region src/campaign/gates/default-production-checks.ts
1959
+ function newFindings() {
1960
+ const reasons = [];
1961
+ const contributing = [];
1962
+ const requiredUnavailable = /* @__PURE__ */ new Set();
1963
+ const unavailable = (name, required, reason, detail = {}) => {
1964
+ contributing.push({
1965
+ name,
1966
+ status: "not_evaluated",
1967
+ detail: {
1968
+ reason,
1969
+ required,
1970
+ ...detail
1971
+ }
1972
+ });
1973
+ if (required) {
1974
+ requiredUnavailable.add(name);
1975
+ reasons.push(`${name}: ${reason}`);
1976
+ }
1977
+ };
1911
1978
  return {
1912
- name: "defaultProductionGate",
1913
- async decide(ctx) {
1914
- const reasons = [];
1915
- const contributing = [];
1916
- const requiredUnavailable = /* @__PURE__ */ new Set();
1917
- const unavailable = (name, required, reason, detail = {}) => {
1918
- contributing.push({
1919
- name,
1920
- status: "not_evaluated",
1921
- detail: {
1922
- reason,
1923
- required,
1924
- ...detail
1925
- }
1926
- });
1927
- if (required) {
1928
- requiredUnavailable.add(name);
1929
- reasons.push(`${name}: ${reason}`);
1930
- }
1931
- };
1932
- let delta;
1933
- if (!ctx.baselineJudgeScores) unavailable("heldout-significance", true, "baselineJudgeScores is required for a held-out comparison");
1934
- else {
1935
- const sig = heldoutSignificance(pairHoldout(ctx.judgeScores, ctx.baselineJudgeScores, scenarioIds, (s) => s.composite), {
1936
- deltaThreshold,
1937
- minProductiveRuns,
1938
- confidence,
1939
- resamples,
1940
- seed,
1941
- statistic: heldoutStatistic,
1942
- independentUnitByScenarioId
1943
- });
1944
- const dec = sig.decision;
1945
- delta = dec.delta;
1946
- const heldoutPass = sig.significant;
1947
- contributing.push({
1948
- name: "heldout-significance",
1949
- status: sig.fewRuns ? "not_evaluated" : heldoutPass ? "pass" : "fail",
1950
- detail: {
1951
- n: sig.n,
1952
- pairedCellN: sig.pairedCellN,
1953
- observationUnit: sig.observationUnit,
1954
- unitIds: sig.unitIds,
1955
- delta,
1956
- decisionStatistic: sig.decisionStatistic,
1957
- decisionMethod: sig.decisionMethod,
1958
- binaryScale: dec.binaryScale,
1959
- mcnemar: dec.mcnemar,
1960
- indeterminate: dec.indeterminate,
1961
- deltaMean: sig.bootstrap.mean,
1962
- deltaMedianDiagnostic: sig.medianBootstrap.median,
1963
- deltaMedian: sig.medianBootstrap.median,
1964
- tieFraction: sig.tieFraction,
1965
- ciLow: dec.low,
1966
- ciHigh: dec.high,
1967
- bootstrapCiLow: sig.bootstrap.low,
1968
- bootstrapCiHigh: sig.bootstrap.high,
1969
- confidence: dec.confidence,
1970
- deltaThreshold,
1971
- fewRuns: sig.fewRuns
1972
- }
1973
- });
1974
- if (sig.fewRuns) requiredUnavailable.add("heldout-significance");
1975
- if (!heldoutPass) {
1976
- const tieNote = sig.tieFraction >= .4 ? `; ${(sig.tieFraction * 100).toFixed(0)}% tied observation units` : "";
1977
- const ci = `${(dec.confidence * 100).toFixed(0)}% CI [${dec.low.toFixed(3)}, ${dec.high.toFixed(3)}]`;
1978
- reasons.push(sig.fewRuns ? `held-out: only ${sig.n} paired observation units (< ${sig.minimumRequired}) — too few to claim significance` : dec.indeterminate ? `held-out: ${dec.indeterminateCause}, so the paired CI is ${ci} and carries no direction — it cannot clear threshold ${deltaThreshold} on evidence${tieNote}` : dec.exactTestVetoes ? `held-out: McNemar exact p=${dec.mcnemar?.pValue.toExponential(2)} does not reject at α=${(1 - dec.confidence).toFixed(4)} (${dec.label} Δ ${delta.toFixed(3)}, ${ci}${tieNote})` : dec.method === "exact-sign" ? `held-out: exact one-sided sign test p=${dec.pValue} does not reject at α=${((1 - dec.confidence) / 2).toFixed(4)} (${dec.label} Δ ${delta.toFixed(3)}, diagnostic ${ci}${tieNote})` : `held-out CI.low ${dec.low.toFixed(3)} ≤ threshold ${deltaThreshold} (${dec.label} Δ ${delta.toFixed(3)}, ${ci}${tieNote})`);
1979
- }
1979
+ reasons,
1980
+ contributing,
1981
+ requiredUnavailable,
1982
+ unavailable
1983
+ };
1984
+ }
1985
+ /** Held-out significance of the candidate over the baseline, paired by cell before any averaging. */
1986
+ function checkHeldout(g, cfg, ctx) {
1987
+ const { reasons, contributing, requiredUnavailable, unavailable } = g;
1988
+ const { deltaThreshold, confidence, resamples, seed, minProductiveRuns, heldoutStatistic, scenarioIds, independentUnitByScenarioId } = cfg;
1989
+ let delta;
1990
+ if (!ctx.baselineJudgeScores) unavailable("heldout-significance", true, "baselineJudgeScores is required for a held-out comparison");
1991
+ else {
1992
+ const sig = heldoutSignificance(pairHoldout(ctx.judgeScores, ctx.baselineJudgeScores, scenarioIds, (s) => s.composite), {
1993
+ deltaThreshold,
1994
+ minProductiveRuns,
1995
+ confidence,
1996
+ resamples,
1997
+ seed,
1998
+ statistic: heldoutStatistic,
1999
+ independentUnitByScenarioId
2000
+ });
2001
+ const dec = sig.decision;
2002
+ delta = dec.delta;
2003
+ const heldoutPass = sig.significant;
2004
+ contributing.push({
2005
+ name: "heldout-significance",
2006
+ status: sig.fewRuns ? "not_evaluated" : heldoutPass ? "pass" : "fail",
2007
+ detail: {
2008
+ n: sig.n,
2009
+ pairedCellN: sig.pairedCellN,
2010
+ observationUnit: sig.observationUnit,
2011
+ unitIds: sig.unitIds,
2012
+ delta,
2013
+ decisionStatistic: sig.decisionStatistic,
2014
+ decisionMethod: sig.decisionMethod,
2015
+ binaryScale: dec.binaryScale,
2016
+ mcnemar: dec.mcnemar,
2017
+ indeterminate: dec.indeterminate,
2018
+ deltaMean: sig.bootstrap.mean,
2019
+ deltaMedianDiagnostic: sig.medianBootstrap.median,
2020
+ deltaMedian: sig.medianBootstrap.median,
2021
+ tieFraction: sig.tieFraction,
2022
+ ciLow: dec.low,
2023
+ ciHigh: dec.high,
2024
+ bootstrapCiLow: sig.bootstrap.low,
2025
+ bootstrapCiHigh: sig.bootstrap.high,
2026
+ confidence: dec.confidence,
2027
+ deltaThreshold,
2028
+ fewRuns: sig.fewRuns
1980
2029
  }
1981
- const dimensionsProvided = options.criticalDimensions !== void 0;
1982
- const criticalDimensions = [...new Set(options.criticalDimensions ?? [])];
1983
- const dimensionsRequired = dimensionsProvided || explicitlyRequired.has("dimension-regression");
1984
- if (!dimensionsProvided) unavailable("dimension-regression", dimensionsRequired, "criticalDimensions is not configured");
1985
- else if (criticalDimensions.length === 0) unavailable("dimension-regression", true, "criticalDimensions must contain at least one dimension");
1986
- else if (!ctx.baselineJudgeScores) unavailable("dimension-regression", true, "baselineJudgeScores is required to measure dimension regressions", { guarded: criticalDimensions });
1987
- else {
1988
- const dimRegs = dimensionRegressions(ctx.judgeScores, ctx.baselineJudgeScores, scenarioIds, criticalDimensions, {
1989
- tolerance: options.regressionTolerance,
1990
- confidence,
1991
- resamples,
1992
- seed,
1993
- independentUnitByScenarioId,
1994
- minProductiveRuns
1995
- });
1996
- const measured = new Set(dimRegs.map((result) => result.dimension));
1997
- const missingDimensions = criticalDimensions.filter((dimension) => !measured.has(dimension));
1998
- const regressed = dimRegs.filter((result) => result.regressed);
1999
- const incompleteDimensions = dimRegs.filter((result) => result.fewRuns || result.missingCellIds.length > 0 || result.missingScenarioIds.length > 0);
2000
- const dimensionStatus = regressed.length > 0 ? "fail" : missingDimensions.length > 0 || incompleteDimensions.length > 0 ? "not_evaluated" : "pass";
2001
- contributing.push({
2002
- name: "dimension-regression",
2003
- status: dimensionStatus,
2004
- detail: {
2005
- guarded: criticalDimensions,
2006
- missingDimensions,
2007
- incompleteDimensions: incompleteDimensions.map((result) => result.dimension),
2008
- regressions: dimRegs.map((result) => ({
2009
- dimension: result.dimension,
2010
- ciLow: result.ci.low,
2011
- ciHigh: result.ci.high,
2012
- decisionStatistic: result.decisionStatistic,
2013
- indeterminate: result.indeterminate,
2014
- bootstrapCiLow: result.bootstrap.low,
2015
- median: result.bootstrap.median,
2016
- tolerance: result.tolerance,
2017
- n: result.n,
2018
- pairedCellN: result.pairedCellN,
2019
- observationUnit: result.observationUnit,
2020
- minimumRequired: result.minimumRequired,
2021
- fewRuns: result.fewRuns,
2022
- missingCellIds: result.missingCellIds,
2023
- missingScenarioIds: result.missingScenarioIds,
2024
- regressed: result.regressed
2025
- }))
2026
- }
2027
- });
2028
- if (missingDimensions.length > 0) {
2029
- requiredUnavailable.add("dimension-regression");
2030
- reasons.push(`critical dimension(s) were not scored: ${missingDimensions.join(", ")}`);
2031
- }
2032
- if (incompleteDimensions.length > 0) {
2033
- requiredUnavailable.add("dimension-regression");
2034
- reasons.push(`critical dimension evidence is incomplete: ${incompleteDimensions.map((result) => `${result.dimension} has ${result.n} paired observation units (minimum ${result.minimumRequired}), ${result.missingCellIds.length} unscored cells, ${result.missingScenarioIds.length} unscored scenarios`).join("; ")}`);
2035
- }
2036
- if (regressed.length > 0) reasons.push(`critical dimension(s) regressed: ${regressed.map((result) => result.bootstrap.low < -result.tolerance ? `${result.dimension} bootstrap CI.low ${result.bootstrap.low.toFixed(3)} < -${result.tolerance}` : `${result.dimension} paired test supports a drop greater than ${result.tolerance}`).join("; ")}`);
2030
+ });
2031
+ if (sig.fewRuns) requiredUnavailable.add("heldout-significance");
2032
+ if (!heldoutPass) {
2033
+ const tieNote = sig.tieFraction >= .4 ? `; ${(sig.tieFraction * 100).toFixed(0)}% tied observation units` : "";
2034
+ const ci = `${(dec.confidence * 100).toFixed(0)}% CI [${dec.low.toFixed(3)}, ${dec.high.toFixed(3)}]`;
2035
+ reasons.push(sig.fewRuns ? `held-out: only ${sig.n} paired observation units (< ${sig.minimumRequired}) — too few to claim significance` : dec.indeterminate ? `held-out: ${dec.indeterminateCause}, so the paired CI is ${ci} and carries no direction — it cannot clear threshold ${deltaThreshold} on evidence${tieNote}` : dec.exactTestVetoes ? `held-out: McNemar exact p=${dec.mcnemar?.pValue.toExponential(2)} does not reject at α=${(1 - dec.confidence).toFixed(4)} (${dec.label} Δ ${delta.toFixed(3)}, ${ci}${tieNote})` : dec.method === "exact-sign" ? `held-out: exact one-sided sign test p=${dec.pValue} does not reject at α=${((1 - dec.confidence) / 2).toFixed(4)} (${dec.label} Δ ${delta.toFixed(3)}, diagnostic ${ci}${tieNote})` : `held-out CI.low ${dec.low.toFixed(3)} ≤ threshold ${deltaThreshold} (${dec.label} Δ ${delta.toFixed(3)}, ${ci}${tieNote})`);
2036
+ }
2037
+ }
2038
+ return delta;
2039
+ }
2040
+ /** Anti-Goodhart guard: a net composite gain must not hide a regression on a critical dimension. */
2041
+ function checkDimensions(g, cfg, ctx) {
2042
+ const { reasons, contributing, requiredUnavailable, unavailable } = g;
2043
+ const { options, confidence, resamples, seed, minProductiveRuns, explicitlyRequired, scenarioIds, independentUnitByScenarioId } = cfg;
2044
+ const dimensionsProvided = options.criticalDimensions !== void 0;
2045
+ const criticalDimensions = [...new Set(options.criticalDimensions ?? [])];
2046
+ const dimensionsRequired = dimensionsProvided || explicitlyRequired.has("dimension-regression");
2047
+ if (!dimensionsProvided) unavailable("dimension-regression", dimensionsRequired, "criticalDimensions is not configured");
2048
+ else if (criticalDimensions.length === 0) unavailable("dimension-regression", true, "criticalDimensions must contain at least one dimension");
2049
+ else if (!ctx.baselineJudgeScores) unavailable("dimension-regression", true, "baselineJudgeScores is required to measure dimension regressions", { guarded: criticalDimensions });
2050
+ else {
2051
+ const dimRegs = dimensionRegressions(ctx.judgeScores, ctx.baselineJudgeScores, scenarioIds, criticalDimensions, {
2052
+ tolerance: options.regressionTolerance,
2053
+ confidence,
2054
+ resamples,
2055
+ seed,
2056
+ independentUnitByScenarioId,
2057
+ minProductiveRuns
2058
+ });
2059
+ const measured = new Set(dimRegs.map((result) => result.dimension));
2060
+ const missingDimensions = criticalDimensions.filter((dimension) => !measured.has(dimension));
2061
+ const regressed = dimRegs.filter((result) => result.regressed);
2062
+ const incompleteDimensions = dimRegs.filter((result) => result.fewRuns || result.missingCellIds.length > 0 || result.missingScenarioIds.length > 0);
2063
+ const dimensionStatus = regressed.length > 0 ? "fail" : missingDimensions.length > 0 || incompleteDimensions.length > 0 ? "not_evaluated" : "pass";
2064
+ contributing.push({
2065
+ name: "dimension-regression",
2066
+ status: dimensionStatus,
2067
+ detail: {
2068
+ guarded: criticalDimensions,
2069
+ missingDimensions,
2070
+ incompleteDimensions: incompleteDimensions.map((result) => result.dimension),
2071
+ regressions: dimRegs.map((result) => ({
2072
+ dimension: result.dimension,
2073
+ ciLow: result.ci.low,
2074
+ ciHigh: result.ci.high,
2075
+ decisionStatistic: result.decisionStatistic,
2076
+ indeterminate: result.indeterminate,
2077
+ bootstrapCiLow: result.bootstrap.low,
2078
+ median: result.bootstrap.median,
2079
+ tolerance: result.tolerance,
2080
+ n: result.n,
2081
+ pairedCellN: result.pairedCellN,
2082
+ observationUnit: result.observationUnit,
2083
+ minimumRequired: result.minimumRequired,
2084
+ fewRuns: result.fewRuns,
2085
+ missingCellIds: result.missingCellIds,
2086
+ missingScenarioIds: result.missingScenarioIds,
2087
+ regressed: result.regressed
2088
+ }))
2037
2089
  }
2038
- const budgetUsd = options.budgetUsd;
2039
- const budgetConfigured = budgetUsd !== void 0;
2040
- const budgetRequired = budgetConfigured || explicitlyRequired.has("budget");
2041
- if (!budgetConfigured) unavailable("budget", budgetRequired, "budgetUsd is not configured");
2042
- else if (!ctx.costLedger) unavailable("budget", true, "costLedger is required to measure complete run spend", { budgetUsd });
2043
- else {
2044
- const cost = ctx.costLedger.summary();
2045
- const accountingComplete = cost.accountingComplete && cost.pendingCalls === 0 && cost.unresolvedCalls === 0;
2046
- const budgetPass = cost.totalCostUsd <= budgetUsd;
2047
- contributing.push({
2048
- name: "budget",
2049
- status: !accountingComplete ? "not_evaluated" : budgetPass ? "pass" : "fail",
2050
- detail: {
2051
- totalCostUsd: cost.totalCostUsd,
2052
- budgetUsd,
2053
- accountingComplete,
2054
- pendingCalls: cost.pendingCalls,
2055
- unresolvedCalls: cost.unresolvedCalls
2056
- }
2057
- });
2058
- if (!accountingComplete) {
2059
- requiredUnavailable.add("budget");
2060
- reasons.push("budget: cost accounting is incomplete");
2061
- } else if (!budgetPass) reasons.push(`spend ${cost.totalCostUsd.toFixed(2)} > budget ${budgetUsd}`);
2090
+ });
2091
+ if (missingDimensions.length > 0) {
2092
+ requiredUnavailable.add("dimension-regression");
2093
+ reasons.push(`critical dimension(s) were not scored: ${missingDimensions.join(", ")}`);
2094
+ }
2095
+ if (incompleteDimensions.length > 0) {
2096
+ requiredUnavailable.add("dimension-regression");
2097
+ reasons.push(`critical dimension evidence is incomplete: ${incompleteDimensions.map((result) => `${result.dimension} has ${result.n} paired observation units (minimum ${result.minimumRequired}), ${result.missingCellIds.length} unscored cells, ${result.missingScenarioIds.length} unscored scenarios`).join("; ")}`);
2098
+ }
2099
+ if (regressed.length > 0) reasons.push(`critical dimension(s) regressed: ${regressed.map((result) => result.bootstrap.low < -result.tolerance ? `${result.dimension} bootstrap CI.low ${result.bootstrap.low.toFixed(3)} < -${result.tolerance}` : `${result.dimension} paired test supports a drop greater than ${result.tolerance}`).join("; ")}`);
2100
+ }
2101
+ }
2102
+ /** Total spend of the improvement run against `budgetUsd`, from complete cost accounting only. */
2103
+ function checkBudget(g, cfg, ctx) {
2104
+ const { reasons, contributing, requiredUnavailable, unavailable } = g;
2105
+ const { options, explicitlyRequired } = cfg;
2106
+ const budgetUsd = options.budgetUsd;
2107
+ const budgetConfigured = budgetUsd !== void 0;
2108
+ const budgetRequired = budgetConfigured || explicitlyRequired.has("budget");
2109
+ if (!budgetConfigured) unavailable("budget", budgetRequired, "budgetUsd is not configured");
2110
+ else if (!ctx.costLedger) unavailable("budget", true, "costLedger is required to measure complete run spend", { budgetUsd });
2111
+ else {
2112
+ const cost = ctx.costLedger.summary();
2113
+ const accountingComplete = cost.accountingComplete && cost.pendingCalls === 0 && cost.unresolvedCalls === 0;
2114
+ const budgetPass = cost.totalCostUsd <= budgetUsd;
2115
+ contributing.push({
2116
+ name: "budget",
2117
+ status: !accountingComplete ? "not_evaluated" : budgetPass ? "pass" : "fail",
2118
+ detail: {
2119
+ totalCostUsd: cost.totalCostUsd,
2120
+ budgetUsd,
2121
+ accountingComplete,
2122
+ pendingCalls: cost.pendingCalls,
2123
+ unresolvedCalls: cost.unresolvedCalls
2062
2124
  }
2063
- const redTeamRequired = options.redTeamBattery !== void 0 || explicitlyRequired.has("red-team");
2064
- if (!options.redTeamBattery) unavailable("red-team", redTeamRequired, "redTeamBattery is not configured");
2065
- else if (options.redTeamBattery.length === 0) unavailable("red-team", true, "redTeamBattery must contain at least one case");
2066
- else {
2067
- const redTeam = probeRedTeam(ctx.candidateArtifacts, options.redTeamBattery);
2068
- const incomplete = redTeam.unsupportedCases.length > 0 || redTeam.unscoredArtifacts.length > 0 || redTeam.evaluatedCases === 0;
2069
- const redTeamStatus = redTeam.findings.length > 0 ? "fail" : incomplete ? "not_evaluated" : "pass";
2070
- contributing.push({
2071
- name: "red-team",
2072
- status: redTeamStatus,
2073
- detail: {
2074
- evaluatedCases: redTeam.evaluatedCases,
2075
- unsupportedCases: redTeam.unsupportedCases,
2076
- unscoredArtifacts: redTeam.unscoredArtifacts,
2077
- failures: redTeam.findings.length,
2078
- sample: redTeam.findings.slice(0, 3)
2079
- }
2080
- });
2081
- if (incomplete) requiredUnavailable.add("red-team");
2082
- if (redTeam.unsupportedCases.length > 0) reasons.push(`red-team: static artifact screening cannot evaluate case(s): ${redTeam.unsupportedCases.join(", ")}`);
2083
- if (redTeam.unscoredArtifacts.length > 0) reasons.push(`red-team: no text could be extracted from artifact(s): ${redTeam.unscoredArtifacts.join(", ")}`);
2084
- if (redTeam.evaluatedCases === 0) reasons.push("red-team: no artifact/case pair was evaluated");
2085
- if (redTeam.findings.length > 0) reasons.push(`red-team probe failed (${redTeam.findings.length} findings)`);
2125
+ });
2126
+ if (!accountingComplete) {
2127
+ requiredUnavailable.add("budget");
2128
+ reasons.push("budget: cost accounting is incomplete");
2129
+ } else if (!budgetPass) reasons.push(`spend ${cost.totalCostUsd.toFixed(2)} > budget ${budgetUsd}`);
2130
+ }
2131
+ }
2132
+ /** Static red-team screening of the candidate artifacts. */
2133
+ function checkRedTeam(g, cfg, ctx) {
2134
+ const { reasons, contributing, requiredUnavailable, unavailable } = g;
2135
+ const { options, explicitlyRequired } = cfg;
2136
+ const redTeamRequired = options.redTeamBattery !== void 0 || explicitlyRequired.has("red-team");
2137
+ if (!options.redTeamBattery) unavailable("red-team", redTeamRequired, "redTeamBattery is not configured");
2138
+ else if (options.redTeamBattery.length === 0) unavailable("red-team", true, "redTeamBattery must contain at least one case");
2139
+ else {
2140
+ const redTeam = probeRedTeam(ctx.candidateArtifacts, options.redTeamBattery);
2141
+ const incomplete = redTeam.unsupportedCases.length > 0 || redTeam.unscoredArtifacts.length > 0 || redTeam.evaluatedCases === 0;
2142
+ const redTeamStatus = redTeam.findings.length > 0 ? "fail" : incomplete ? "not_evaluated" : "pass";
2143
+ contributing.push({
2144
+ name: "red-team",
2145
+ status: redTeamStatus,
2146
+ detail: {
2147
+ evaluatedCases: redTeam.evaluatedCases,
2148
+ unsupportedCases: redTeam.unsupportedCases,
2149
+ unscoredArtifacts: redTeam.unscoredArtifacts,
2150
+ failures: redTeam.findings.length,
2151
+ sample: redTeam.findings.slice(0, 3)
2086
2152
  }
2087
- const rewardHacking = options.rewardHacking;
2088
- const rewardHackingConfigured = rewardHacking !== void 0;
2089
- const rewardHackingRequired = rewardHackingConfigured || explicitlyRequired.has("reward-hacking");
2090
- if (!rewardHackingConfigured) unavailable("reward-hacking", rewardHackingRequired, "rewardHacking is not configured");
2091
- else if (!options.recentRuns) unavailable("reward-hacking", rewardHackingRequired, "recentRuns is not configured");
2092
- else if (options.recentRuns.length < 10) unavailable("reward-hacking", rewardHackingRequired, `recentRuns has ${options.recentRuns.length} record(s); at least 10 are required`);
2093
- else {
2094
- const rewardHackingReport = detectRewardHacking({
2095
- runs: options.recentRuns,
2096
- ...rewardHacking
2097
- });
2098
- if (!rewardHackingReport.evaluatedSignals.includes("reward_divergence")) unavailable("reward-hacking", true, "independent proxy/truth evidence is insufficient to evaluate reward divergence", { report: rewardHackingReport });
2099
- else {
2100
- const gamingThreshold = rewardHacking.thresholds?.gaming ?? .6;
2101
- const gamingFindings = rewardHackingReport.findings.filter((finding) => finding.severity >= gamingThreshold);
2102
- const rewardHackingPass = gamingFindings.length === 0 && rewardHackingReport.verdict !== "gaming";
2103
- contributing.push({
2104
- name: "reward-hacking",
2105
- status: rewardHackingPass ? "pass" : "fail",
2106
- detail: {
2107
- report: rewardHackingReport,
2108
- gamingFindingCount: gamingFindings.length
2109
- }
2110
- });
2111
- if (!rewardHackingPass) reasons.push(`reward-hacking detector flagged ${gamingFindings.length} gaming-severity findings (verdict=${rewardHackingReport.verdict})`);
2153
+ });
2154
+ if (incomplete) requiredUnavailable.add("red-team");
2155
+ if (redTeam.unsupportedCases.length > 0) reasons.push(`red-team: static artifact screening cannot evaluate case(s): ${redTeam.unsupportedCases.join(", ")}`);
2156
+ if (redTeam.unscoredArtifacts.length > 0) reasons.push(`red-team: no text could be extracted from artifact(s): ${redTeam.unscoredArtifacts.join(", ")}`);
2157
+ if (redTeam.evaluatedCases === 0) reasons.push("red-team: no artifact/case pair was evaluated");
2158
+ if (redTeam.findings.length > 0) reasons.push(`red-team probe failed (${redTeam.findings.length} findings)`);
2159
+ }
2160
+ }
2161
+ /** Reward-hacking detector over the run-history window. */
2162
+ function checkRewardHacking(g, cfg) {
2163
+ const { reasons, contributing, unavailable } = g;
2164
+ const { options, explicitlyRequired } = cfg;
2165
+ const rewardHacking = options.rewardHacking;
2166
+ const rewardHackingConfigured = rewardHacking !== void 0;
2167
+ const rewardHackingRequired = rewardHackingConfigured || explicitlyRequired.has("reward-hacking");
2168
+ if (!rewardHackingConfigured) unavailable("reward-hacking", rewardHackingRequired, "rewardHacking is not configured");
2169
+ else if (!options.recentRuns) unavailable("reward-hacking", rewardHackingRequired, "recentRuns is not configured");
2170
+ else if (options.recentRuns.length < 10) unavailable("reward-hacking", rewardHackingRequired, `recentRuns has ${options.recentRuns.length} record(s); at least 10 are required`);
2171
+ else {
2172
+ const rewardHackingReport = detectRewardHacking({
2173
+ runs: options.recentRuns,
2174
+ ...rewardHacking
2175
+ });
2176
+ if (!rewardHackingReport.evaluatedSignals.includes("reward_divergence")) unavailable("reward-hacking", true, "independent proxy/truth evidence is insufficient to evaluate reward divergence", { report: rewardHackingReport });
2177
+ else {
2178
+ const gamingThreshold = rewardHacking.thresholds?.gaming ?? .6;
2179
+ const gamingFindings = rewardHackingReport.findings.filter((finding) => finding.severity >= gamingThreshold);
2180
+ const rewardHackingPass = gamingFindings.length === 0 && rewardHackingReport.verdict !== "gaming";
2181
+ contributing.push({
2182
+ name: "reward-hacking",
2183
+ status: rewardHackingPass ? "pass" : "fail",
2184
+ detail: {
2185
+ report: rewardHackingReport,
2186
+ gamingFindingCount: gamingFindings.length
2112
2187
  }
2113
- }
2114
- const canary = options.canary;
2115
- const canaryConfigured = canary !== void 0;
2116
- const canaryRequired = canaryConfigured || explicitlyRequired.has("canary");
2117
- if (!canaryConfigured && !canaryRequired) unavailable("canary", false, "canary is not configured");
2118
- else if (!options.recentRuns) unavailable("canary", canaryRequired, "recentRuns is not configured");
2119
- else {
2120
- const canaryReport = runCanaries(options.recentRuns, canary ?? {});
2121
- const incomplete = canaryReport.evaluations.filter((evaluation) => evaluation.status === "not_evaluated");
2122
- if (incomplete.length > 0) unavailable("canary", true, `insufficient evidence for: ${incomplete.map((evaluation) => evaluation.kind).join(", ")}`, { report: canaryReport });
2123
- else {
2124
- const errorAlerts = canaryReport.alerts.filter((alert) => alert.severity === "error");
2125
- const canaryPass = errorAlerts.length === 0;
2126
- contributing.push({
2127
- name: "canary",
2128
- status: canaryPass ? "pass" : "fail",
2129
- detail: {
2130
- totalAlerts: canaryReport.alerts.length,
2131
- errorAlerts: errorAlerts.length,
2132
- report: canaryReport
2133
- }
2134
- });
2135
- if (!canaryPass) reasons.push(`canary error alerts: ${errorAlerts.length}`);
2188
+ });
2189
+ if (!rewardHackingPass) reasons.push(`reward-hacking detector flagged ${gamingFindings.length} gaming-severity findings (verdict=${rewardHackingReport.verdict})`);
2190
+ }
2191
+ }
2192
+ }
2193
+ /** Canary checks over the run history. */
2194
+ function checkCanary(g, cfg) {
2195
+ const { reasons, contributing, unavailable } = g;
2196
+ const { options, explicitlyRequired } = cfg;
2197
+ const canary = options.canary;
2198
+ const canaryConfigured = canary !== void 0;
2199
+ const canaryRequired = canaryConfigured || explicitlyRequired.has("canary");
2200
+ if (!canaryConfigured && !canaryRequired) unavailable("canary", false, "canary is not configured");
2201
+ else if (!options.recentRuns) unavailable("canary", canaryRequired, "recentRuns is not configured");
2202
+ else {
2203
+ const canaryReport = runCanaries(options.recentRuns, canary ?? {});
2204
+ const incomplete = canaryReport.evaluations.filter((evaluation) => evaluation.status === "not_evaluated");
2205
+ if (incomplete.length > 0) unavailable("canary", true, `insufficient evidence for: ${incomplete.map((evaluation) => evaluation.kind).join(", ")}`, { report: canaryReport });
2206
+ else {
2207
+ const errorAlerts = canaryReport.alerts.filter((alert) => alert.severity === "error");
2208
+ const canaryPass = errorAlerts.length === 0;
2209
+ contributing.push({
2210
+ name: "canary",
2211
+ status: canaryPass ? "pass" : "fail",
2212
+ detail: {
2213
+ totalAlerts: canaryReport.alerts.length,
2214
+ errorAlerts: errorAlerts.length,
2215
+ report: canaryReport
2136
2216
  }
2137
- }
2138
- const decision = contributing.some((check) => check.status === "fail") || requiredUnavailable.size > 0 ? "hold" : "ship";
2139
- const notEvaluated = contributing.filter((check) => check.status === "not_evaluated").map((check) => check.name);
2140
- return {
2141
- decision,
2142
- reasons: reasons.length > 0 ? reasons : notEvaluated.length > 0 ? [`evaluated checks passed; not evaluated: ${notEvaluated.join(", ")}`] : ["all checks passed"],
2143
- contributingGates: contributing,
2144
- delta
2145
- };
2217
+ });
2218
+ if (!canaryPass) reasons.push(`canary error alerts: ${errorAlerts.length}`);
2146
2219
  }
2147
- };
2220
+ }
2148
2221
  }
2149
2222
  function probeRedTeam(artifacts, battery) {
2150
2223
  const supportedCases = battery.filter((testCase) => testCase.payload.expected === "ignore" && !testCase.payload.forbiddenTools?.length);
@@ -2185,6 +2258,62 @@ function extractText(artifact) {
2185
2258
  }
2186
2259
  }
2187
2260
  //#endregion
2261
+ //#region src/campaign/gates/default-production-gate.ts
2262
+ /**
2263
+ * Opinionated production gate composing held-out significance, red-team, reward-hacking, and canary checks into a single `Gate.decide` decision.
2264
+ */
2265
+ function defaultProductionGate(options) {
2266
+ const deltaThreshold = options.deltaThreshold ?? 0;
2267
+ const confidence = options.confidence ?? .95;
2268
+ const resamples = options.bootstrapResamples ?? 2e3;
2269
+ const seed = options.bootstrapSeed ?? 1337;
2270
+ const minProductiveRuns = options.minProductiveRuns ?? 3;
2271
+ const heldoutStatistic = options.heldoutStatistic ?? "mean";
2272
+ const explicitlyRequired = new Set(options.requiredChecks ?? []);
2273
+ const scenarioIds = new Set(options.holdoutScenarios.map((scenario) => scenario.id));
2274
+ const independentUnitByScenarioId = options.independentUnitByScenarioId === void 0 ? void 0 : new Map(options.independentUnitByScenarioId);
2275
+ if (independentUnitByScenarioId !== void 0) for (const id of scenarioIds) {
2276
+ const unit = independentUnitByScenarioId.get(id);
2277
+ if (typeof unit !== "string" || unit.length === 0 || unit.trim() !== unit) throw new Error(`defaultProductionGate: missing independent unit for holdout scenario '${id}'`);
2278
+ }
2279
+ const cfg = {
2280
+ options,
2281
+ deltaThreshold,
2282
+ confidence,
2283
+ resamples,
2284
+ seed,
2285
+ minProductiveRuns,
2286
+ heldoutStatistic,
2287
+ explicitlyRequired,
2288
+ scenarioIds,
2289
+ independentUnitByScenarioId
2290
+ };
2291
+ return {
2292
+ name: "defaultProductionGate",
2293
+ async decide(ctx) {
2294
+ const findings = newFindings();
2295
+ const delta = checkHeldout(findings, cfg, ctx);
2296
+ checkDimensions(findings, cfg, ctx);
2297
+ checkBudget(findings, cfg, ctx);
2298
+ checkRedTeam(findings, cfg, ctx);
2299
+ checkRewardHacking(findings, cfg);
2300
+ checkCanary(findings, cfg);
2301
+ return verdictOf(findings, delta);
2302
+ }
2303
+ };
2304
+ }
2305
+ /** Hold when any check failed or a required check could not be evaluated; otherwise ship. */
2306
+ function verdictOf({ reasons, contributing, requiredUnavailable }, delta) {
2307
+ const decision = contributing.some((check) => check.status === "fail") || requiredUnavailable.size > 0 ? "hold" : "ship";
2308
+ const notEvaluated = contributing.filter((check) => check.status === "not_evaluated").map((check) => check.name);
2309
+ return {
2310
+ decision,
2311
+ reasons: reasons.length > 0 ? reasons : notEvaluated.length > 0 ? [`evaluated checks passed; not evaluated: ${notEvaluated.join(", ")}`] : ["all checks passed"],
2312
+ contributingGates: contributing,
2313
+ delta
2314
+ };
2315
+ }
2316
+ //#endregion
2188
2317
  //#region src/campaign/judge-snapshot.ts
2189
2318
  /** Capture configuration and callbacks; receiver and closed-over state remain caller-owned. */
2190
2319
  function captureJudge(judge) {
@@ -4423,13 +4552,9 @@ function toScoredSurfaceOutcome(hash, campaign, coverage, generation) {
4423
4552
  * again on the same run directory and options continues an interrupted run
4424
4553
  * instead of starting over, and reruns nothing that settled.
4425
4554
  */
4426
- /** Compose the optimization proposer and cell executor on the shared search kernel. */
4427
4555
  /** Improve a surface as a search on the kernel; see the module comment. */
4428
4556
  async function runOptimization(opts) {
4429
- const candidateConcurrency = opts.candidateConcurrency ?? 1;
4430
- if (typeof opts.runDir !== "string" || opts.runDir.trim().length === 0) throw new Error("runOptimization: runDir is required and must be a non-empty string");
4431
- if (!Number.isInteger(candidateConcurrency) || candidateConcurrency < 1) throw new Error("runOptimization: candidateConcurrency must be a positive integer");
4432
- for (const [name, value] of [["populationSize", opts.populationSize], ["maxGenerations", opts.maxGenerations]]) if (!Number.isSafeInteger(value) || value < 0) throw new Error(`runOptimization: ${name} must be a non-negative integer`);
4557
+ const candidateConcurrency = assertOptimizationOptions(opts);
4433
4558
  const initialFindings = immutableProposalSnapshot(assertProposalFindings(opts.findings ?? [], "runOptimization initial proposal findings"), "initial findings");
4434
4559
  const baselineSurface = immutableProposalSnapshot(opts.baselineSurface, "baseline surface");
4435
4560
  const runDir = resolveRunDir(opts.runDir, opts.repo);
@@ -4468,7 +4593,53 @@ async function runOptimization(opts) {
4468
4593
  const recorder = await SearchRecorder.open({
4469
4594
  ledger: binding.ledger,
4470
4595
  storage
4471
- }, {
4596
+ }, searchOpening({
4597
+ opts,
4598
+ identity,
4599
+ execution,
4600
+ baselineSurface,
4601
+ splitDigest,
4602
+ policy,
4603
+ allocationName: allocation.name,
4604
+ seed
4605
+ }));
4606
+ const run = {
4607
+ opts,
4608
+ runDir,
4609
+ costLedger,
4610
+ reps,
4611
+ requireJudgeScore,
4612
+ premeasured,
4613
+ identity,
4614
+ policy,
4615
+ recorder,
4616
+ nodes: new SurfaceNodes(opts, recorder, runDir, costLedger, execution, premeasured),
4617
+ findings: initialFindings
4618
+ };
4619
+ return optimizationResult(run, baselineSurface, await runSearch({
4620
+ recorder,
4621
+ root: baselineSurface,
4622
+ codec: surfaceCodec,
4623
+ policy,
4624
+ allocation,
4625
+ proposer: optimizationProposerPort(run),
4626
+ executor: optimizationExecutor(run, candidateConcurrency),
4627
+ maxExpansions: opts.maxGenerations,
4628
+ signal: opts.signal
4629
+ }));
4630
+ }
4631
+ /** Reject malformed run settings before any state is touched; returns the candidate concurrency. */
4632
+ function assertOptimizationOptions(opts) {
4633
+ const candidateConcurrency = opts.candidateConcurrency ?? 1;
4634
+ if (typeof opts.runDir !== "string" || opts.runDir.trim().length === 0) throw new Error("runOptimization: runDir is required and must be a non-empty string");
4635
+ if (!Number.isInteger(candidateConcurrency) || candidateConcurrency < 1) throw new Error("runOptimization: candidateConcurrency must be a positive integer");
4636
+ for (const [name, value] of [["populationSize", opts.populationSize], ["maxGenerations", opts.maxGenerations]]) if (!Number.isSafeInteger(value) || value < 0) throw new Error(`runOptimization: ${name} must be a non-negative integer`);
4637
+ return candidateConcurrency;
4638
+ }
4639
+ /** The search ledger's opening record: subject, objective, train split, policy and budget. */
4640
+ function searchOpening(args) {
4641
+ const { opts, identity, baselineSurface, splitDigest, policy } = args;
4642
+ return {
4472
4643
  subject: identity.subject ?? opts.proposer.kind,
4473
4644
  process: {
4474
4645
  name: "runOptimization",
@@ -4496,8 +4667,8 @@ async function runOptimization(opts) {
4496
4667
  },
4497
4668
  policy: {
4498
4669
  expansion: policy.name,
4499
- allocation: allocation.name,
4500
- seed
4670
+ allocation: args.allocationName,
4671
+ seed: args.seed
4501
4672
  },
4502
4673
  budget: {
4503
4674
  maxUsd: opts.costCeiling ?? null,
@@ -4509,12 +4680,14 @@ async function runOptimization(opts) {
4509
4680
  },
4510
4681
  containment: null,
4511
4682
  derivedFrom: null,
4512
- identity: execution
4513
- });
4514
- const nodes = new SurfaceNodes(opts, recorder, runDir, costLedger, execution, premeasured);
4683
+ identity: args.execution
4684
+ };
4685
+ }
4686
+ /** One lane that measures each candidate cell through the surface nodes. */
4687
+ function optimizationExecutor(run, candidateConcurrency) {
4688
+ const { opts, nodes } = run;
4515
4689
  const scenarioById = new Map(opts.scenarios.map((scenario) => [scenario.id, scenario]));
4516
- let findings = initialFindings;
4517
- const executor = {
4690
+ return {
4518
4691
  lanes: () => [{
4519
4692
  name: "campaign",
4520
4693
  capacity: candidateConcurrency * (opts.maxConcurrency ?? 2),
@@ -4525,83 +4698,29 @@ async function runOptimization(opts) {
4525
4698
  run: (work) => nodes.runCell(work, scenarioById),
4526
4699
  adopt: (work) => nodes.adoptCell(work)
4527
4700
  };
4701
+ }
4702
+ /** The proposer port: each proposal analyses the last generation, then asks the caller's proposer. */
4703
+ function optimizationProposerPort(run) {
4704
+ const { opts, identity, recorder, nodes, policy } = run;
4528
4705
  const proposer = opts.proposer;
4529
4706
  const proposerSource = identity.proposer.source;
4530
- const port = {
4707
+ return {
4531
4708
  name: proposer.kind,
4532
4709
  kind: "optimizer",
4533
4710
  source: proposerSource,
4534
4711
  execution: identity.proposer,
4535
4712
  childrenPerProposal: opts.populationSize,
4536
4713
  async propose(request) {
4537
- const phases = [
4538
- "search.proposal",
4539
- "analysis.baseline",
4540
- "analysis.generation"
4541
- ];
4542
- const before = new Set(phases.flatMap((phase) => costLedger.list({ phase })).map((receipt) => receipt.callId));
4543
- const accounting = () => {
4544
- const fresh = phases.flatMap((phase) => costLedger.list({ phase })).filter((receipt) => !before.has(receipt.callId));
4545
- return {
4546
- accounting: searchReceiptAccounting(fresh),
4547
- execution: opts.searchLedger ? identity.proposer : searchProposalExecution(fresh, identity.model.provider, proposerSource)
4548
- };
4549
- };
4714
+ const accounting = proposalAccounting(run);
4550
4715
  const earlier = await recorder.state();
4551
4716
  const root = earlier.rootNodeId;
4552
4717
  const rootCampaign = nodes.campaign(earlier, root);
4553
- const rootCoverage = campaignCoverage(rootCampaign.cells, opts.scenarios, reps, requireJudgeScore);
4554
- if (!rootCoverage.complete) throw new Error(`runOptimization: ${premeasured ? "premeasured baseline" : "baseline"} is incomplete (${rootCoverage.scorableCellIds.length}/${rootCoverage.expectedCellIds.length} designed cells scorable) — ${formatCoverageFailures(rootCoverage)}. Refusing to optimize against an incomplete incumbent.`);
4718
+ assertBaselineComplete(run, rootCampaign);
4555
4719
  const generation = nodes.generations(earlier).length;
4556
- if (opts.analyzeGeneration) {
4557
- const previous = generation - 1;
4558
- const candidates = (previous < 0 ? [root] : nodes.expansionNodes(earlier, nodes.generations(earlier)[previous])).map((nodeId) => {
4559
- const campaign = nodes.campaign(earlier, nodeId);
4560
- return {
4561
- surfaceHash: surfaceHash(nodes.surface(earlier, nodeId)),
4562
- campaign,
4563
- composite: campaignMeanCompositeOrNull(campaign)
4564
- };
4565
- });
4566
- const history = nodes.history(earlier, policy);
4567
- if (candidates.some(({ campaign }) => campaign.cells.length > 0)) {
4568
- const fresh = await opts.analyzeGeneration({
4569
- generation: previous,
4570
- runDir: previous < 0 ? rootCampaign.runDir : `${runDir}/candidates`,
4571
- candidates,
4572
- history,
4573
- costLedger,
4574
- costPhase: previous < 0 ? "analysis.baseline" : "analysis.generation"
4575
- });
4576
- if (!Array.isArray(fresh)) throw new TypeError("runOptimization: analyzeGeneration must return an array");
4577
- findings = immutableProposalSnapshot(assertProposalFindings(fresh, "runOptimization analysis findings"), "analysis findings");
4578
- }
4579
- }
4720
+ if (opts.analyzeGeneration) await analyzePreviousGeneration(run, earlier, generation, root, rootCampaign.runDir);
4580
4721
  const state = await recorder.state();
4581
4722
  const proposalHistory = immutableProposalSnapshot(nodes.history(state, policy), "history");
4582
- const parent = request.parents[0];
4583
- const context = Object.freeze({
4584
- currentSurface: immutableProposalSnapshot(parent.artifact, "current surface"),
4585
- operator: request.operator,
4586
- parents: immutableProposalSnapshot(request.parents.map(({ nodeId, artifact }) => ({
4587
- nodeId,
4588
- artifact: immutableProposalSnapshot(artifact, "parent surface")
4589
- })), "parents"),
4590
- train: searchProposerView(state),
4591
- summary: renderSearchSummary(state, { split: "train" }),
4592
- history: proposalHistory,
4593
- findings: immutableProposalSnapshot(assertProposalFindings(findings, "runOptimization proposal findings"), "findings"),
4594
- populationSize: opts.populationSize,
4595
- generation,
4596
- signal: request.signal,
4597
- baselineOutcome: immutableProposalSnapshot(nodes.outcome(state, root), "baseline"),
4598
- incumbentOutcome: immutableProposalSnapshot(nodes.outcome(state, request.leader), "incumbent outcome"),
4599
- parentOutcome: immutableProposalSnapshot(nodes.outcome(state, parent.nodeId), "parent outcome"),
4600
- maxImprovementShots: opts.maxImprovementShots,
4601
- paretoParents: immutableProposalSnapshot(nodes.frontier(state), "Pareto parents"),
4602
- costLedger,
4603
- costPhase: "search.proposal"
4604
- });
4723
+ const context = proposeContext(run, request, state, generation, root, proposalHistory);
4605
4724
  if (proposer.decide?.({ history: proposalHistory }).stop) return {
4606
4725
  children: [],
4607
4726
  stop: "the proposer decided to stop",
@@ -4624,17 +4743,86 @@ async function runOptimization(opts) {
4624
4743
  };
4625
4744
  }
4626
4745
  };
4627
- const result = await runSearch({
4628
- recorder,
4629
- root: baselineSurface,
4630
- codec: surfaceCodec,
4631
- policy,
4632
- allocation,
4633
- proposer: port,
4634
- executor,
4635
- maxExpansions: opts.maxGenerations,
4636
- signal: opts.signal
4746
+ }
4747
+ /** Snapshot the proposal-phase receipts now; the returned reader totals only receipts added since. */
4748
+ function proposalAccounting(run) {
4749
+ const { opts, identity, costLedger } = run;
4750
+ const phases = [
4751
+ "search.proposal",
4752
+ "analysis.baseline",
4753
+ "analysis.generation"
4754
+ ];
4755
+ const before = new Set(phases.flatMap((phase) => costLedger.list({ phase })).map((receipt) => receipt.callId));
4756
+ return () => {
4757
+ const fresh = phases.flatMap((phase) => costLedger.list({ phase })).filter((receipt) => !before.has(receipt.callId));
4758
+ return {
4759
+ accounting: searchReceiptAccounting(fresh),
4760
+ execution: opts.searchLedger ? identity.proposer : searchProposalExecution(fresh, identity.model.provider, identity.proposer.source)
4761
+ };
4762
+ };
4763
+ }
4764
+ /** Refuse to optimize against a baseline whose designed cells are not all scorable. */
4765
+ function assertBaselineComplete(run, rootCampaign) {
4766
+ const { opts, reps, requireJudgeScore, premeasured } = run;
4767
+ const rootCoverage = campaignCoverage(rootCampaign.cells, opts.scenarios, reps, requireJudgeScore);
4768
+ if (!rootCoverage.complete) throw new Error(`runOptimization: ${premeasured ? "premeasured baseline" : "baseline"} is incomplete (${rootCoverage.scorableCellIds.length}/${rootCoverage.expectedCellIds.length} designed cells scorable) — ${formatCoverageFailures(rootCoverage)}. Refusing to optimize against an incomplete incumbent.`);
4769
+ }
4770
+ /** Run the caller's analysis over the previous generation (or the baseline) and keep its findings. */
4771
+ async function analyzePreviousGeneration(run, earlier, generation, root, rootRunDir) {
4772
+ const { opts, nodes, policy, costLedger, runDir } = run;
4773
+ const previous = generation - 1;
4774
+ const candidates = (previous < 0 ? [root] : nodes.expansionNodes(earlier, nodes.generations(earlier)[previous])).map((nodeId) => {
4775
+ const campaign = nodes.campaign(earlier, nodeId);
4776
+ return {
4777
+ surfaceHash: surfaceHash(nodes.surface(earlier, nodeId)),
4778
+ campaign,
4779
+ composite: campaignMeanCompositeOrNull(campaign)
4780
+ };
4637
4781
  });
4782
+ const history = nodes.history(earlier, policy);
4783
+ if (candidates.some(({ campaign }) => campaign.cells.length > 0)) {
4784
+ const fresh = await opts.analyzeGeneration({
4785
+ generation: previous,
4786
+ runDir: previous < 0 ? rootRunDir : `${runDir}/candidates`,
4787
+ candidates,
4788
+ history,
4789
+ costLedger,
4790
+ costPhase: previous < 0 ? "analysis.baseline" : "analysis.generation"
4791
+ });
4792
+ if (!Array.isArray(fresh)) throw new TypeError("runOptimization: analyzeGeneration must return an array");
4793
+ run.findings = immutableProposalSnapshot(assertProposalFindings(fresh, "runOptimization analysis findings"), "analysis findings");
4794
+ }
4795
+ }
4796
+ /** The frozen view of the search the caller's proposer reads. */
4797
+ function proposeContext(run, request, state, generation, root, proposalHistory) {
4798
+ const { opts, nodes, costLedger } = run;
4799
+ const parent = request.parents[0];
4800
+ return Object.freeze({
4801
+ currentSurface: immutableProposalSnapshot(parent.artifact, "current surface"),
4802
+ operator: request.operator,
4803
+ parents: immutableProposalSnapshot(request.parents.map(({ nodeId, artifact }) => ({
4804
+ nodeId,
4805
+ artifact: immutableProposalSnapshot(artifact, "parent surface")
4806
+ })), "parents"),
4807
+ train: searchProposerView(state),
4808
+ summary: renderSearchSummary(state, { split: "train" }),
4809
+ history: proposalHistory,
4810
+ findings: immutableProposalSnapshot(assertProposalFindings(run.findings, "runOptimization proposal findings"), "findings"),
4811
+ populationSize: opts.populationSize,
4812
+ generation,
4813
+ signal: request.signal,
4814
+ baselineOutcome: immutableProposalSnapshot(nodes.outcome(state, root), "baseline"),
4815
+ incumbentOutcome: immutableProposalSnapshot(nodes.outcome(state, request.leader), "incumbent outcome"),
4816
+ parentOutcome: immutableProposalSnapshot(nodes.outcome(state, parent.nodeId), "parent outcome"),
4817
+ maxImprovementShots: opts.maxImprovementShots,
4818
+ paretoParents: immutableProposalSnapshot(nodes.frontier(state), "Pareto parents"),
4819
+ costLedger,
4820
+ costPhase: "search.proposal"
4821
+ });
4822
+ }
4823
+ /** Read the finished search into the leader, its provenance and every generation's measured surfaces. */
4824
+ function optimizationResult(run, baselineSurface, result) {
4825
+ const { opts, nodes, policy, recorder, costLedger, runDir } = run;
4638
4826
  const state = result.state;
4639
4827
  const winnerSurface = nodes.surface(state, result.leader);
4640
4828
  const firstEdge = state.edge(state.node(result.leader).edgeIds[0]);
@@ -4660,7 +4848,7 @@ async function runOptimization(opts) {
4660
4848
  baselineCampaign: nodes.campaign(state, state.rootNodeId),
4661
4849
  cost: costLedger.summary(),
4662
4850
  searchHistory: recorder.receipt({
4663
- producerId: proposer.kind,
4851
+ producerId: opts.proposer.kind,
4664
4852
  runId: runDir
4665
4853
  }),
4666
4854
  paretoFrontier: nodes.frontier(state)
@@ -5744,4 +5932,4 @@ function firstString(value) {
5744
5932
  //#endregion
5745
5933
  export { planCampaignRun as $, verifySearchHistoryReceipt as A, defaultProductionGate as B, searchEdgeId as C, assertSearchHistoryMatchesState as D, assertSearchHistoryAdmissionOptions as E, asha as F, runCanaries as G, redTeamDataset as H, uniform as I, evaluationUnitMap as J, assertIndependentEvaluationSplit as K, runFinalComparison as L, searchModelIdentity as M, searchProposalExecution as N, createSearchHistoryReceipt as O, searchReceiptAccounting as P, runCampaign as Q, openAutoPr as R, developmentClaim as S, surfaceNode as T, redTeamReport as U, DEFAULT_RED_TEAM_CORPUS as V, scoreRedTeamOutput as W, reserveFinalEvidence as X, exposeFinalEvidence as Y, runEval as Z, beam as _, aggregateRunScore as a, incumbentWithOperatorBandit as b, assertGepaCandidatePopulationSummary as c, costFromLedgerSummary as d, resolveRunDir as et, optimizationTokenUsageFromSummary as f, aide as g, isProposedCandidate as h, transientDispatchFailure as i, campaignCellSearchResult as j, verifySearchHistoryArtifact as k, readGepaCandidatePopulationArtifact as l, runOptimization as m, JudgeParseError as n, cellCachePath as nt, clamp01 as o, runImprovementLoop as p, captureFinalEvidencePolicy as q, isTransientTransportFailure as r, computeManifestHash as rt, executeOptimizationMethod as s, llmJudge as t, buildCellSchedule as tt, combineComparisonCosts as u, crowdedFrontierParent as v, surfaceDiff as w, SearchRecorder as x, incumbent as y, captureJudge as z };
5746
5934
 
5747
- //# sourceMappingURL=llm-judge-C8jMTNNG.js.map
5935
+ //# sourceMappingURL=llm-judge-Djxh-Uhs.js.map