@tangle-network/agent-eval 0.174.0 → 0.176.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +1 -1
  3. package/dist/adapters/http.d.ts +1 -1
  4. package/dist/agent-profile-cell-0gSi5ffD.js +374 -0
  5. package/dist/agent-profile-cell-0gSi5ffD.js.map +1 -0
  6. package/dist/analyst/index.d.ts +3 -3
  7. package/dist/analyst/index.js +4 -4
  8. package/dist/{benchmark-command-mZIlR-ra.js → benchmark-command-yPqjcZnC.js} +7 -7
  9. package/dist/{benchmark-command-mZIlR-ra.js.map → benchmark-command-yPqjcZnC.js.map} +1 -1
  10. package/dist/benchmarks/index.d.ts +1 -1
  11. package/dist/benchmarks/index.js +3 -3
  12. package/dist/campaign/index.d.ts +3 -3
  13. package/dist/campaign/index.js +9 -9
  14. package/dist/{campaign-BzMSCejE.js → campaign-85igdlgG.js} +12 -12
  15. package/dist/{campaign-BzMSCejE.js.map → campaign-85igdlgG.js.map} +1 -1
  16. package/dist/campaign-evidence-D8DBLqLI.js +2083 -0
  17. package/dist/campaign-evidence-D8DBLqLI.js.map +1 -0
  18. package/dist/{opencode-sqlite-eK6HW6dr.js → claude-jsonl-CxZZrDJ3.js} +9 -149
  19. package/dist/claude-jsonl-CxZZrDJ3.js.map +1 -0
  20. package/dist/cli.js +9 -2
  21. package/dist/cli.js.map +1 -1
  22. package/dist/contract/index.d.ts +4 -4
  23. package/dist/contract/index.js +9 -9
  24. package/dist/{default-registry-CrAp0pYq.js → default-registry-DBqVI4pq.js} +2 -2
  25. package/dist/{default-registry-CrAp0pYq.js.map → default-registry-DBqVI4pq.js.map} +1 -1
  26. package/dist/{define-agent-eval-V1jQyCDR.d.ts → define-agent-eval-CCbl8k2E.d.ts} +11 -4
  27. package/dist/define-agent-eval-CCbl8k2E.d.ts.map +1 -0
  28. package/dist/{define-agent-eval-ox5McL6e.js → define-agent-eval-DEMsu5eA.js} +54 -36
  29. package/dist/define-agent-eval-DEMsu5eA.js.map +1 -0
  30. package/dist/{dspy-rlm-engine-Caz2pl4L.js → dspy-rlm-engine-DqjER2sV.js} +2 -2
  31. package/dist/{dspy-rlm-engine-Caz2pl4L.js.map → dspy-rlm-engine-DqjER2sV.js.map} +1 -1
  32. package/dist/{eval-campaign-BeAjdhzC.js → eval-campaign-Cs-7MiCs.js} +4 -5
  33. package/dist/{eval-campaign-BeAjdhzC.js.map → eval-campaign-Cs-7MiCs.js.map} +1 -1
  34. package/dist/experiment/index.d.ts +3 -68
  35. package/dist/experiment/index.d.ts.map +1 -1
  36. package/dist/experiment/index.js +6 -128
  37. package/dist/experiment/index.js.map +1 -1
  38. package/dist/{attestation-XSUpbc4o.js → experiment-tracker-BKEumQug.js} +2 -96
  39. package/dist/experiment-tracker-BKEumQug.js.map +1 -0
  40. package/dist/{attestation-c1QvaBdX.d.ts → experiment-tracker-CNwqCZFD.d.ts} +2 -78
  41. package/dist/experiment-tracker-CNwqCZFD.d.ts.map +1 -0
  42. package/dist/{external-optimizer-process-CxnFL1hd.js → external-optimizer-process-Dlz8YxrT.js} +3 -3
  43. package/dist/{external-optimizer-process-CxnFL1hd.js.map → external-optimizer-process-Dlz8YxrT.js.map} +1 -1
  44. package/dist/{external-optimizer-subprocess-CQi27uEI.js → external-optimizer-subprocess-q3VzlGAO.js} +2 -2
  45. package/dist/{external-optimizer-subprocess-CQi27uEI.js.map → external-optimizer-subprocess-q3VzlGAO.js.map} +1 -1
  46. package/dist/{index-Bn-nlnSV.d.ts → index-BAAiSF3_.d.ts} +2 -2
  47. package/dist/{index-Bn-nlnSV.d.ts.map → index-BAAiSF3_.d.ts.map} +1 -1
  48. package/dist/{index-DKXuBPXf.d.ts → index-Bg6OT2Dd.d.ts} +23 -10
  49. package/dist/{index-DKXuBPXf.d.ts.map → index-Bg6OT2Dd.d.ts.map} +1 -1
  50. package/dist/{index-BTrx5s8m.d.ts → index-DBkcm_9H.d.ts} +4 -4
  51. package/dist/{index-BTrx5s8m.d.ts.map → index-DBkcm_9H.d.ts.map} +1 -1
  52. package/dist/{index-D-UdhAmg.d.ts → index-u0d1Jp4F.d.ts} +4 -2
  53. package/dist/{index-D-UdhAmg.d.ts.map → index-u0d1Jp4F.d.ts.map} +1 -1
  54. package/dist/index.d.ts +5 -5
  55. package/dist/index.js +15 -16
  56. package/dist/index.js.map +1 -1
  57. package/dist/{integrity-BWywb34E.js → integrity-DsHWCebQ.js} +11 -435
  58. package/dist/integrity-DsHWCebQ.js.map +1 -0
  59. package/dist/ledger-core/index.d.ts +2 -2
  60. package/dist/ledger-core/index.js +2 -2
  61. package/dist/{ledger-core-PIfjCbKn.js → ledger-core-Cs9f7385.js} +60 -47
  62. package/dist/{ledger-core-PIfjCbKn.js.map → ledger-core-Cs9f7385.js.map} +1 -1
  63. package/dist/{llm-judge-DmNaBrXB.js → llm-judge-DliimmRb.js} +994 -1517
  64. package/dist/llm-judge-DliimmRb.js.map +1 -0
  65. package/dist/{mint-vWOdD8Ae.js → mint-Cc1_zwRQ.js} +2 -2
  66. package/dist/{mint-vWOdD8Ae.js.map → mint-Cc1_zwRQ.js.map} +1 -1
  67. package/dist/openapi.json +1 -1
  68. package/dist/opencode-sqlite-CNw3vubS.js +145 -0
  69. package/dist/opencode-sqlite-CNw3vubS.js.map +1 -0
  70. package/dist/{produced-state-B8mw6zj9.js → produced-state-DrMqa2HD.js} +3 -2
  71. package/dist/{produced-state-B8mw6zj9.js.map → produced-state-DrMqa2HD.js.map} +1 -1
  72. package/dist/profile-cell.js +1 -268
  73. package/dist/{promotion-policy-LY9mVQ7W.js → promotion-policy-DWOm70gx.js} +2 -2
  74. package/dist/{promotion-policy-LY9mVQ7W.js.map → promotion-policy-DWOm70gx.js.map} +1 -1
  75. package/dist/{release-confidence-BsGEg_xg.js → release-confidence-BcGCclTB.js} +2 -2
  76. package/dist/{release-confidence-BsGEg_xg.js.map → release-confidence-BcGCclTB.js.map} +1 -1
  77. package/dist/report-command-DKlXfU5r.js +1528 -0
  78. package/dist/report-command-DKlXfU5r.js.map +1 -0
  79. package/dist/reporting.js +2 -2
  80. package/dist/{reward-hacking-CKW4teig.js → reward-hacking-D0XwhVWE.js} +2 -215
  81. package/dist/reward-hacking-D0XwhVWE.js.map +1 -0
  82. package/dist/rl.js +5 -4
  83. package/dist/rl.js.map +1 -1
  84. package/dist/rollout/index.js +4 -3
  85. package/dist/{rollout-C-znbbYg.js → rollout-DmoJVqrF.js} +4 -3
  86. package/dist/{rollout-C-znbbYg.js.map → rollout-DmoJVqrF.js.map} +1 -1
  87. package/dist/run-record-CR63CpHK.js +216 -0
  88. package/dist/run-record-CR63CpHK.js.map +1 -0
  89. package/dist/{run-record-ZIsR9Fif.js → run-record-DQpSf7t-.js} +2 -2
  90. package/dist/{run-record-ZIsR9Fif.js.map → run-record-DQpSf7t-.js.map} +1 -1
  91. package/dist/{semantic-concept-judge-E3s_fEjB.js → semantic-concept-judge-Dw-f7TEs.js} +3 -3
  92. package/dist/{semantic-concept-judge-E3s_fEjB.js.map → semantic-concept-judge-Dw-f7TEs.js.map} +1 -1
  93. package/dist/{sequential-B51qAYE4.js → sequential-B5gXgcyp.js} +3 -3
  94. package/dist/{sequential-B51qAYE4.js.map → sequential-B5gXgcyp.js.map} +1 -1
  95. package/dist/{skillopt-optimization-method-f7399oGb.js → skillopt-optimization-method-CV7go7ex.js} +6 -749
  96. package/dist/skillopt-optimization-method-CV7go7ex.js.map +1 -0
  97. package/dist/{statistical-heldout-Cqb73yE9.d.ts → statistical-heldout-Z9NROFFS.d.ts} +156 -3
  98. package/dist/statistical-heldout-Z9NROFFS.d.ts.map +1 -0
  99. package/dist/{summary-report-Bgh8CpNK.js → summary-report-B16xy9Kd.js} +2 -2
  100. package/dist/{summary-report-Bgh8CpNK.js.map → summary-report-B16xy9Kd.js.map} +1 -1
  101. package/dist/supervisor-run/index.d.ts +71 -6
  102. package/dist/supervisor-run/index.d.ts.map +1 -1
  103. package/dist/supervisor-run/index.js +6 -1357
  104. package/dist/supervisor-run/index.js.map +1 -1
  105. package/dist/terminal-record-Ce9_UjRz.js +539 -0
  106. package/dist/terminal-record-Ce9_UjRz.js.map +1 -0
  107. package/dist/traces.js +1 -1
  108. package/dist/{types-CoPUTiXb.d.ts → types-vUdAx2Cj.d.ts} +65 -3
  109. package/dist/types-vUdAx2Cj.d.ts.map +1 -0
  110. package/docs/public-api.md +62 -39
  111. package/docs/search-history-receipts.md +48 -1
  112. package/package.json +1 -1
  113. package/dist/attestation-XSUpbc4o.js.map +0 -1
  114. package/dist/attestation-c1QvaBdX.d.ts.map +0 -1
  115. package/dist/define-agent-eval-V1jQyCDR.d.ts.map +0 -1
  116. package/dist/define-agent-eval-ox5McL6e.js.map +0 -1
  117. package/dist/integrity-BWywb34E.js.map +0 -1
  118. package/dist/llm-judge-DmNaBrXB.js.map +0 -1
  119. package/dist/opencode-sqlite-eK6HW6dr.js.map +0 -1
  120. package/dist/power-preflight-CFXm0Vjo.js +0 -502
  121. package/dist/power-preflight-CFXm0Vjo.js.map +0 -1
  122. package/dist/pre-registration-D94b7Of5.js +0 -110
  123. package/dist/pre-registration-D94b7Of5.js.map +0 -1
  124. package/dist/profile-cell.js.map +0 -1
  125. package/dist/reward-hacking-CKW4teig.js.map +0 -1
  126. package/dist/skillopt-optimization-method-f7399oGb.js.map +0 -1
  127. package/dist/statistical-heldout-Cqb73yE9.d.ts.map +0 -1
  128. package/dist/types-CoPUTiXb.d.ts.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"power-preflight-CFXm0Vjo.js","names":[],"sources":["../src/paired-delta-test.ts","../src/paired-promotion-decision.ts","../src/campaign/gates/statistical-heldout.ts","../src/campaign/gates/power-preflight.ts"],"sourcesContent":["import {\n type PairedBootstrapOptions,\n type PairedBootstrapResult,\n pairedBootstrap,\n pairedSignTest,\n} from './statistics'\n\nexport interface PairedDeltaTestOptions extends PairedBootstrapOptions {\n /** Smallest candidate-minus-baseline delta that counts as improvement. Default 0. */\n threshold?: number\n /** Caller-required paired observations. The exact test may impose a higher minimum. */\n minPairs?: number\n}\n\nexport interface PairedDeltaTestResult {\n bootstrap: PairedBootstrapResult\n method: 'bootstrap-ci' | 'exact-sign'\n /** Exact one-sided p-value below the bootstrap minimum; otherwise null. */\n pValue: number | null\n /** Effective observation minimum after accounting for confidence. */\n minimumPairs: number\n sufficient: boolean\n /**\n * The bootstrap interval has zero width (or is non-finite), so it carries no\n * information about how far the estimate could be wrong and cannot support a\n * decision in either direction. See {@link pairedDeltaTest}.\n */\n indeterminate: boolean\n significant: boolean\n}\n\n/** Smallest all-positive sample that can clear a one-sided exact sign test. */\nexport function minimumPairsForPairedDeltaTest(confidence = 0.95): number {\n if (!Number.isFinite(confidence) || confidence <= 0 || confidence >= 1) {\n throw new Error(\n `minimumPairsForPairedDeltaTest: confidence must be in (0,1), got ${confidence}`,\n )\n }\n const oneSidedAlpha = (1 - confidence) / 2\n return Math.ceil(Math.log2(1 / oneSidedAlpha))\n}\n\n/**\n * Tests whether a paired candidate-minus-baseline delta clears a threshold.\n *\n * At 20 or more pairs, the percentile bootstrap lower bound carries the\n * decision. Below that point the interval is descriptive only, so the function\n * switches to a pre-registered one-sided exact sign test. The exact path is\n * deliberately conservative: it requires both a point estimate above the\n * threshold and enough consistently positive paired differences.\n *\n * ## A zero-width interval is never significant\n *\n * When every paired delta is identical the resample distribution is a point\n * mass and the interval collapses: `[0, 0]` when all pairs tie, `[g, g]` on n\n * identical deltas of g. Neither says the effect is certain — both say the\n * sample carries no information about how far the estimate could be wrong, and\n * `low > threshold` then answers on the point estimate alone. It fails in both\n * directions: `[0, 0]` clears every NEGATIVE threshold, which is how a\n * tie-dominated pass/fail comparison laundered a regression into a\n * noninferiority pass, and `[g, g]` clears every threshold below g with no\n * spread behind it. Under a bounded asymmetric null whose true mean paired\n * delta is exactly 0 — 2 % of pairs dropping by 1.0, the rest gaining 0.0204 —\n * every sample that misses the drop is exactly that shape, and deciding on\n * `low > 0` promoted 65.65 % of samples at n = 20 against a nominal 5 %.\n *\n * So `indeterminate` is reported and `significant` is false whenever the\n * interval has zero width, on BOTH paths: at small n the exact sign test is a\n * test of the MEDIAN and a zero-spread sample is precisely where it stops\n * saying anything about the mean the caller is thresholding.\n *\n * `threshold` may be negative — that is a noninferiority margin, and it is the\n * regime the zero-width hole is worst in. For a two-point (pass/fail) outcome\n * the percentile bootstrap is not a valid interval at a nonzero margin at all;\n * use {@link decidePairedPromotion}, which routes those to Tango's score\n * interval, rather than thresholding this function's bootstrap directly.\n */\nexport function pairedDeltaTest(\n before: number[],\n after: number[],\n options: PairedDeltaTestOptions = {},\n): PairedDeltaTestResult {\n const threshold = options.threshold ?? 0\n if (!Number.isFinite(threshold)) {\n throw new Error(`pairedDeltaTest: threshold must be finite, got ${threshold}`)\n }\n const confidence = options.confidence ?? 0.95\n const exactMinimum = minimumPairsForPairedDeltaTest(confidence)\n const requestedMinimum = options.minPairs ?? exactMinimum\n if (!Number.isInteger(requestedMinimum) || requestedMinimum < 1) {\n throw new Error(`pairedDeltaTest: minPairs must be a positive integer, got ${requestedMinimum}`)\n }\n const minimumPairs = Math.max(requestedMinimum, exactMinimum)\n const bootstrap = pairedBootstrap(before, after, options)\n const sufficient = bootstrap.n >= minimumPairs\n // A point-mass resample distribution is an absence of evidence, not a\n // certainty, and it is the shape that clears every negative threshold. Both\n // paths refuse it: see the \"zero-width\" section of this function's docs.\n const indeterminate =\n bootstrap.n > 0 &&\n (!Number.isFinite(bootstrap.low) ||\n !Number.isFinite(bootstrap.high) ||\n bootstrap.low === bootstrap.high)\n\n if (bootstrap.gateEligible) {\n return {\n bootstrap,\n method: 'bootstrap-ci',\n pValue: null,\n minimumPairs,\n sufficient,\n indeterminate,\n significant: sufficient && !indeterminate && bootstrap.low > threshold,\n }\n }\n\n const differences = before.map((value, index) => after[index]! - value - threshold)\n const exact = pairedSignTest(differences, 'greater')\n const estimate = options.statistic === 'mean' ? bootstrap.mean : bootstrap.median\n return {\n bootstrap,\n method: 'exact-sign',\n pValue: exact.pValue,\n minimumPairs,\n sufficient,\n indeterminate,\n significant:\n sufficient &&\n !indeterminate &&\n estimate > threshold &&\n exact.pValue <= (1 - bootstrap.confidence) / 2,\n }\n}\n","/**\n * @module\n * ONE rule for \"does this paired interval clear a promotion threshold\".\n *\n * The rule below was derived on `HeldOutGate` (#479) after the same estimator\n * bug shipped twice. It then turned out that a SECOND gate — the composable\n * `heldOutGate`, plus everything else routed through `heldoutSignificance` —\n * still carried the original defect, because the rule had been written into one\n * gate's method body rather than into a shared function. Two copies of a\n * statistical rule is how a defect survives in one of them, so there is now\n * exactly one copy and both gates call it.\n *\n * Three things the rule does that a bare `pairedBootstrap(...).low > threshold`\n * does not:\n *\n * 1. **Two-point (pass/fail) outcomes decide on Tango's SCORE interval.** On a\n * pass/fail eval the paired delta vector is dominated by ties, so the\n * bootstrap of the mean is a resample of a lattice with three atoms and its\n * percentile interval is not valid at a nonzero margin. The score interval\n * (`pairedRiskDifferenceScore`) re-estimates the nuisance loss rate under\n * each hypothesised margin instead of fixing it at the observed value, which\n * is the only construction that stays a confidence interval as the margin\n * moves off zero — the regime every noninferiority threshold lives in.\n * Measured on the composable gate before this change, at a true risk\n * difference sitting exactly on the production caller's -0.05 margin and a\n * nominal 5 %: 14.60 % false promotion at n = 40 and 10.10 % at n = 76.\n * 2. **McNemar's exact test holds a VETO at every non-negative threshold.**\n * Redundant with the interval by construction and kept anyway, so that\n * swapping the estimator for one without that duality cannot silently\n * reintroduce \"promotes what the exact test refuses\". Witness: n = 6, b = 5,\n * c = 0 — no exact argument reaches alpha = 0.05 with 5 discordant pairs\n * (two-sided floor 2/2^5 = 0.0625), whatever an interval says. A NEGATIVE\n * threshold is a noninferiority question, which McNemar's test of \"no\n * difference\" is not the right test for, so the veto does not apply there.\n * 3. **A ZERO-WIDTH interval is refused, wherever it sits.** At [0, 0] it\n * cannot tell a gain from a regression and clears every negative threshold.\n * Away from zero it fails the opposite way: n identical positive deltas give\n * [g, g], which clears threshold 0 on no spread at all. Both are an absence\n * of evidence. Measured on the composable gate before this change, under a\n * bounded asymmetric null whose true mean paired delta is exactly 0: 88.50 %\n * false promotion at n = 6 and 65.65 % at n = 20 against a nominal 5 %.\n *\n * Orthogonal to the small-sample switch inside {@link pairedDeltaTest}: that\n * picks the TEST from the sample size (bootstrap CI at n >= 20, pre-registered\n * exact sign test below it), this picks the ESTIMATOR from the outcome's shape.\n * Both are needed — an exact sign test applied to a tie-pinned median is still\n * blind, and a mean bootstrap CI at n = 6 is still not a valid test.\n */\n\nimport { minimumPairsForPairedDeltaTest, pairedDeltaTest } from './paired-delta-test'\nimport {\n type PairedBootstrapResult,\n pairedBinaryScale,\n pairedDeltaTieFraction,\n pairedRiskDifferenceExact,\n pairedRiskDifferenceScore,\n} from './statistics'\n\n/** Which paired estimator produced the deciding interval. */\nexport type PairedDecisionStatistic =\n | 'paired_risk_difference'\n | 'mean_bootstrap'\n | 'median_bootstrap'\n\n/** Which test carried the decision, given the estimator and the sample size. */\nexport type PairedDecisionMethod = 'score-interval' | 'bootstrap-ci' | 'exact-sign'\n\n/** McNemar's exact paired-binary evidence, on the two-point path only. */\nexport interface PairedMcNemarEvidence {\n /** Discordant pairs the treatment won. */\n b: number\n /** Discordant pairs the control won. */\n c: number\n /** b + c — the only pairs carrying information. */\n nDiscordant: number\n /** Two-sided exact p-value. */\n pValue: number\n}\n\nexport interface PairedPromotionDecisionOptions {\n /** Smallest candidate-minus-baseline delta that counts as improvement, in the\n * caller's native units. May be negative (a noninferiority margin). Default 0. */\n threshold?: number\n /** Confidence level. Default 0.95. */\n confidence?: number\n /** Bootstrap resamples, on the paths where a bootstrap decides. Default 2000. */\n resamples?: number\n /** Deterministic bootstrap seed. Omitted ⇒ derived from the deltas. */\n seed?: number\n /** Caller-required paired observations. The exact test may impose a higher\n * minimum; the effective one is reported as `minimumPairs`. */\n minPairs?: number\n /**\n * `'mean'` (default) routes by SHAPE: a two-point (pass/fail) outcome on any\n * encoding decides on the score interval, everything else on the mean\n * bootstrap. `'median'` forces the median bootstrap on every input, including\n * shapes where it is structurally blind — kept for callers who want outlier\n * robustness on genuinely continuous outcomes and accept that cost.\n */\n statistic?: 'mean' | 'median'\n}\n\nexport interface PairedPromotionDecision {\n /** Paired observations supplied. */\n n: number\n /** Threshold the interval was judged against, native units. */\n threshold: number\n confidence: number\n statistic: PairedDecisionStatistic\n method: PairedDecisionMethod\n /** Common positive level of a two-point outcome ({0,1} ⇒ 1, {0,100} ⇒ 100),\n * or null when the outcome is not two-point. Non-null is exactly the\n * condition for the `paired_risk_difference` path, and it is the factor\n * `delta` / `low` / `high` were rescaled by. */\n binaryScale: number | null\n /** Exact-tie fraction over the paired deltas; null when there are no pairs. */\n tieFraction: number | null\n /** Point estimate of the DECIDING statistic, in the caller's native units. */\n delta: number\n /** Lower bound of the DECIDING interval, native units. */\n low: number\n /** Upper bound of the DECIDING interval, native units. */\n high: number\n /** The bootstrap that decided, or null when the score interval did. Callers\n * that need a bootstrap as a diagnostic on the two-point path compute their\n * own — it is not computed here, so the binary path costs no resamples. */\n bootstrap: PairedBootstrapResult | null\n /** McNemar's exact evidence, or null off the two-point path. */\n mcnemar: PairedMcNemarEvidence | null\n /** Exact one-sided sign-test p-value on the small-sample bootstrap path;\n * null otherwise. */\n pValue: number | null\n /** Effective observation minimum after accounting for confidence. */\n minimumPairs: number\n /** n >= minimumPairs. */\n sufficient: boolean\n /** The deciding interval is zero-width or non-finite — no evidence in either\n * direction, so it cannot clear any threshold on evidence. */\n indeterminate: boolean\n /** McNemar's exact test refuses at a non-negative threshold. */\n exactTestVetoes: boolean\n /** The deciding interval clears the threshold, ignoring the other two guards. */\n clearsThreshold: boolean\n /** `sufficient && !indeterminate && clearsThreshold && !exactTestVetoes` —\n * the whole rule. */\n promote: boolean\n /** What `delta` measures, for a reason string. */\n label: 'success-rate' | 'mean' | 'median'\n /** Why a zero-width interval is zero-width; empty when it is not. */\n indeterminateCause: string\n /** Sentence naming the test when the exact sign test decided; else empty. */\n methodDetail: string\n}\n\n/** The shape facts that pick the estimator, without computing an interval. */\nexport interface PairedDecisionShape {\n statistic: PairedDecisionStatistic\n /** Common positive level of a two-point outcome; null when not two-point. */\n binaryScale: number | null\n /** Exact-tie fraction over the paired deltas; null when there are no pairs. */\n tieFraction: number | null\n}\n\n/**\n * Which estimator {@link decidePairedPromotion} would use on this data, and the\n * shape facts behind it — for callers that must report the shape on a path\n * where no interval is computed at all (an early rejection, or zero pairs).\n * Cheap: no bootstrap, no interval.\n */\nexport function pairedDecisionShape(\n before: number[],\n after: number[],\n statistic: 'mean' | 'median' = 'mean',\n): PairedDecisionShape {\n const tieFraction = before.length === 0 ? null : pairedDeltaTieFraction(before, after)\n if (statistic === 'median') {\n return { statistic: 'median_bootstrap', binaryScale: null, tieFraction }\n }\n const binaryScale = pairedBinaryScale(before, after)\n if (binaryScale !== null) {\n return { statistic: 'paired_risk_difference', binaryScale, tieFraction }\n }\n return { statistic: 'mean_bootstrap', binaryScale: null, tieFraction }\n}\n\n/**\n * Decide whether a paired candidate-minus-baseline delta clears a promotion\n * threshold. `before` is the baseline arm, `after` the candidate arm, paired by\n * position. Throws on unequal lengths.\n */\nexport function decidePairedPromotion(\n before: number[],\n after: number[],\n options: PairedPromotionDecisionOptions = {},\n): PairedPromotionDecision {\n if (before.length !== after.length) {\n throw new Error(\n `decidePairedPromotion: unequal sample sizes (${before.length} vs ${after.length})`,\n )\n }\n const threshold = options.threshold ?? 0\n if (!Number.isFinite(threshold)) {\n throw new Error(`decidePairedPromotion: threshold must be finite, got ${threshold}`)\n }\n const confidence = options.confidence ?? 0.95\n const exactMinimum = minimumPairsForPairedDeltaTest(confidence)\n const requestedMinimum = options.minPairs ?? exactMinimum\n if (!Number.isInteger(requestedMinimum) || requestedMinimum < 1) {\n throw new Error(\n `decidePairedPromotion: minPairs must be a positive integer, got ${requestedMinimum}`,\n )\n }\n const minimumPairs = Math.max(requestedMinimum, exactMinimum)\n const n = before.length\n const sufficient = n >= minimumPairs\n const { binaryScale, tieFraction } = pairedDecisionShape(before, after, options.statistic)\n\n let core: {\n statistic: PairedDecisionStatistic\n method: PairedDecisionMethod\n delta: number\n low: number\n high: number\n bootstrap: PairedBootstrapResult | null\n mcnemar: PairedMcNemarEvidence | null\n pValue: number | null\n clearsThreshold: boolean\n label: PairedPromotionDecision['label']\n methodDetail: string\n }\n\n if (binaryScale !== null) {\n // Normalise the two-point encoding to {0,1} so the estimators see the\n // pass/fail structure, then rescale the answer back into the caller's\n // native units — the threshold is read in the units of the scores, so a\n // 0-100 pass/fail dimension must be gated in points, not in a rate.\n const unitControl = before.map((v) => v / binaryScale)\n const unitTreatment = after.map((v) => v / binaryScale)\n // TWO estimators with different jobs, because no single one does both.\n // `exact` is the authority on RD = 0 and supplies the veto; its\n // Clopper-Pearson interval conditions on the discordant count and is\n // therefore NOT a confidence interval at a nonzero margin. `score` is\n // Tango's, which is.\n const exact = pairedRiskDifferenceExact(unitControl, unitTreatment, confidence)\n const score = pairedRiskDifferenceScore(unitControl, unitTreatment, confidence)\n const low = score.lower * binaryScale\n core = {\n statistic: 'paired_risk_difference',\n method: 'score-interval',\n delta: score.riskDifference * binaryScale,\n low,\n high: score.upper * binaryScale,\n bootstrap: null,\n mcnemar: {\n b: exact.b,\n c: exact.c,\n nDiscordant: exact.nDiscordant,\n pValue: exact.pValue,\n },\n pValue: null,\n clearsThreshold: low > threshold,\n label: 'success-rate',\n methodDetail: '',\n }\n } else {\n const bootstrapStatistic = options.statistic === 'median' ? 'median' : 'mean'\n const test = pairedDeltaTest(before, after, {\n confidence,\n resamples: options.resamples,\n statistic: bootstrapStatistic,\n seed: options.seed,\n threshold,\n minPairs: options.minPairs,\n })\n const ci = test.bootstrap\n core = {\n statistic: bootstrapStatistic === 'mean' ? 'mean_bootstrap' : 'median_bootstrap',\n method: test.method,\n delta: bootstrapStatistic === 'mean' ? ci.mean : ci.median,\n low: ci.low,\n high: ci.high,\n bootstrap: ci,\n mcnemar: null,\n pValue: test.pValue,\n clearsThreshold: test.significant,\n label: bootstrapStatistic,\n methodDetail:\n test.method === 'exact-sign'\n ? ` Below ${test.minimumPairs} pairs the interval is descriptive only;` +\n ` the decision is the exact one-sided sign test, p=${fmt(test.pValue ?? 1)}.`\n : '',\n }\n }\n\n const indeterminate =\n !Number.isFinite(core.low) || !Number.isFinite(core.high) || core.low === core.high\n const indeterminateCause = !indeterminate\n ? ''\n : tieFraction === 1\n ? 'every paired delta is an exact tie'\n : core.mcnemar !== null && core.mcnemar.nDiscordant === 0\n ? 'every pair is concordant (0 discordant pairs)'\n : `the ${core.label} CI collapsed to a point at ${fmt(core.low)}`\n // Only at a non-negative threshold: a negative threshold asks a\n // noninferiority question, which McNemar's test of \"no difference\" does not\n // answer.\n const exactTestVetoes =\n core.mcnemar !== null && threshold >= 0 && !(core.mcnemar.pValue < 1 - confidence)\n\n return {\n n,\n threshold,\n confidence,\n binaryScale,\n tieFraction,\n minimumPairs,\n sufficient,\n indeterminate,\n indeterminateCause,\n exactTestVetoes,\n promote: sufficient && !indeterminate && core.clearsThreshold && !exactTestVetoes,\n ...core,\n }\n}\n\nfunction fmt(x: number): string {\n return x.toFixed(4)\n}\n","/**\n * Statistical held-out promotion machinery — the trustworthy core the\n * point-estimate `heldout-delta` gate lacked.\n *\n * The shipped false positive it prevents: a winner re-scored against the\n * baseline on the holdout read run-to-run model NOISE (e.g. 91 vs 95) as a\n * \"+4 lift\" and shipped, because the gate compared point estimates with no\n * confidence interval. Here we pair candidate vs baseline holdout observations\n * and bootstrap a CI on the paired delta — a candidate ships only when the CI\n * lower bound clears the effect-size threshold (the gain is real at the\n * confidence level, not noise), and is blocked when a critical dimension\n * (e.g. `hallucination_free` for a legal agent) significantly regresses even if\n * the net composite rose (anti-Goodhart).\n *\n * Two traps this module is built around (both produce a NEW false positive if\n * gotten wrong):\n * 1. PAIRING GRANULARITY — pairs by FULL `cellId` (`scenario:rep`), never by\n * `scenarioId` (which averages reps away and destroys the within-pair\n * variance reduction that makes a paired bootstrap tighter than unpaired).\n * One paired observation per cell ⇒ reps multiply n.\n * 2. SCALE — a judge may emit composites/dimensions on [0,1] or 0-100. The\n * threshold + tolerance are interpreted in the judge's NATIVE scale; the\n * per-dimension tolerance auto-scales off the observed baseline magnitudes\n * so `-0.10` on [0,1] doesn't silently become a no-op on a 0-100 dimension.\n */\n\nimport {\n decidePairedPromotion,\n type PairedDecisionMethod,\n type PairedDecisionStatistic,\n type PairedMcNemarEvidence,\n type PairedPromotionDecision,\n} from '../../paired-promotion-decision'\nimport {\n DECISION_PAIRED_DELTA_STATISTIC,\n type PairedBootstrapResult,\n pairedBootstrap,\n} from '../../statistics'\nimport type { JudgeScore } from '../types'\n\n/** Tie fraction at/above which a gate annotates its verdict with the tie share.\n * Tie-domination of the median bites structurally at >= 0.5 (the median is then\n * 0 by construction); 0.4 is a softer warn threshold that flags a run APPROACHING\n * that regime, so an operator sees it before the median goes fully blind. */\nexport const TIE_WARN_FRACTION = 0.4\n\nexport interface PairedHoldout {\n /** Baseline scalar per paired cell (same order as `after`/`cellIds`). */\n before: number[]\n /** Candidate scalar per paired cell. */\n after: number[]\n /** The full cellIds (`scenario:rep`) that paired, in order. */\n cellIds: string[]\n}\n\n/**\n * Pair candidate vs baseline holdout observations by FULL cellId. `select`\n * pulls the scalar from a cell's judge reports (composite, or a named\n * dimension); a cell contributes the mean of `select` across its judges. Cells\n * whose scenario is not in `scenarioIds`, or where `select` is undefined for\n * every judge on either side, are skipped on BOTH sides so the arrays stay\n * paired. Throws when the two maps disagree on which holdout cells exist — a\n * load-bearing invariant: the baseline + winner holdout campaigns run the same\n * scenarios with the same seed base, so their cellIds MUST align; a mismatch\n * means a silent pairing bug, not a soft fallback.\n */\nexport function pairHoldout(\n candidate: Map<string, Record<string, JudgeScore>>,\n baseline: Map<string, Record<string, JudgeScore>>,\n scenarioIds: Set<string>,\n select: (s: JudgeScore) => number | undefined,\n): PairedHoldout {\n const cellValue = (\n byCell: Map<string, Record<string, JudgeScore>>,\n cellId: string,\n ): number | undefined => {\n const scores = byCell.get(cellId)\n if (!scores) return undefined\n const vals: number[] = []\n for (const s of Object.values(scores)) {\n if (s.failed === true) {\n throw new Error(`pairHoldout: cell '${cellId}' contains a failed judge score`)\n }\n const v = select(s)\n if (typeof v === 'number' && !Number.isFinite(v)) {\n throw new Error(`pairHoldout: cell '${cellId}' contains a non-finite selected score`)\n }\n if (typeof v === 'number') vals.push(v)\n }\n if (vals.length === 0) return undefined\n return vals.reduce((a, b) => a + b, 0) / vals.length\n }\n\n const inScope = (cellId: string) => scenarioIds.has(cellId.split(':')[0] ?? '')\n const candCells = [...candidate.keys()].filter(inScope).sort()\n const baseCells = [...baseline.keys()].filter(inScope).sort()\n // Alignment invariant — the holdout campaigns share scenarios + seed, so the\n // cell sets must be identical. Differ ⇒ a real pairing bug; fail loud.\n if (candCells.length !== baseCells.length || candCells.some((c, i) => c !== baseCells[i])) {\n throw new Error(\n `pairHoldout: candidate/baseline holdout cells do not align — ` +\n `candidate=[${candCells.join(',')}] baseline=[${baseCells.join(',')}]. ` +\n `Both holdout campaigns must run the same scenarios with the same seed base.`,\n )\n }\n\n const before: number[] = []\n const after: number[] = []\n const cellIds: string[] = []\n for (const cellId of candCells) {\n const b = cellValue(baseline, cellId)\n const a = cellValue(candidate, cellId)\n // A scalar absent on both sides means that dimension was not scored. A\n // one-sided absence is asymmetric evidence loss, never a row to discard.\n if (b === undefined && a === undefined) continue\n if (b === undefined || a === undefined) {\n throw new Error(`pairHoldout: cell '${cellId}' has a selected score on only one arm`)\n }\n before.push(b)\n after.push(a)\n cellIds.push(cellId)\n }\n return { before, after, cellIds }\n}\n\nexport interface HeldoutSignificance {\n paired: PairedHoldout\n /**\n * The paired bootstrap on the requested statistic (MEAN by default — see the\n * tie note on `heldoutSignificance`).\n *\n * DIAGNOSTIC, not necessarily the interval the verdict keyed on. On a\n * two-point (pass/fail) outcome the decision routes to Tango's score interval\n * instead, because a percentile bootstrap of the mean over a three-atom\n * lattice is not a valid interval at a nonzero margin. Read\n * `decision.low`/`decision.high` for the interval that actually decided, and\n * `decisionStatistic` for which one it is.\n */\n bootstrap: PairedBootstrapResult\n /** The MEDIAN paired-delta bootstrap, reported as a diagnostic. When many\n * scenarios are tied (both sides solve them), the median is pinned near 0\n * regardless of the mean lift — comparing the two exposes tie-domination. */\n medianBootstrap: PairedBootstrapResult\n /**\n * The full promotion decision: which estimator the outcome's shape admits,\n * the interval it produced, McNemar's exact veto on the two-point path, and\n * whether the interval was zero-width (no evidence in either direction). The\n * single source of `significant`.\n */\n decision: PairedPromotionDecision\n /** Which paired estimator the verdict was decided on. */\n decisionStatistic: PairedDecisionStatistic\n /** McNemar's exact evidence on the two-point path; null otherwise. */\n mcnemar: PairedMcNemarEvidence | null\n /** Fraction of paired observations that are exact ties (|delta| < 1e-9). A\n * high tie fraction is WHY a median-based gate would have missed a real lift;\n * it is the observability the tie fix adds. */\n tieFraction: number\n /** n paired observations. */\n n: number\n /** Effective minimum after applying the bootstrap's hard statistical floor. */\n minimumRequired: number\n /** Statistical method that carried the decision. */\n decisionMethod: PairedDecisionMethod\n /** Exact one-sided p-value on the small-sample path; otherwise null. */\n pValue: number | null\n /** True iff n >= minimumRequired, the DECIDING interval has nonzero width,\n * its lower bound clears the threshold, and McNemar's exact test does not\n * veto at a non-negative threshold. */\n significant: boolean\n /** Set when n < minimumRequired — too little evidence to claim significance. */\n fewRuns: boolean\n}\n\nexport interface HeldoutSignificanceOptions {\n deltaThreshold?: number\n minProductiveRuns?: number\n confidence?: number\n resamples?: number\n /** Fixed by default for a deterministic, reproducible gate verdict. */\n seed?: number\n statistic?: 'mean' | 'median'\n}\n\n/**\n * Significance of the held-out composite lift: ship only when the lower bound\n * of the interval the outcome's shape ADMITS exceeds `deltaThreshold` (default\n * 0 ⇒ \"confidently positive\"). Interpret `deltaThreshold` in the judge's native\n * scale.\n *\n * The decision is delegated whole to {@link decidePairedPromotion}, the one\n * copy of the rule (`src/paired-promotion-decision.ts`), which `HeldOutGate`\n * also calls. That module's header carries the measurements; the short version\n * is three guards a bare `bootstrap.low > threshold` does not have:\n *\n * - a two-point (pass/fail) outcome decides on Tango's SCORE interval, the\n * only paired-binary construction that stays valid at a nonzero margin;\n * - McNemar's exact test VETOES at any non-negative threshold;\n * - a ZERO-WIDTH interval is refused rather than promoted, in either\n * direction — [0,0] clears every negative threshold and [g,g] clears every\n * threshold below g, and both are an absence of evidence, not a result.\n *\n * Measured on this function before those guards landed, at a nominal 5 %:\n * 14.60 % false promotion at n = 40 on a paired-binary noninferiority boundary,\n * and 88.50 % at n = 6 under a bounded asymmetric null whose true mean paired\n * delta is exactly 0.\n *\n * At small n, where the percentile bootstrap is descriptive only, a\n * pre-registered exact sign test still carries the bootstrap path.\n */\nexport function heldoutSignificance(\n paired: PairedHoldout,\n opts: HeldoutSignificanceOptions = {},\n): HeldoutSignificance {\n const deltaThreshold = opts.deltaThreshold ?? 0\n const confidence = opts.confidence ?? 0.95\n const resamples = opts.resamples ?? 2000\n const seed = opts.seed ?? 1337\n // DEFAULT to the MEAN paired delta, not the median. The median is destroyed by\n // TIES: whenever both baseline and candidate solve a holdout scenario (a common\n // case once the agent is decent — and INCREASINGLY common as you add holdout\n // scenarios for statistical power), that scenario contributes delta 0. When\n // >=50% of paired cells are ties the median is pinned at 0 regardless of a large,\n // consistent lift on the rest, and the gate holds a genuinely better candidate.\n // (Measured live, supervisor-lab run 6: 40 cells, 20 ties, MEAN +0.177, MEDIAN 0\n // → false hold. Doubling the holdout for \"power\" made it WORSE by adding ties.)\n // The mean equals the reported aggregate lift, ties correctly contribute 0\n // without dominating, and it is the textbook paired-comparison estimator; the\n // median is kept as a reported diagnostic. Callers wanting outlier-robustness at\n // the cost of tie-blindness can still pass `statistic: 'median'`.\n const statistic = opts.statistic ?? 'mean'\n const decision = decidePairedPromotion(paired.before, paired.after, {\n confidence,\n resamples,\n statistic,\n seed,\n threshold: deltaThreshold,\n minPairs: opts.minProductiveRuns,\n })\n // `decision.bootstrap` is null exactly when the score interval decided, so\n // the requested-statistic bootstrap is computed here for the diagnostic\n // field. Same two bootstraps as before on every path.\n const bootstrap =\n decision.bootstrap ??\n pairedBootstrap(paired.before, paired.after, { confidence, resamples, statistic, seed })\n const medianBootstrap =\n statistic === 'median'\n ? bootstrap\n : pairedBootstrap(paired.before, paired.after, {\n confidence,\n resamples,\n statistic: 'median',\n seed,\n })\n const n = paired.before.length\n let ties = 0\n for (let i = 0; i < n; i += 1) {\n const after = paired.after[i] ?? 0\n const before = paired.before[i] ?? 0\n if (Math.abs(after - before) < 1e-9) ties += 1\n }\n const tieFraction = n === 0 ? 0 : ties / n\n return {\n paired,\n bootstrap,\n medianBootstrap,\n decision,\n decisionStatistic: decision.statistic,\n mcnemar: decision.mcnemar,\n tieFraction,\n n,\n minimumRequired: decision.minimumPairs,\n decisionMethod: decision.method,\n pValue: decision.pValue,\n significant: decision.promote,\n fewRuns: !decision.sufficient,\n }\n}\n\nexport interface DimensionRegression {\n dimension: string\n /** Paired bootstrap on (candidate − baseline). DIAGNOSTIC on a pass/fail\n * dimension, where `ci` carries the interval that decided instead. */\n bootstrap: PairedBootstrapResult\n /** Which paired statistic `bootstrap.low` is the lower bound of. `'mean'`\n * unless the caller asked for the median. `bootstrap.median` still carries\n * the median point estimate either way. */\n bootstrapStatistic: 'median' | 'mean'\n /** The interval `regressed` was decided on, in the dimension's native units. */\n ci: { low: number; high: number }\n /** Which estimator produced `ci`. */\n decisionStatistic: PairedDecisionStatistic\n /** McNemar's exact evidence on a pass/fail dimension; null otherwise. */\n mcnemar: PairedMcNemarEvidence | null\n /** `ci` has zero width — no evidence in either direction. */\n indeterminate: boolean\n /** True iff the candidate may have regressed this dimension by more than\n * tolerance: the lower bound of the DECIDING interval on (candidate −\n * baseline) is below −tolerance, OR the exact small-sample test proves a drop\n * past tolerance. */\n regressed: boolean\n tolerance: number\n n: number\n}\n\n/** Detect the native scale of a set of scores: 0-100 when any magnitude clears\n * 1.5, else [0,1]. Used to auto-scale the regression tolerance so a default\n * expressed for [0,1] is not silently a no-op on a 0-100 dimension. */\nexport function detectScale(values: number[]): 1 | 100 {\n return values.some((v) => Math.abs(v) > 1.5) ? 100 : 1\n}\n\n/** Per-critical-dimension regression guard. For each dimension, pair the\n * candidate vs baseline values by full cellId and bootstrap the paired delta;\n * a dimension is \"regressed\" when the CI lower bound < −tolerance (conservative\n * — blocks if the credible worst case exceeds tolerance, which is the right\n * posture for safety dimensions like `hallucination_free`). When `tolerance`\n * is omitted it auto-scales: 0.05 on [0,1], 5 on 0-100.\n *\n * The interval comes from {@link decidePairedPromotion}, so a pass/fail\n * dimension is judged on Tango's score interval rather than a percentile\n * bootstrap of the mean — `tolerance` is a NONZERO margin, and the bootstrap\n * is not a valid interval at one. That matters most here because this guard\n * fails OPEN by construction: `tolerance` is positive, so an interval pinned at\n * [0,0] never satisfies `low < −tolerance` and a real regression on a safety\n * dimension would be reported as `regressed: false`. On the median it fails the\n * same way for the same reason — when most pairs tie, which is automatic for a\n * pass/fail dimension on {0,1} and on the 0-100 encoding `detectScale` exists\n * to support, the median CI collapses to [0,0]. Pass `statistic: 'median'` to\n * restore the pre-0.134 behaviour. */\nexport function dimensionRegressions(\n candidate: Map<string, Record<string, JudgeScore>>,\n baseline: Map<string, Record<string, JudgeScore>>,\n scenarioIds: Set<string>,\n criticalDimensions: string[],\n opts: {\n tolerance?: number\n confidence?: number\n resamples?: number\n seed?: number\n /** Paired statistic the CI is computed on. Default `'mean'` — see\n * {@link DECISION_PAIRED_DELTA_STATISTIC} for why the median is not. */\n statistic?: 'mean' | 'median'\n } = {},\n): DimensionRegression[] {\n const out: DimensionRegression[] = []\n for (const dim of criticalDimensions) {\n const paired = pairHoldout(candidate, baseline, scenarioIds, (s) => s.dimensions[dim])\n if (paired.before.length === 0) continue // dimension not scored on this judge\n const tolerance = opts.tolerance ?? 0.05 * detectScale([...paired.before, ...paired.after])\n const bootstrapStatistic = opts.statistic ?? DECISION_PAIRED_DELTA_STATISTIC\n const shared = {\n confidence: opts.confidence ?? 0.95,\n resamples: opts.resamples ?? 2000,\n statistic: bootstrapStatistic,\n seed: opts.seed ?? 1337,\n }\n const guard = decidePairedPromotion(paired.before, paired.after, shared)\n const regression = decidePairedPromotion(paired.after, paired.before, {\n ...shared,\n threshold: tolerance,\n })\n const bootstrap = guard.bootstrap ?? pairedBootstrap(paired.before, paired.after, shared)\n out.push({\n dimension: dim,\n bootstrap,\n bootstrapStatistic,\n ci: { low: guard.low, high: guard.high },\n decisionStatistic: guard.statistic,\n mcnemar: guard.mcnemar,\n indeterminate: guard.indeterminate,\n // Fires on EITHER burden of proof — see `floorBreached` in\n // `promotion-policy.ts` for the same rule and the reasoning. The CI arm\n // is the contract this interface and `default-production-gate`'s reason\n // string both state (\"CI.low < -tolerance\"), read off the DECIDING\n // interval; the exact-test arm adds the small-sample path where the\n // bootstrap interval is descriptive only.\n // The credible-worst-case arm stays on the BOOTSTRAP — see the same\n // decision in `floorBreached` (`promotion-policy.ts`). On a pass/fail\n // dimension the score interval is ±z²/(n+z²) even when every pair is\n // concordant, so reading the floor off it would flag an unchanged safety\n // dimension as regressed at any realistic n. The PROVEN-drop arm does use\n // the shared rule, which is what closes the fail-open hole that mattered:\n // a genuine pass/fail regression is now judged on an interval valid at\n // the nonzero `tolerance`, instead of a bootstrap that is pinned wherever\n // ties dominate.\n regressed: bootstrap.low < -tolerance || regression.promote,\n tolerance,\n n: paired.before.length,\n })\n }\n return out\n}\n","/**\n * Power preflight — \"can this budget detect the effect you are hunting?\"\n *\n * The failure it prevents (measured, twice): a live prompt-improvement campaign ran\n * 333 sandbox cells over 5.6 hours and produced a +0.08 holdout lift the ship gate\n * (paired bootstrap, CI.low > 0.05) could not distinguish from zero — because at\n * that holdout size and worker variance the MINIMUM DETECTABLE lift was larger than\n * any effect a prompt change plausibly produces. The budget was spent learning what\n * a 30-second calculation on the baseline cells already knew. No eval framework we\n * know of surfaces this; every underpowered improvement run everywhere ends in an\n * uninformative \"hold\".\n *\n * Model: the ship rule is `CI.low(paired Δ) > deltaThreshold`. Approximating the\n * bootstrap CI as normal, `CI.low ≈ effect − z·sd_Δ/√n`, so the smallest shippable\n * true effect is `MDE = deltaThreshold + z·sd_Δ/√n`. The paired-delta SD is unknown\n * before the candidate exists; we bound it by the zero-correlation case\n * `sd_Δ ≤ √2·sd_baseline` — a CONSERVATIVE (upper) MDE, which is the correct\n * direction for a warning. Pairing is per cell (`scenario:rep`), so reps multiply n.\n *\n * Standalone by design: feed it any baseline composites (a `gate:'none'` run, a\n * live-proof table) BEFORE budgeting the real search; `selfImprove` also attaches\n * it to every result and warns when the run was structurally unable to ship.\n */\n\nexport interface PowerPreflightOptions {\n /** Per-cell baseline composites on the HOLDOUT scenarios (one per scenario:rep cell). */\n baselineComposites: number[]\n /** Paired observations the budgeted comparison will produce\n * (holdout scenarios × reps). Defaults to `baselineComposites.length`. */\n pairedN?: number\n /** The ship gate's effect-size threshold. Default 0.05 (defaultProductionGate). */\n deltaThreshold?: number\n /** CI confidence the gate uses. Default 0.95. */\n confidence?: number\n /** True when the holdout is scored by the SAME judge/scorer family as the gate\n * (selfImprove's default composition — one judge scores everything). Under a\n * shared channel, raising paired n reduces only the IDIOSYNCRATIC noise share;\n * systematic judge bias is untouched, so the MDE here is a lower bound and the\n * only full debiaser is an independent second scoring channel\n * (recursive-self-improvement S1c, closed form in EXP-023 P0). Default false. */\n sharedScorerChannel?: boolean\n}\n\nexport interface PowerPreflight {\n /** Paired observations the comparison will have. */\n n: number\n /** Baseline per-cell composite standard deviation (the variance the effect must beat). */\n sd: number\n /** Minimum detectable lift: the smallest TRUE effect the gate could ship at this budget. */\n mde: number\n /** Baseline holdout composite mean. */\n baselineMean: number\n /** Headroom to a perfect 1.0 composite (the largest achievable lift on a [0,1] judge). */\n headroom: number\n /** True when even the largest achievable effect (headroom) is below the MDE —\n * the run is structurally unable to ship regardless of proposal quality.\n * Only asserted for [0,1]-scaled judges (see `scaleAssumed`). */\n underpowered: boolean\n /** True when composites look [0,1]-scaled; headroom/underpowered are only\n * meaningful under that convention (0-100 judges get mde/sd/n but no verdict). */\n scaleAssumed: boolean\n deltaThreshold: number\n confidence: number\n /** Set when the holdout shares the gate's scoring channel: more cells cannot\n * buy back systematic judge bias — treat the MDE as a lower bound. */\n sharedChannelCaveat?: string\n /** One actionable sentence for humans and logs. */\n recommendation: string\n}\n\n/** Two-sided z for the common confidence levels; interpolation is overkill here. */\nfunction zFor(confidence: number): number {\n if (confidence >= 0.99) return 2.576\n if (confidence >= 0.95) return 1.96\n if (confidence >= 0.9) return 1.645\n return 1.282\n}\n\n/** Estimate the minimum detectable lift a paired-holdout improvement run can\n * ship at a given budget, from the baseline holdout composites — call it BEFORE\n * spending a search to learn whether the effect you are hunting is even\n * observable at this holdout size and worker variance. */\nexport function powerPreflight(opts: PowerPreflightOptions): PowerPreflight {\n const composites = opts.baselineComposites.filter((v) => Number.isFinite(v))\n if (composites.length < 3) {\n throw new Error(\n `powerPreflight: need >= 3 finite baseline composites to estimate variance, got ${composites.length}`,\n )\n }\n const deltaThreshold = opts.deltaThreshold ?? 0.05\n const confidence = opts.confidence ?? 0.95\n const n = opts.pairedN ?? composites.length\n if (n < 2) throw new Error(`powerPreflight: pairedN must be >= 2, got ${n}`)\n\n const mean = composites.reduce((a, b) => a + b, 0) / composites.length\n const variance =\n composites.reduce((a, b) => a + (b - mean) * (b - mean), 0) / (composites.length - 1)\n const sd = Math.sqrt(variance)\n const z = zFor(confidence)\n const mde = deltaThreshold + (z * Math.SQRT2 * sd) / Math.sqrt(n)\n\n const scaleAssumed = composites.every((v) => v >= -0.001 && v <= 1.5)\n const headroom = Math.max(0, 1 - mean)\n const underpowered = scaleAssumed && mde > headroom\n\n const sharedChannelCaveat = opts.sharedScorerChannel\n ? 'Holdout and gate share one scoring channel: raising n/reps reduces only idiosyncratic noise — systematic judge bias remains and this MDE is a lower bound. Full debiasing needs an independent second scoring channel (different judge/benchmark family).'\n : undefined\n\n const recommendation = underpowered\n ? `UNDERPOWERED: minimum detectable lift ${mde.toFixed(3)} exceeds the ${headroom.toFixed(3)} headroom above the baseline (${mean.toFixed(3)}) — no achievable effect can ship at this budget. Raise paired n (scenarios x reps) to ~${Math.ceil(((z * Math.SQRT2 * sd) / Math.max(headroom - deltaThreshold, 0.01)) ** 2)} or reduce worker variance before searching.`\n : `Minimum detectable lift at n=${n}: ${mde.toFixed(3)} (baseline sd ${sd.toFixed(3)}). Effects smaller than this cannot clear the gate; budget the search for effects you believe exceed it.`\n\n return {\n n,\n sd,\n mde,\n baselineMean: mean,\n headroom,\n underpowered,\n scaleAssumed,\n deltaThreshold,\n confidence,\n ...(sharedChannelCaveat ? { sharedChannelCaveat } : {}),\n recommendation: sharedChannelCaveat\n ? `${recommendation} ${sharedChannelCaveat}`\n : recommendation,\n }\n}\n"],"mappings":"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"}
@@ -1,110 +0,0 @@
1
- import { a as hashCanonical, r as canonicalString } from "./canonical-DPyQ_rpt.js";
2
- import { createHash } from "node:crypto";
3
- //#region src/pre-registration.ts
4
- /**
5
- * Pre-registered hypotheses — declare what you're testing BEFORE the
6
- * run, check it AFTER. Prevents p-hacking, optional stopping, and the
7
- * "we ran until it looked good" failure mode.
8
- *
9
- * Manifest is a plain JSON-friendly object. Sign it with a content hash
10
- * + timestamp; the registered record becomes immutable. Post-run,
11
- * evaluate the manifest against observed results — the library refuses
12
- * to let you re-interpret a different metric as the declared one.
13
- *
14
- * A signed manifest is a portable record: it is written once and verified
15
- * later, possibly by a different release. `algo` names the digest scheme it
16
- * was signed under, and verification selects the encoder by that field, so a
17
- * manifest signed by an earlier release still verifies.
18
- */
19
- /**
20
- * SHA-256 hex (full 64 chars) over the RFC 8785 canonical JSON encoding of
21
- * `obj` — the package's one identity scheme, shared with `ledger-core`.
22
- *
23
- * Values canonical JSON cannot represent faithfully — `undefined`, `NaN`,
24
- * class instances, cycles — are refused rather than coerced, because a
25
- * coercion maps two distinct records onto one digest.
26
- *
27
- * Named `hashJson` to disambiguate from `prompt-registry.ts`'s `hashContent`,
28
- * which takes a string input and returns a truncated 12-char prompt id.
29
- *
30
- * @example
31
- * const hash = await hashJson({ id: '1', kind: 'spec' })
32
- * // 'a3f1...' (64 hex chars)
33
- */
34
- async function hashJson(obj) {
35
- return hashCanonical(obj).slice(7);
36
- }
37
- /**
38
- * Key-sorted `JSON.stringify` digest. Private and read-only: it exists so a
39
- * manifest signed under `'sha256-content'` still verifies, and nothing that
40
- * WRITES a digest may call it.
41
- */
42
- function legacyContentDigest(value) {
43
- return createHash("sha256").update(JSON.stringify(sortKeysDeep(value)), "utf8").digest("hex");
44
- }
45
- function sortKeysDeep(value) {
46
- if (value === null || typeof value !== "object") return value;
47
- if (Array.isArray(value)) return value.map(sortKeysDeep);
48
- const out = {};
49
- for (const key of Object.keys(value).sort()) out[key] = sortKeysDeep(value[key]);
50
- return out;
51
- }
52
- /**
53
- * Digest of a manifest under its own declared scheme, with `contentHash` and
54
- * `algo` stripped. Synchronous, so a caller that must fail before consuming an
55
- * observation does not have to await. Throws on an `algo` this release does
56
- * not know — an unverifiable manifest must not read as a valid one.
57
- */
58
- function manifestContentDigest(manifest) {
59
- const { contentHash: _contentHash, algo, ...rest } = manifest;
60
- if (algo === void 0 || algo === "sha256-content") return legacyContentDigest(rest);
61
- if (algo === "sha256-rfc8785") return createHash("sha256").update(canonicalString(rest), "utf8").digest("hex");
62
- throw new Error(`pre-registration: unrecognized manifest hash algo '${String(algo)}'`);
63
- }
64
- /**
65
- * Sign a manifest with a SHA-256 content hash over its RFC 8785 canonical
66
- * JSON, with `contentHash` and `algo` stripped, and stamp the scheme in
67
- * `algo` so a later reader knows which encoder to verify with.
68
- */
69
- async function signManifest(m) {
70
- const signed = {
71
- ...m,
72
- contentHash: "",
73
- algo: "sha256-rfc8785"
74
- };
75
- return {
76
- ...signed,
77
- contentHash: manifestContentDigest(signed)
78
- };
79
- }
80
- /**
81
- * Verify that a signed manifest has not been tampered with, under the scheme
82
- * the manifest itself declares.
83
- */
84
- async function verifyManifest(m) {
85
- return manifestContentDigest(m) === m.contentHash;
86
- }
87
- /**
88
- * Evaluate a pre-registered hypothesis against observed results.
89
- * Mechanical — no re-interpretation permitted.
90
- */
91
- async function evaluateHypothesis(manifest, observed) {
92
- if (!await verifyManifest(manifest)) throw new Error("evaluateHypothesis: manifest content hash mismatch (tampered)");
93
- const reasons = [];
94
- if (!(manifest.direction === "increase" ? observed.effect > 0 : observed.effect < 0)) reasons.push("wrong_direction");
95
- if (Math.abs(observed.effect) < manifest.minEffect) reasons.push("effect_too_small");
96
- if (observed.pValue >= manifest.alpha) reasons.push("not_significant");
97
- if (observed.n < manifest.preRegisteredN) reasons.push("undersampled");
98
- return {
99
- manifest,
100
- observedN: observed.n,
101
- observedEffect: observed.effect,
102
- observedPValue: observed.pValue,
103
- confirmed: reasons.length === 0,
104
- rejectionReasons: reasons
105
- };
106
- }
107
- //#endregion
108
- export { verifyManifest as a, signManifest as i, hashJson as n, manifestContentDigest as r, evaluateHypothesis as t };
109
-
110
- //# sourceMappingURL=pre-registration-D94b7Of5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pre-registration-D94b7Of5.js","names":[],"sources":["../src/pre-registration.ts"],"sourcesContent":["/**\n * Pre-registered hypotheses — declare what you're testing BEFORE the\n * run, check it AFTER. Prevents p-hacking, optional stopping, and the\n * \"we ran until it looked good\" failure mode.\n *\n * Manifest is a plain JSON-friendly object. Sign it with a content hash\n * + timestamp; the registered record becomes immutable. Post-run,\n * evaluate the manifest against observed results — the library refuses\n * to let you re-interpret a different metric as the declared one.\n *\n * A signed manifest is a portable record: it is written once and verified\n * later, possibly by a different release. `algo` names the digest scheme it\n * was signed under, and verification selects the encoder by that field, so a\n * manifest signed by an earlier release still verifies.\n */\n\nimport { createHash } from 'node:crypto'\nimport { canonicalString, hashCanonical } from './ledger-core/canonical'\n\nexport interface HypothesisManifest {\n id: string\n /** Human prose — goes into the audit trail. */\n hypothesis: string\n /** Metric the hypothesis claims to move. */\n metric: string\n /** 'increase' = candidate should score higher than baseline; 'decrease' = lower. */\n direction: 'increase' | 'decrease'\n /** Minimum effect size to count (same units as the metric). */\n minEffect: number\n /** Alpha threshold. */\n alpha: number\n /** Target statistical power at which sample size was pre-computed. */\n power: number\n /** Declared N per arm before running. */\n preRegisteredN: number\n /** ISO8601 timestamp the manifest was registered. */\n registeredAt: string\n /** Optional identifiers to tie into the trace corpus. */\n baselineLabel?: string\n candidateLabel?: string\n}\n\n/**\n * Identifier for the hashing scheme used to produce `contentHash`.\n *\n * Both schemes are sha256 hex over the manifest with `contentHash` and `algo`\n * stripped, and differ only in how that manifest is serialized:\n *\n * - `'sha256-rfc8785'` — RFC 8785 canonical JSON. What {@link signManifest}\n * emits.\n * - `'sha256-content'` — key-sorted `JSON.stringify`. Read-only: manifests\n * signed by an earlier release carry it, or carry no `algo` at all, and\n * {@link verifyManifest} still verifies them.\n */\nexport type SignedManifestAlgo = 'sha256-content' | 'sha256-rfc8785'\n\nexport interface SignedManifest extends HypothesisManifest {\n /** sha256 hex of canonicalized manifest (everything except contentHash and algo). */\n contentHash: string\n /**\n * Algorithm string describing how `contentHash` was produced.\n *\n * Optional on the type so serialized manifests without it still parse,\n * but ALWAYS populated by {@link signManifest}. Consumers that want to\n * enforce a known algorithm should reject manifests where this field\n * is missing or unrecognized.\n */\n algo?: SignedManifestAlgo\n}\n\nexport interface HypothesisResult {\n manifest: SignedManifest\n observedN: number\n observedEffect: number\n observedPValue: number\n /** True iff the observed effect hits the pre-declared direction with\n * magnitude ≥ minEffect AND p < alpha. */\n confirmed: boolean\n /** Enumerated reasons the hypothesis was rejected (each a machine-tag). */\n rejectionReasons: Array<\n 'wrong_direction' | 'effect_too_small' | 'not_significant' | 'undersampled'\n >\n notes?: string\n}\n\n/**\n * SHA-256 hex (full 64 chars) over the RFC 8785 canonical JSON encoding of\n * `obj` — the package's one identity scheme, shared with `ledger-core`.\n *\n * Values canonical JSON cannot represent faithfully — `undefined`, `NaN`,\n * class instances, cycles — are refused rather than coerced, because a\n * coercion maps two distinct records onto one digest.\n *\n * Named `hashJson` to disambiguate from `prompt-registry.ts`'s `hashContent`,\n * which takes a string input and returns a truncated 12-char prompt id.\n *\n * @example\n * const hash = await hashJson({ id: '1', kind: 'spec' })\n * // 'a3f1...' (64 hex chars)\n */\nexport async function hashJson<T>(obj: T): Promise<string> {\n return hashCanonical(obj).slice('sha256:'.length)\n}\n\n/**\n * Key-sorted `JSON.stringify` digest. Private and read-only: it exists so a\n * manifest signed under `'sha256-content'` still verifies, and nothing that\n * WRITES a digest may call it.\n */\nfunction legacyContentDigest(value: unknown): string {\n return createHash('sha256')\n .update(JSON.stringify(sortKeysDeep(value)), 'utf8')\n .digest('hex')\n}\n\nfunction sortKeysDeep(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map(sortKeysDeep)\n const out: Record<string, unknown> = {}\n for (const key of Object.keys(value as Record<string, unknown>).sort()) {\n out[key] = sortKeysDeep((value as Record<string, unknown>)[key])\n }\n return out\n}\n\n/**\n * Digest of a manifest under its own declared scheme, with `contentHash` and\n * `algo` stripped. Synchronous, so a caller that must fail before consuming an\n * observation does not have to await. Throws on an `algo` this release does\n * not know — an unverifiable manifest must not read as a valid one.\n */\nexport function manifestContentDigest(manifest: SignedManifest): string {\n const { contentHash: _contentHash, algo, ...rest } = manifest\n void _contentHash\n if (algo === undefined || algo === 'sha256-content') return legacyContentDigest(rest)\n if (algo === 'sha256-rfc8785') {\n return createHash('sha256').update(canonicalString(rest), 'utf8').digest('hex')\n }\n throw new Error(`pre-registration: unrecognized manifest hash algo '${String(algo)}'`)\n}\n\n/**\n * Sign a manifest with a SHA-256 content hash over its RFC 8785 canonical\n * JSON, with `contentHash` and `algo` stripped, and stamp the scheme in\n * `algo` so a later reader knows which encoder to verify with.\n */\nexport async function signManifest(m: HypothesisManifest): Promise<SignedManifest> {\n const signed: SignedManifest = { ...m, contentHash: '', algo: 'sha256-rfc8785' }\n return { ...signed, contentHash: manifestContentDigest(signed) }\n}\n\n/**\n * Verify that a signed manifest has not been tampered with, under the scheme\n * the manifest itself declares.\n */\nexport async function verifyManifest(m: SignedManifest): Promise<boolean> {\n return manifestContentDigest(m) === m.contentHash\n}\n\n/**\n * Evaluate a pre-registered hypothesis against observed results.\n * Mechanical — no re-interpretation permitted.\n */\nexport async function evaluateHypothesis(\n manifest: SignedManifest,\n observed: { n: number; effect: number; pValue: number },\n): Promise<HypothesisResult> {\n if (!(await verifyManifest(manifest))) {\n throw new Error('evaluateHypothesis: manifest content hash mismatch (tampered)')\n }\n const reasons: HypothesisResult['rejectionReasons'] = []\n const directionOk = manifest.direction === 'increase' ? observed.effect > 0 : observed.effect < 0\n if (!directionOk) reasons.push('wrong_direction')\n if (Math.abs(observed.effect) < manifest.minEffect) reasons.push('effect_too_small')\n if (observed.pValue >= manifest.alpha) reasons.push('not_significant')\n if (observed.n < manifest.preRegisteredN) reasons.push('undersampled')\n return {\n manifest,\n observedN: observed.n,\n observedEffect: observed.effect,\n observedPValue: observed.pValue,\n confirmed: reasons.length === 0,\n rejectionReasons: reasons,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoGA,eAAsB,SAAY,KAAyB;CACzD,OAAO,cAAc,GAAG,CAAC,CAAC,MAAM,CAAgB;AAClD;;;;;;AAOA,SAAS,oBAAoB,OAAwB;CACnD,OAAO,WAAW,QAAQ,CAAC,CACxB,OAAO,KAAK,UAAU,aAAa,KAAK,CAAC,GAAG,MAAM,CAAC,CACnD,OAAO,KAAK;AACjB;AAEA,SAAS,aAAa,OAAyB;CAC7C,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,IAAI,YAAY;CACvD,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,CAAC,CAAC,KAAK,GACnE,IAAI,OAAO,aAAc,MAAkC,IAAI;CAEjE,OAAO;AACT;;;;;;;AAQA,SAAgB,sBAAsB,UAAkC;CACtE,MAAM,EAAE,aAAa,cAAc,MAAM,GAAG,SAAS;CAErD,IAAI,SAAS,KAAA,KAAa,SAAS,kBAAkB,OAAO,oBAAoB,IAAI;CACpF,IAAI,SAAS,kBACX,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,gBAAgB,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,KAAK;CAEhF,MAAM,IAAI,MAAM,sDAAsD,OAAO,IAAI,EAAE,EAAE;AACvF;;;;;;AAOA,eAAsB,aAAa,GAAgD;CACjF,MAAM,SAAyB;EAAE,GAAG;EAAG,aAAa;EAAI,MAAM;CAAiB;CAC/E,OAAO;EAAE,GAAG;EAAQ,aAAa,sBAAsB,MAAM;CAAE;AACjE;;;;;AAMA,eAAsB,eAAe,GAAqC;CACxE,OAAO,sBAAsB,CAAC,MAAM,EAAE;AACxC;;;;;AAMA,eAAsB,mBACpB,UACA,UAC2B;CAC3B,IAAI,CAAE,MAAM,eAAe,QAAQ,GACjC,MAAM,IAAI,MAAM,+DAA+D;CAEjF,MAAM,UAAgD,CAAC;CAEvD,IAAI,EADgB,SAAS,cAAc,aAAa,SAAS,SAAS,IAAI,SAAS,SAAS,IAC9E,QAAQ,KAAK,iBAAiB;CAChD,IAAI,KAAK,IAAI,SAAS,MAAM,IAAI,SAAS,WAAW,QAAQ,KAAK,kBAAkB;CACnF,IAAI,SAAS,UAAU,SAAS,OAAO,QAAQ,KAAK,iBAAiB;CACrE,IAAI,SAAS,IAAI,SAAS,gBAAgB,QAAQ,KAAK,cAAc;CACrE,OAAO;EACL;EACA,WAAW,SAAS;EACpB,gBAAgB,SAAS;EACzB,gBAAgB,SAAS;EACzB,WAAW,QAAQ,WAAW;EAC9B,kBAAkB;CACpB;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"profile-cell.js","names":[],"sources":["../src/agent-profile-cell.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from './errors'\nimport { hashJson } from './pre-registration'\n\nexport type AgentProfileCellSchemaVersion = 'agent-profile-cell/v1'\n\nexport type AgentProfileJsonObject = { [key: string]: AgentProfileJson }\n\nexport type AgentProfileJson =\n | string\n | number\n | boolean\n | null\n | AgentProfileJson[]\n | AgentProfileJsonObject\n\nexport type AgentProfileDimensionValue = string | number | boolean | null\n\nexport interface AgentProfileSource {\n /** Runtime/profile contract being fingerprinted, e.g. `agent-interface-profile`. */\n kind: string\n /** sha256 over the canonical source profile object. */\n hash: string\n}\n\nexport interface AgentProfileSourceInput {\n kind: string\n /** Precomputed sha256 for callers that already sign their profile artifact. */\n hash?: string\n /** Full canonical runtime profile; hashed and then discarded from the cell. */\n profile?: AgentProfileJson\n}\n\nexport interface AgentProfileHarness {\n id: string\n version?: string\n hash?: string\n}\n\nexport interface AgentProfileCellInput {\n profileId: string\n sourceProfile: AgentProfileSourceInput\n harness?: AgentProfileHarness\n model?: string\n promptHash?: string\n dimensions?: Record<string, AgentProfileDimensionValue>\n}\n\nexport interface AgentProfileCell {\n schemaVersion: AgentProfileCellSchemaVersion\n cellId: string\n profileId: string\n sourceProfile: AgentProfileSource\n harness?: AgentProfileHarness\n model?: string\n promptHash?: string\n dimensions?: Record<string, AgentProfileDimensionValue>\n}\n\nexport class AgentProfileCellValidationError extends ValidationError {\n readonly path: string\n constructor(message: string, path = '') {\n super(path ? `${message} (at ${path})` : message)\n this.path = path\n }\n}\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/\n/**\n * A cell id names the digest scheme that produced it. `sha256-rfc8785` is what\n * {@link buildAgentProfileCell} mints; the bare `sha256` form is read-only,\n * carried by cells built under an earlier release, and still verifies.\n */\nconst CELL_ID = /^agent-profile-cell:sha256(?:-rfc8785)?:[0-9a-f]{64}$/\nconst CELL_ID_PREFIX = 'agent-profile-cell:sha256-rfc8785:'\nconst LEGACY_CELL_ID_PREFIX = 'agent-profile-cell:sha256:'\n\nexport async function buildAgentProfileCell(\n input: AgentProfileCellInput,\n): Promise<AgentProfileCell> {\n const material = await normalizeAgentProfileCellInput(input)\n const cellId = `${CELL_ID_PREFIX}${await hashJson(material)}`\n return { ...material, cellId }\n}\n\nexport function agentProfileCellHashMaterial(\n cell: AgentProfileCell,\n): Omit<AgentProfileCell, 'cellId'> {\n const { cellId: _cellId, ...material } = cell\n void _cellId\n return normalizeAgentProfileCell(material)\n}\n\n/**\n * Verify an `AgentProfileCell`'s `cellId` matches the sha256 of its hash-material\n * fields, confirming the record has not been tampered with. The id names its own\n * digest scheme, so a cell minted by an earlier release verifies under that scheme.\n */\nexport async function verifyAgentProfileCell(cell: AgentProfileCell): Promise<boolean> {\n validateAgentProfileCell(cell)\n const material = agentProfileCellHashMaterial(cell)\n if (cell.cellId.startsWith(CELL_ID_PREFIX)) {\n return cell.cellId === `${CELL_ID_PREFIX}${await hashJson(material)}`\n }\n return cell.cellId === `${LEGACY_CELL_ID_PREFIX}${legacyCellDigest(material)}`\n}\n\n/**\n * Key-sorted `JSON.stringify` digest. Private and read-only: it verifies a cell\n * id minted before the RFC 8785 scheme, and no path that MINTS an id calls it.\n */\nfunction legacyCellDigest(value: unknown): string {\n return createHash('sha256')\n .update(JSON.stringify(sortKeysDeep(value)), 'utf8')\n .digest('hex')\n}\n\nfunction sortKeysDeep(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map(sortKeysDeep)\n const out: Record<string, unknown> = {}\n for (const key of Object.keys(value as Record<string, unknown>).sort()) {\n out[key] = sortKeysDeep((value as Record<string, unknown>)[key])\n }\n return out\n}\n\nexport function validateAgentProfileCell(input: unknown): AgentProfileCell {\n if (input === null || typeof input !== 'object') {\n throw new AgentProfileCellValidationError('expected object')\n }\n const obj = input as Record<string, unknown>\n expectLiteral(obj.schemaVersion, 'agent-profile-cell/v1', 'schemaVersion')\n if (typeof obj.cellId !== 'string' || !CELL_ID.test(obj.cellId)) {\n throw new AgentProfileCellValidationError(\n 'cellId must match agent-profile-cell:sha256:<64 lowercase hex chars>',\n 'cellId',\n )\n }\n expectString(obj.profileId, 'profileId')\n validateSource(obj.sourceProfile, 'sourceProfile')\n if (obj.harness !== undefined) validateHarness(obj.harness, 'harness')\n if (obj.model !== undefined) expectString(obj.model, 'model')\n if (obj.promptHash !== undefined) expectString(obj.promptHash, 'promptHash')\n if (obj.dimensions !== undefined) validateDimensions(obj.dimensions, 'dimensions')\n return input as AgentProfileCell\n}\n\nexport function requireAgentProfileCell(record: {\n runId: string\n agentProfile?: AgentProfileCell\n}): AgentProfileCell {\n if (!record.agentProfile) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" is missing agentProfile; profile-cell grouping requires explicit profile identity`,\n 'agentProfile',\n )\n }\n return validateAgentProfileCell(record.agentProfile)\n}\n\nexport function agentProfileCellKey(record: {\n runId: string\n agentProfile?: AgentProfileCell\n}): string {\n return requireAgentProfileCell(record).cellId\n}\n\nexport async function assertRunAgentProfileCell(record: {\n runId: string\n model: string\n promptHash: string\n agentProfile?: AgentProfileCell\n}): Promise<AgentProfileCell> {\n const profile = requireAgentProfileCell(record)\n if (!(await verifyAgentProfileCell(profile))) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" has an agentProfile.cellId that does not match its content`,\n 'agentProfile.cellId',\n )\n }\n if (profile.model !== undefined && profile.model !== record.model) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" agentProfile.model \"${profile.model}\" does not match model \"${record.model}\"`,\n 'agentProfile.model',\n )\n }\n if (profile.promptHash !== undefined && profile.promptHash !== record.promptHash) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" agentProfile.promptHash \"${profile.promptHash}\" does not match promptHash \"${record.promptHash}\"`,\n 'agentProfile.promptHash',\n )\n }\n return profile\n}\n\nexport function groupRunsByAgentProfileCell<\n T extends { runId: string; agentProfile?: AgentProfileCell },\n>(records: readonly T[]): Map<string, T[]> {\n const groups = new Map<string, T[]>()\n for (const record of records) {\n const key = agentProfileCellKey(record)\n const bucket = groups.get(key)\n if (bucket) bucket.push(record)\n else groups.set(key, [record])\n }\n return groups\n}\n\nasync function normalizeAgentProfileCellInput(\n input: AgentProfileCellInput,\n): Promise<Omit<AgentProfileCell, 'cellId'>> {\n return normalizeAgentProfileCell({\n schemaVersion: 'agent-profile-cell/v1',\n profileId: input.profileId,\n sourceProfile: await normalizeSourceInput(input.sourceProfile),\n harness: input.harness,\n model: input.model,\n promptHash: input.promptHash,\n dimensions: input.dimensions,\n })\n}\n\nfunction normalizeAgentProfileCell(\n input: Omit<AgentProfileCell, 'cellId'>,\n): Omit<AgentProfileCell, 'cellId'> {\n return compactObject({\n schemaVersion: 'agent-profile-cell/v1' as const,\n profileId: requireNonEmpty(input.profileId, 'profileId'),\n sourceProfile: normalizeSource(input.sourceProfile),\n harness: input.harness ? normalizeHarness(input.harness, 'harness') : undefined,\n model: optionalNonEmpty(input.model, 'model'),\n promptHash: optionalNonEmpty(input.promptHash, 'promptHash'),\n dimensions: input.dimensions\n ? nonEmptyRecord(normalizeDimensions(input.dimensions))\n : undefined,\n })\n}\n\nasync function normalizeSourceInput(input: AgentProfileSourceInput): Promise<AgentProfileSource> {\n const kind = requireNonEmpty(input.kind, 'sourceProfile.kind')\n if (input.hash !== undefined && input.profile !== undefined) {\n throw new AgentProfileCellValidationError(\n 'sourceProfile must provide either hash or profile, not both',\n 'sourceProfile',\n )\n }\n if (input.hash !== undefined) {\n return { kind, hash: requireSha256Hex(input.hash, 'sourceProfile.hash') }\n }\n if (input.profile === undefined) {\n throw new AgentProfileCellValidationError(\n 'sourceProfile must provide hash or profile',\n 'sourceProfile',\n )\n }\n assertJson(input.profile, 'sourceProfile.profile')\n return { kind, hash: await hashJson(input.profile) }\n}\n\nfunction normalizeSource(input: AgentProfileSource): AgentProfileSource {\n return {\n kind: requireNonEmpty(input.kind, 'sourceProfile.kind'),\n hash: requireSha256Hex(input.hash, 'sourceProfile.hash'),\n }\n}\n\nfunction normalizeHarness(input: AgentProfileHarness, path: string): AgentProfileHarness {\n return compactObject({\n id: requireNonEmpty(input.id, `${path}.id`),\n version: optionalNonEmpty(input.version, `${path}.version`),\n hash: optionalNonEmpty(input.hash, `${path}.hash`),\n })\n}\n\nfunction normalizeDimensions(\n input: Record<string, AgentProfileDimensionValue>,\n): Record<string, AgentProfileDimensionValue> {\n const out: Record<string, AgentProfileDimensionValue> = {}\n for (const key of Object.keys(input).sort()) {\n const value = input[key]\n requireNonEmpty(key, 'dimensions.<key>')\n if (\n value !== null &&\n typeof value !== 'string' &&\n typeof value !== 'number' &&\n typeof value !== 'boolean'\n ) {\n throw new AgentProfileCellValidationError(\n 'expected primitive dimension value',\n `dimensions.${key}`,\n )\n }\n if (typeof value === 'number' && !Number.isFinite(value)) {\n throw new AgentProfileCellValidationError('expected finite number', `dimensions.${key}`)\n }\n out[key] = value\n }\n return out\n}\n\nfunction compactObject<T extends Record<string, unknown>>(input: T): T {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(input)) {\n if (value !== undefined) out[key] = value\n }\n return out as T\n}\n\nfunction nonEmptyRecord<T extends Record<string, unknown>>(input: T): T | undefined {\n return Object.keys(input).length > 0 ? input : undefined\n}\n\nfunction validateSource(value: unknown, path: string): void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new AgentProfileCellValidationError('expected object', path)\n }\n const rec = value as Record<string, unknown>\n expectString(rec.kind, `${path}.kind`)\n requireSha256Hex(rec.hash, `${path}.hash`)\n}\n\nfunction validateHarness(value: unknown, path: string): void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new AgentProfileCellValidationError('expected object', path)\n }\n const rec = value as Record<string, unknown>\n expectString(rec.id, `${path}.id`)\n if (rec.version !== undefined) expectString(rec.version, `${path}.version`)\n if (rec.hash !== undefined) expectString(rec.hash, `${path}.hash`)\n}\n\nfunction validateDimensions(value: unknown, path: string): void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new AgentProfileCellValidationError('expected object', path)\n }\n normalizeDimensions(value as Record<string, AgentProfileDimensionValue>)\n}\n\nfunction assertJson(value: AgentProfileJson, path: string): void {\n if (value === null) return\n const type = typeof value\n if (type === 'string' || type === 'boolean') return\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new AgentProfileCellValidationError('expected finite number', path)\n }\n return\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n assertJson(item, `${path}[${index}]`)\n })\n return\n }\n if (type === 'object') {\n for (const [key, nested] of Object.entries(value)) {\n requireNonEmpty(key, `${path}.<key>`)\n assertJson(nested, `${path}.${key}`)\n }\n return\n }\n throw new AgentProfileCellValidationError('expected JSON-compatible value', path)\n}\n\nfunction expectLiteral(value: unknown, expected: string, path: string): void {\n if (value !== expected) {\n throw new AgentProfileCellValidationError(`expected ${expected}`, path)\n }\n}\n\nfunction expectString(value: unknown, path: string): void {\n if (typeof value !== 'string' || value.length === 0) {\n throw new AgentProfileCellValidationError('expected non-empty string', path)\n }\n}\n\nfunction requireNonEmpty(value: string, path: string): string {\n if (typeof value !== 'string' || value.length === 0) {\n throw new AgentProfileCellValidationError('expected non-empty string', path)\n }\n return value\n}\n\nfunction optionalNonEmpty(value: string | undefined, path: string): string | undefined {\n if (value === undefined) return undefined\n return requireNonEmpty(value, path)\n}\n\nfunction requireSha256Hex(value: unknown, path: string): string {\n if (typeof value !== 'string' || !SHA256_HEX.test(value)) {\n throw new AgentProfileCellValidationError('expected 64 lowercase sha256 hex chars', path)\n }\n return value\n}\n\n// ── Consumer helpers ─────────────────────────────────────────────────\n//\n// Boilerplate every product consuming `buildAgentProfileCell` used to duplicate:\n//\n// 1. A `JSON.parse(JSON.stringify(value))` helper that canonicalizes an\n// arbitrary `@tangle-network/agent-interface` `AgentProfile` into the recursive\n// `AgentProfileJson` shape, with a fail-loud error when the profile\n// is not JSON-serializable.\n//\n// 2. The magic string `'agent-interface-profile'` for `sourceProfile.kind`.\n//\n// Both belong here so the cross-product cell join (same canonical profile\n// hashes to the same `sourceProfile.hash` across products) is enforced by\n// the type system, not by every consumer remembering to do it right.\n// See blueprint-agent issue tangle-network/agent-eval#82.\n\n/** Canonical `sourceProfile.kind` values. Two products fingerprinting the\n * same canonical profile MUST use the same kind for their cells to share\n * `sourceProfile.hash`. Extend rather than create new strings — adding a\n * new kind is a deliberate cross-product schema change. */\nexport const AGENT_PROFILE_KINDS = {\n /** A profile declared via `defineAgentProfile(...)` from\n * `@tangle-network/agent-interface`. The default kind for router-backed\n * and sandbox-backed products. */\n AGENT_INTERFACE_PROFILE: 'agent-interface-profile',\n} as const\n\nexport type AgentProfileKind = (typeof AGENT_PROFILE_KINDS)[keyof typeof AGENT_PROFILE_KINDS]\n\n/** Canonicalize an arbitrary value into `AgentProfileJson` by JSON\n * round-trip. Throws when the value contains anything not representable\n * as JSON (functions, BigInt, cycles) — non-portable profiles fail loud\n * rather than silently dropping fields. */\nexport function toAgentProfileJson(value: unknown): AgentProfileJson {\n let serialized: string | undefined\n try {\n serialized = JSON.stringify(value)\n } catch (err) {\n throw new AgentProfileCellValidationError(\n `agent profile must be JSON-serializable: ${err instanceof Error ? err.message : String(err)}`,\n 'sourceProfile.profile',\n )\n }\n if (serialized === undefined) {\n throw new AgentProfileCellValidationError(\n 'agent profile must be JSON-serializable (got undefined after JSON.stringify)',\n 'sourceProfile.profile',\n )\n }\n return JSON.parse(serialized) as AgentProfileJson\n}\n\n/** Canonical AgentProfile shape required when deriving a stable cell id. */\nexport type AgentInterfaceProfileLike = AgentProfile & { name: string; version: string }\n\n/** Higher-level helper that hard-codes the canonical\n * `agent-interface-profile` kind plus the JSON canonicalization. Equivalent\n * to calling `buildAgentProfileCell` with `profileId = \\`${name}@${version}\\``\n * and `sourceProfile = { kind: AGENT_INTERFACE_PROFILE, profile: <round-tripped> }`.\n *\n * Use this from any product consuming an agent-interface `AgentProfile`; the\n * manual `buildAgentProfileCell` call is reserved for advanced cases\n * (custom kinds, pre-computed source hashes, alternate profileId\n * conventions). */\nexport async function buildAgentInterfaceProfileCell(\n profile: AgentInterfaceProfileLike,\n input: Omit<AgentProfileCellInput, 'profileId' | 'sourceProfile'>,\n): Promise<AgentProfileCell> {\n if (!profile || typeof profile !== 'object') {\n throw new AgentProfileCellValidationError('AgentProfile must be an object', 'profile')\n }\n if (typeof profile.name !== 'string' || profile.name.length === 0) {\n throw new AgentProfileCellValidationError(\n 'AgentProfile must have a non-empty `name`',\n 'profile.name',\n )\n }\n if (typeof profile.version !== 'string' || profile.version.length === 0) {\n throw new AgentProfileCellValidationError(\n 'AgentProfile must have a non-empty `version`',\n 'profile.version',\n )\n }\n return buildAgentProfileCell({\n ...input,\n profileId: `${profile.name}@${profile.version}`,\n sourceProfile: {\n kind: AGENT_PROFILE_KINDS.AGENT_INTERFACE_PROFILE,\n profile: toAgentProfileJson(profile),\n },\n })\n}\n"],"mappings":";;;;AA4DA,IAAa,kCAAb,cAAqD,gBAAgB;CACnE;CACA,YAAY,SAAiB,OAAO,IAAI;EACtC,MAAM,OAAO,GAAG,QAAQ,OAAO,KAAK,KAAK,OAAO;EAChD,KAAK,OAAO;CACd;AACF;AAEA,MAAM,aAAa;;;;;;AAMnB,MAAM,UAAU;AAChB,MAAM,iBAAiB;AACvB,MAAM,wBAAwB;AAE9B,eAAsB,sBACpB,OAC2B;CAC3B,MAAM,WAAW,MAAM,+BAA+B,KAAK;CAC3D,MAAM,SAAS,GAAG,iBAAiB,MAAM,SAAS,QAAQ;CAC1D,OAAO;EAAE,GAAG;EAAU;CAAO;AAC/B;AAEA,SAAgB,6BACd,MACkC;CAClC,MAAM,EAAE,QAAQ,SAAS,GAAG,aAAa;CAEzC,OAAO,0BAA0B,QAAQ;AAC3C;;;;;;AAOA,eAAsB,uBAAuB,MAA0C;CACrF,yBAAyB,IAAI;CAC7B,MAAM,WAAW,6BAA6B,IAAI;CAClD,IAAI,KAAK,OAAO,WAAW,cAAc,GACvC,OAAO,KAAK,WAAW,GAAG,iBAAiB,MAAM,SAAS,QAAQ;CAEpE,OAAO,KAAK,WAAW,GAAG,wBAAwB,iBAAiB,QAAQ;AAC7E;;;;;AAMA,SAAS,iBAAiB,OAAwB;CAChD,OAAO,WAAW,QAAQ,CAAC,CACxB,OAAO,KAAK,UAAU,aAAa,KAAK,CAAC,GAAG,MAAM,CAAC,CACnD,OAAO,KAAK;AACjB;AAEA,SAAS,aAAa,OAAyB;CAC7C,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,IAAI,YAAY;CACvD,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,CAAC,CAAC,KAAK,GACnE,IAAI,OAAO,aAAc,MAAkC,IAAI;CAEjE,OAAO;AACT;AAEA,SAAgB,yBAAyB,OAAkC;CACzE,IAAI,UAAU,QAAQ,OAAO,UAAU,UACrC,MAAM,IAAI,gCAAgC,iBAAiB;CAE7D,MAAM,MAAM;CACZ,cAAc,IAAI,eAAe,yBAAyB,eAAe;CACzE,IAAI,OAAO,IAAI,WAAW,YAAY,CAAC,QAAQ,KAAK,IAAI,MAAM,GAC5D,MAAM,IAAI,gCACR,wEACA,QACF;CAEF,aAAa,IAAI,WAAW,WAAW;CACvC,eAAe,IAAI,eAAe,eAAe;CACjD,IAAI,IAAI,YAAY,KAAA,GAAW,gBAAgB,IAAI,SAAS,SAAS;CACrE,IAAI,IAAI,UAAU,KAAA,GAAW,aAAa,IAAI,OAAO,OAAO;CAC5D,IAAI,IAAI,eAAe,KAAA,GAAW,aAAa,IAAI,YAAY,YAAY;CAC3E,IAAI,IAAI,eAAe,KAAA,GAAW,mBAAmB,IAAI,YAAY,YAAY;CACjF,OAAO;AACT;AAEA,SAAgB,wBAAwB,QAGnB;CACnB,IAAI,CAAC,OAAO,cACV,MAAM,IAAI,gCACR,QAAQ,OAAO,MAAM,sFACrB,cACF;CAEF,OAAO,yBAAyB,OAAO,YAAY;AACrD;AAEA,SAAgB,oBAAoB,QAGzB;CACT,OAAO,wBAAwB,MAAM,CAAC,CAAC;AACzC;AAEA,eAAsB,0BAA0B,QAKlB;CAC5B,MAAM,UAAU,wBAAwB,MAAM;CAC9C,IAAI,CAAE,MAAM,uBAAuB,OAAO,GACxC,MAAM,IAAI,gCACR,QAAQ,OAAO,MAAM,+DACrB,qBACF;CAEF,IAAI,QAAQ,UAAU,KAAA,KAAa,QAAQ,UAAU,OAAO,OAC1D,MAAM,IAAI,gCACR,QAAQ,OAAO,MAAM,wBAAwB,QAAQ,MAAM,0BAA0B,OAAO,MAAM,IAClG,oBACF;CAEF,IAAI,QAAQ,eAAe,KAAA,KAAa,QAAQ,eAAe,OAAO,YACpE,MAAM,IAAI,gCACR,QAAQ,OAAO,MAAM,6BAA6B,QAAQ,WAAW,+BAA+B,OAAO,WAAW,IACtH,yBACF;CAEF,OAAO;AACT;AAEA,SAAgB,4BAEd,SAAyC;CACzC,MAAM,yBAAS,IAAI,IAAiB;CACpC,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,MAAM,oBAAoB,MAAM;EACtC,MAAM,SAAS,OAAO,IAAI,GAAG;EAC7B,IAAI,QAAQ,OAAO,KAAK,MAAM;OACzB,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;CAC/B;CACA,OAAO;AACT;AAEA,eAAe,+BACb,OAC2C;CAC3C,OAAO,0BAA0B;EAC/B,eAAe;EACf,WAAW,MAAM;EACjB,eAAe,MAAM,qBAAqB,MAAM,aAAa;EAC7D,SAAS,MAAM;EACf,OAAO,MAAM;EACb,YAAY,MAAM;EAClB,YAAY,MAAM;CACpB,CAAC;AACH;AAEA,SAAS,0BACP,OACkC;CAClC,OAAO,cAAc;EACnB,eAAe;EACf,WAAW,gBAAgB,MAAM,WAAW,WAAW;EACvD,eAAe,gBAAgB,MAAM,aAAa;EAClD,SAAS,MAAM,UAAU,iBAAiB,MAAM,SAAS,SAAS,IAAI,KAAA;EACtE,OAAO,iBAAiB,MAAM,OAAO,OAAO;EAC5C,YAAY,iBAAiB,MAAM,YAAY,YAAY;EAC3D,YAAY,MAAM,aACd,eAAe,oBAAoB,MAAM,UAAU,CAAC,IACpD,KAAA;CACN,CAAC;AACH;AAEA,eAAe,qBAAqB,OAA6D;CAC/F,MAAM,OAAO,gBAAgB,MAAM,MAAM,oBAAoB;CAC7D,IAAI,MAAM,SAAS,KAAA,KAAa,MAAM,YAAY,KAAA,GAChD,MAAM,IAAI,gCACR,+DACA,eACF;CAEF,IAAI,MAAM,SAAS,KAAA,GACjB,OAAO;EAAE;EAAM,MAAM,iBAAiB,MAAM,MAAM,oBAAoB;CAAE;CAE1E,IAAI,MAAM,YAAY,KAAA,GACpB,MAAM,IAAI,gCACR,8CACA,eACF;CAEF,WAAW,MAAM,SAAS,uBAAuB;CACjD,OAAO;EAAE;EAAM,MAAM,MAAM,SAAS,MAAM,OAAO;CAAE;AACrD;AAEA,SAAS,gBAAgB,OAA+C;CACtE,OAAO;EACL,MAAM,gBAAgB,MAAM,MAAM,oBAAoB;EACtD,MAAM,iBAAiB,MAAM,MAAM,oBAAoB;CACzD;AACF;AAEA,SAAS,iBAAiB,OAA4B,MAAmC;CACvF,OAAO,cAAc;EACnB,IAAI,gBAAgB,MAAM,IAAI,GAAG,KAAK,IAAI;EAC1C,SAAS,iBAAiB,MAAM,SAAS,GAAG,KAAK,SAAS;EAC1D,MAAM,iBAAiB,MAAM,MAAM,GAAG,KAAK,MAAM;CACnD,CAAC;AACH;AAEA,SAAS,oBACP,OAC4C;CAC5C,MAAM,MAAkD,CAAC;CACzD,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,KAAK,GAAG;EAC3C,MAAM,QAAQ,MAAM;EACpB,gBAAgB,KAAK,kBAAkB;EACvC,IACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,WAEjB,MAAM,IAAI,gCACR,sCACA,cAAc,KAChB;EAEF,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GACrD,MAAM,IAAI,gCAAgC,0BAA0B,cAAc,KAAK;EAEzF,IAAI,OAAO;CACb;CACA,OAAO;AACT;AAEA,SAAS,cAAiD,OAAa;CACrE,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,UAAU,KAAA,GAAW,IAAI,OAAO;CAEtC,OAAO;AACT;AAEA,SAAS,eAAkD,OAAyB;CAClF,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ,KAAA;AACjD;AAEA,SAAS,eAAe,OAAgB,MAAoB;CAC1D,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GACpE,MAAM,IAAI,gCAAgC,mBAAmB,IAAI;CAEnE,MAAM,MAAM;CACZ,aAAa,IAAI,MAAM,GAAG,KAAK,MAAM;CACrC,iBAAiB,IAAI,MAAM,GAAG,KAAK,MAAM;AAC3C;AAEA,SAAS,gBAAgB,OAAgB,MAAoB;CAC3D,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GACpE,MAAM,IAAI,gCAAgC,mBAAmB,IAAI;CAEnE,MAAM,MAAM;CACZ,aAAa,IAAI,IAAI,GAAG,KAAK,IAAI;CACjC,IAAI,IAAI,YAAY,KAAA,GAAW,aAAa,IAAI,SAAS,GAAG,KAAK,SAAS;CAC1E,IAAI,IAAI,SAAS,KAAA,GAAW,aAAa,IAAI,MAAM,GAAG,KAAK,MAAM;AACnE;AAEA,SAAS,mBAAmB,OAAgB,MAAoB;CAC9D,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GACpE,MAAM,IAAI,gCAAgC,mBAAmB,IAAI;CAEnE,oBAAoB,KAAmD;AACzE;AAEA,SAAS,WAAW,OAAyB,MAAoB;CAC/D,IAAI,UAAU,MAAM;CACpB,MAAM,OAAO,OAAO;CACpB,IAAI,SAAS,YAAY,SAAS,WAAW;CAC7C,IAAI,SAAS,UAAU;EACrB,IAAI,CAAC,OAAO,SAAS,KAAK,GACxB,MAAM,IAAI,gCAAgC,0BAA0B,IAAI;EAE1E;CACF;CACA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,MAAM,SAAS,MAAM,UAAU;GAC7B,WAAW,MAAM,GAAG,KAAK,GAAG,MAAM,EAAE;EACtC,CAAC;EACD;CACF;CACA,IAAI,SAAS,UAAU;EACrB,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,KAAK,GAAG;GACjD,gBAAgB,KAAK,GAAG,KAAK,OAAO;GACpC,WAAW,QAAQ,GAAG,KAAK,GAAG,KAAK;EACrC;EACA;CACF;CACA,MAAM,IAAI,gCAAgC,kCAAkC,IAAI;AAClF;AAEA,SAAS,cAAc,OAAgB,UAAkB,MAAoB;CAC3E,IAAI,UAAU,UACZ,MAAM,IAAI,gCAAgC,YAAY,YAAY,IAAI;AAE1E;AAEA,SAAS,aAAa,OAAgB,MAAoB;CACxD,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAChD,MAAM,IAAI,gCAAgC,6BAA6B,IAAI;AAE/E;AAEA,SAAS,gBAAgB,OAAe,MAAsB;CAC5D,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAChD,MAAM,IAAI,gCAAgC,6BAA6B,IAAI;CAE7E,OAAO;AACT;AAEA,SAAS,iBAAiB,OAA2B,MAAkC;CACrF,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,gBAAgB,OAAO,IAAI;AACpC;AAEA,SAAS,iBAAiB,OAAgB,MAAsB;CAC9D,IAAI,OAAO,UAAU,YAAY,CAAC,WAAW,KAAK,KAAK,GACrD,MAAM,IAAI,gCAAgC,0CAA0C,IAAI;CAE1F,OAAO;AACT;;;;;AAsBA,MAAa,sBAAsB;;;;AAIjC,yBAAyB,0BAC3B;;;;;AAQA,SAAgB,mBAAmB,OAAkC;CACnE,IAAI;CACJ,IAAI;EACF,aAAa,KAAK,UAAU,KAAK;CACnC,SAAS,KAAK;EACZ,MAAM,IAAI,gCACR,4CAA4C,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,KAC3F,uBACF;CACF;CACA,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,gCACR,gFACA,uBACF;CAEF,OAAO,KAAK,MAAM,UAAU;AAC9B;;;;;;;;;;AAcA,eAAsB,+BACpB,SACA,OAC2B;CAC3B,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,MAAM,IAAI,gCAAgC,kCAAkC,SAAS;CAEvF,IAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,WAAW,GAC9D,MAAM,IAAI,gCACR,6CACA,cACF;CAEF,IAAI,OAAO,QAAQ,YAAY,YAAY,QAAQ,QAAQ,WAAW,GACpE,MAAM,IAAI,gCACR,gDACA,iBACF;CAEF,OAAO,sBAAsB;EAC3B,GAAG;EACH,WAAW,GAAG,QAAQ,KAAK,GAAG,QAAQ;EACtC,eAAe;GACb,MAAM,oBAAoB;GAC1B,SAAS,mBAAmB,OAAO;EACrC;CACF,CAAC;AACH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"reward-hacking-CKW4teig.js","names":["mean","clamp01"],"sources":["../src/campaign/run-record.ts","../src/rl/verifiable-reward.ts","../src/rl/reward-hacking.ts"],"sourcesContent":["import type { AgentProfileCell } from '../agent-profile-cell'\nimport type { CostProvenance } from '../cost-ledger'\nimport type {\n JudgeScoresRecord,\n RunOutcome,\n RunRecord,\n RunSplitTag,\n RunTerminalOutcome,\n} from '../run-record'\nimport { validateRunRecord } from '../run-record'\nimport type { CampaignCellResult, JudgeScore } from './types'\n\nexport interface CampaignCellRunRecordOptions {\n runId: string\n experimentId: string\n candidateId: string\n model: string\n promptHash: string\n configHash: string\n commitSha: string\n splitTag: RunSplitTag\n seed?: number\n scenarioId?: string\n defaultCostUsd?: number\n agentProfile?: AgentProfileCell\n raw?: Record<string, number>\n}\n\nexport interface CampaignCellQualityProjection {\n score?: number\n judgeScores?: JudgeScoresRecord\n successfulJudgeScores: Record<string, JudgeScore>\n failedJudges: string[]\n raw: Record<string, number>\n}\n\n/**\n * A campaign cell carried a judge score without a `dimensions` record.\n *\n * Two exported types share the name `JudgeScore`: the campaign verdict\n * (`{ dimensions, composite, notes }` from `@tangle-network/agent-eval/campaign`)\n * and the root export's flat per-dimension row (`{ judgeName, dimension, score }`\n * from `@tangle-network/agent-eval`). Campaign aggregation accepts only the\n * campaign shape; the flat shape previously crashed here with an opaque\n * TypeError deep inside aggregation.\n */\nexport class CampaignJudgeScoreShapeError extends TypeError {\n constructor(judgeName: string) {\n super(\n `campaign cell judge '${judgeName}' carries a score without a 'dimensions' record. ` +\n `Use the campaign JudgeScore ({ dimensions, composite, notes }) from ` +\n `'@tangle-network/agent-eval/campaign'; the root export's JudgeScore ` +\n `({ judgeName, dimension, score }) is a different type with the same name.`,\n )\n this.name = 'CampaignJudgeScoreShapeError'\n }\n}\n\nexport interface CampaignCellExecutionEvidence {\n terminalOutcome: RunTerminalOutcome\n executionErrorCount?: number\n judgeErrorCount?: number\n unclassifiedErrorCount?: number\n terminalFailureReason?: string\n}\n\n/**\n * Project one campaign cell into the canonical run format.\n *\n * A dispatch error establishes terminal execution failure. A judge error only\n * establishes that quality measurement failed after dispatch completed.\n * Failures without a stage remain unknown. No failure becomes a zero-quality\n * label.\n */\nexport function campaignCellToRunRecord<TArtifact>(\n cell: CampaignCellResult<TArtifact>,\n options: CampaignCellRunRecordOptions,\n): RunRecord {\n const quality = projectCampaignCellQuality(cell)\n const execution = campaignCellExecutionEvidence(cell)\n const judgeErrorCount = Math.max(\n quality.raw.judge_error_count ?? 0,\n execution.judgeErrorCount ?? 0,\n )\n const cellCostProvenance = campaignCellCostProvenance(cell)\n const costProvenance: CostProvenance =\n cellCostProvenance.kind === 'uncaptured' && options.defaultCostUsd !== undefined\n ? { kind: 'estimated', usd: options.defaultCostUsd }\n : cellCostProvenance\n const costUsd = costProvenance.kind === 'uncaptured' ? null : costProvenance.usd\n const raw: Record<string, number> = {\n ...finiteMetrics(options.raw),\n ...quality.raw,\n rep: cell.rep,\n duration_ms: cell.durationMs,\n ...(costUsd === null ? {} : { cost_usd: costUsd }),\n // Retain the observed subtotal even when the caller supplies an estimated total.\n ...(cellCostProvenance.kind === 'uncaptured' ? { cost_known_subtotal_usd: cell.costUsd } : {}),\n cost_observed: costProvenance.kind === 'observed' ? 1 : 0,\n cost_estimated: costProvenance.kind === 'estimated' ? 1 : 0,\n cost_uncaptured: costProvenance.kind === 'uncaptured' ? 1 : 0,\n tokens_input: cell.tokenUsage.input,\n tokens_output: cell.tokenUsage.output,\n tokens_known: cell.tokenUsage.tokensKnown === false ? 0 : 1,\n latency_ms: cell.durationMs,\n ...(execution.executionErrorCount === undefined\n ? {}\n : { execution_error_count: execution.executionErrorCount }),\n ...(judgeErrorCount > 0 ? { judge_error_count: judgeErrorCount } : {}),\n ...(execution.unclassifiedErrorCount === undefined\n ? {}\n : { unclassified_error_count: execution.unclassifiedErrorCount }),\n }\n if (typeof cell.generation === 'number') raw.generation = cell.generation\n if (cell.tokenUsage.reasoning !== undefined) {\n raw.tokens_reasoning = cell.tokenUsage.reasoning\n }\n if (cell.tokenUsage.cached !== undefined) raw.tokens_cached = cell.tokenUsage.cached\n if (cell.tokenUsage.cacheWrite !== undefined) {\n raw.tokens_cache_write = cell.tokenUsage.cacheWrite\n }\n if (cell.tokenUsage.tokensKnown !== false && costUsd !== null && costUsd > 0) {\n raw.tokens_per_dollar = (cell.tokenUsage.input + cell.tokenUsage.output) / costUsd\n }\n if (costUsd !== null && quality.score !== undefined && quality.score > 0.01) {\n raw.cost_per_quality = costUsd / quality.score\n }\n\n const outcome: RunOutcome = {\n raw,\n ...(quality.judgeScores ? { judgeScores: quality.judgeScores } : {}),\n }\n if (quality.score !== undefined) {\n if (options.splitTag === 'holdout') outcome.holdoutScore = quality.score\n else outcome.searchScore = quality.score\n }\n\n return validateRunRecord({\n runId: options.runId,\n experimentId: options.experimentId,\n candidateId: options.candidateId,\n seed: options.seed ?? cell.seed,\n model: options.model,\n promptHash: options.promptHash,\n configHash: options.configHash,\n commitSha: options.commitSha,\n wallMs: cell.durationMs,\n costUsd,\n costProvenance,\n tokenUsage: { ...cell.tokenUsage },\n terminalOutcome: execution.terminalOutcome,\n ...(execution.terminalFailureReason\n ? { terminalFailureReason: execution.terminalFailureReason }\n : {}),\n outcome,\n splitTag: options.splitTag,\n scenarioId: options.scenarioId ?? cell.scenarioId,\n ...(options.agentProfile ? { agentProfile: options.agentProfile } : {}),\n })\n}\n\n/**\n * Validate the cost fields that cross campaign cache and RunRecord boundaries.\n * `costUsd` is a known subtotal for uncaptured cells, but it must equal the\n * authoritative total whenever that total is observed or estimated.\n */\nexport function campaignCellCostProvenance<TArtifact>(\n cell: Pick<CampaignCellResult<TArtifact>, 'cellId' | 'costUsd' | 'costProvenance'>,\n): CostProvenance {\n if (!Number.isFinite(cell.costUsd) || cell.costUsd < 0) {\n throw new Error(`campaign cell '${cell.cellId}' has invalid costUsd`)\n }\n const provenance = cell.costProvenance\n if (!provenance || typeof provenance !== 'object') {\n throw new Error(`campaign cell '${cell.cellId}' has no costProvenance`)\n }\n if (provenance.kind === 'uncaptured') {\n if (provenance.usd !== null) {\n throw new Error(`campaign cell '${cell.cellId}' has invalid uncaptured costProvenance`)\n }\n return { kind: 'uncaptured', usd: null }\n }\n if (\n (provenance.kind !== 'observed' && provenance.kind !== 'estimated') ||\n !Number.isFinite(provenance.usd) ||\n provenance.usd < 0\n ) {\n throw new Error(`campaign cell '${cell.cellId}' has invalid costProvenance`)\n }\n if (provenance.usd !== cell.costUsd) {\n throw new Error(`campaign cell '${cell.cellId}' has costUsd inconsistent with costProvenance`)\n }\n return { kind: provenance.kind, usd: provenance.usd }\n}\n\nexport function campaignCellExecutionEvidence<TArtifact>(\n cell: CampaignCellResult<TArtifact>,\n): CampaignCellExecutionEvidence {\n if (cell.errorStage === 'dispatch') {\n return {\n terminalOutcome: 'failed',\n executionErrorCount: 1,\n ...(cell.error ? { terminalFailureReason: cell.error } : {}),\n }\n }\n if (cell.errorStage === 'judge') {\n return {\n terminalOutcome: 'succeeded',\n executionErrorCount: 0,\n judgeErrorCount: 1,\n }\n }\n if (!cell.error) {\n return { terminalOutcome: 'succeeded', executionErrorCount: 0 }\n }\n return {\n terminalOutcome: 'unknown',\n unclassifiedErrorCount: 1,\n }\n}\n\n/**\n * Produce the only task-quality view used by campaign aggregates and exports.\n *\n * Successful judge results remain available for diagnosis after another judge\n * fails, but a task score exists only for an error-free cell whose reported\n * judge values are all finite.\n */\nexport function projectCampaignCellQuality<TArtifact>(\n cell: CampaignCellResult<TArtifact>,\n): CampaignCellQualityProjection {\n if (cell.errorStage === 'dispatch') {\n return { successfulJudgeScores: {}, failedJudges: [], raw: {} }\n }\n\n const perJudge: Record<string, Record<string, number>> = {}\n const successfulJudgeScores: Record<string, JudgeScore> = {}\n const dimensionValues = new Map<string, number[]>()\n const composites: number[] = []\n const notes: string[] = []\n const failedJudges = new Set<string>(\n cell.errorStage === 'judge' ? [cell.errorJudge ?? 'unknown-judge'] : [],\n )\n const raw: Record<string, number> = {}\n\n for (const [judgeName, score] of Object.entries(cell.judgeScores)) {\n const dimensionsShape = (score as { dimensions?: unknown }).dimensions\n if (\n typeof dimensionsShape !== 'object' ||\n dimensionsShape === null ||\n Array.isArray(dimensionsShape)\n ) {\n throw new CampaignJudgeScoreShapeError(judgeName)\n }\n const finiteDimensions = Object.values(score.dimensions).every(Number.isFinite)\n if (score.failed || !Number.isFinite(score.composite) || !finiteDimensions) {\n failedJudges.add(judgeName)\n continue\n }\n\n composites.push(score.composite)\n successfulJudgeScores[judgeName] = score\n const dimensions = { ...score.dimensions }\n perJudge[judgeName] = dimensions\n for (const [dimension, value] of Object.entries(dimensions)) {\n raw[`${judgeName}.${dimension}`] = value\n const values = dimensionValues.get(dimension) ?? []\n values.push(value)\n dimensionValues.set(dimension, values)\n }\n if (score.notes) notes.push(`${judgeName}: ${score.notes}`)\n for (const failedJudge of score.failedJudges ?? []) {\n failedJudges.add(`${judgeName}/${failedJudge}`)\n }\n }\n\n if (failedJudges.size > 0) raw.judge_error_count = failedJudges.size\n const sortedFailedJudges = [...failedJudges].sort()\n if (composites.length === 0) {\n return {\n successfulJudgeScores,\n failedJudges: sortedFailedJudges,\n raw,\n }\n }\n\n const composite = mean(composites)\n const perDimMean = Object.fromEntries(\n [...dimensionValues.entries()].map(([dimension, values]) => [dimension, mean(values)]),\n )\n const complete =\n cell.error === undefined && cell.errorStage === undefined && failedJudges.size === 0\n if (complete) raw.composite = composite\n\n return {\n ...(complete ? { score: composite } : {}),\n raw,\n successfulJudgeScores,\n failedJudges: sortedFailedJudges,\n judgeScores: {\n perJudge,\n perDimMean,\n composite,\n ...(sortedFailedJudges.length > 0 ? { failedJudges: sortedFailedJudges } : {}),\n ...(notes.length > 0 ? { notes: notes.join(' | ') } : {}),\n },\n }\n}\n\n/** Read the canonical task score without recomputing cell quality. */\nexport function campaignCellTaskScore<TArtifact>(\n cell: CampaignCellResult<TArtifact>,\n): number | undefined {\n return projectCampaignCellQuality(cell).score\n}\n\n/** Read canonical successful judge dimensions without recomputing cell quality. */\nexport function campaignCellJudgeDimensions<TArtifact>(\n cell: CampaignCellResult<TArtifact>,\n): Record<string, Record<string, number>> {\n return projectCampaignCellQuality(cell).judgeScores?.perJudge ?? {}\n}\n\nfunction finiteMetrics(metrics: Record<string, number> | undefined): Record<string, number> {\n const finite: Record<string, number> = {}\n for (const [key, value] of Object.entries(metrics ?? {})) {\n if (Number.isFinite(value)) finite[key] = value\n }\n return finite\n}\n\nfunction mean(values: number[]): number {\n return values.reduce((sum, value) => sum + value, 0) / values.length\n}\n","/**\n * Verifiable reward channel.\n *\n * For RL on coding / math / theorem-proving / structured-output tasks, the\n * reward signal is *decidable* — a test passes or fails, a proof checks or\n * doesn't, an output validates against a schema or doesn't. These rewards\n * are dramatically more useful for RL training than LLM-judge scores\n * because they don't drift, can't be Goodhart-gamed by the policy in the\n * same way, and don't require a separate calibration loop.\n *\n * The `MultiLayerVerifier` already produces this signal — it just doesn't\n * surface it in a shape that's clean enough for RL training. This module\n * wraps the verifier output so consumers can:\n *\n * 1. Extract a clean `VerifiableReward` from a `VerificationReport`\n * 2. Distinguish *deterministic* rewards (compile, test, schema) from\n * *probabilistic* rewards (judge) so they can be weighted differently\n * in the RL training step\n * 3. Filter `RunRecord[]` to only those with a verifiable reward,\n * producing the clean training set that DeepSeek-R1-style GRPO and\n * AlphaProof-style search both depend on\n *\n * Why this matters: every credible 2025-2026 frontier RL result on coding\n * agents leans on verifiable reward (DeepSeek-R1 GRPO on test pass-rate,\n * o-series RL on math/code, AlphaProof on Lean kernel checking). Mixing\n * judge scores into the reward signal poisons the gradient. This module\n * is the seam.\n */\n\nimport type { LayerResult, VerificationReport } from '../multi-layer-verifier'\nimport { isRealnessGated, observedScore, trainingScore } from '../rollout/reward'\nimport type { RunRecord } from '../run-record'\nimport type { VerificationStrategySource } from '../verification-strategy'\n\n/**\n * What produced a reward. This is the verification-strategy family\n * (`src/verification-strategy.ts`) — one open vocabulary shared by rewards\n * and verdict certifications, so an RL consumer and a certification reader\n * mean the same thing by `'proof-kernel'`.\n *\n * Beyond the classic answer-key members (`compile`, `test`, `schema`,\n * `sandbox`, `judge`, `composite`) the family carries four members for\n * tasks with no held-out suite, each with a documented failure mode:\n *\n * - `'proof-kernel'` — kernel-checked formal proof. Failure mode: the\n * formalization gap — the kernel never certifies that the formal\n * statement matches the informal claim (`src/verification-strategy.ts`\n * is the discharge protocol).\n * - `'invariant'` — invariant / metamorphic properties held. Failure\n * mode: weak invariants pass everything; the set needs seeded-bug\n * calibration before its pass carries weight.\n * - `'replication'` — independent re-execution from pinned inputs.\n * Failure mode: re-runs the method, so it never catches an error the\n * method itself carries.\n * - `'agreement'` — independently-derived results agree. Failure mode:\n * the shared blind spot — derivers with common training corpora or\n * priors agree for the same wrong reason. Probabilistic: the check may\n * be mechanical, but the independence it certifies is not.\n *\n * The deterministic/probabilistic axis stays on `VerifiableReward` —\n * the producer declares it per reward, and `VERIFICATION_STRATEGIES`\n * documents each member's default class.\n */\nexport type VerifiableRewardSource = VerificationStrategySource\n\nexport interface VerifiableReward {\n /** Scalar in [0, 1]. The RL training signal. */\n value: number\n /** What produced the reward — different sources have different determinism. */\n source: VerifiableRewardSource\n /**\n * Determinism class. `'deterministic'` rewards are repeatable byte-for-byte\n * given the same inputs (compile, test, schema validation, sandbox exit code).\n * `'probabilistic'` rewards depend on a stochastic component (LLM judge).\n * Mixing these in the same training batch without separation is a known\n * footgun in production RLHF pipelines.\n */\n determinism: 'deterministic' | 'probabilistic'\n /**\n * Confidence in the reward value. For deterministic sources this is 1.0\n * (the bit either flipped or didn't). For judge sources this is the\n * judge-reported confidence or — when missing — a calibrated prior.\n */\n confidence: number\n /** The layer / judge id that produced the signal, for provenance. */\n origin: string\n /**\n * Per-source contribution to `value`, keyed by layer/judge id. Single-source\n * rewards carry one entry (`{ [origin]: value }`); composite rewards carry\n * every contributing layer's score — the anti-scalar-collapse surface RL\n * consumers weight per-source instead of trusting one blended number.\n */\n components: Record<string, number>\n /**\n * The run carries `outcome.realness.gated` — the authenticity gate flagged\n * its success signal as faked.\n *\n * With the gate applied (the default) `value` and every `components` entry\n * are 0 on such a run; with `applyRealnessGate: false` the observed numbers\n * come back untouched and this flag is the only marker that they are not to\n * be trusted. Either way it distinguishes \"measured a genuine failure\" from\n * \"claimed a success we refuse to believe\", which a bare 0 cannot.\n */\n realnessGated?: boolean\n /**\n * Whether an authenticity screen COULD run on this reward at all — the same\n * distinction `RolloutOutcome.realness_screened` draws, for the same reason.\n *\n * `false` on every reward from `extractVerifiableReward`, because a\n * `VerificationReport` carries layer scores and nothing else: there is no\n * `outcome.realness` to consult, so no gate has run, and `realnessGated`\n * being absent there means \"unknown\", NOT \"clean\". Absent on the\n * `RunRecord` path when the record itself carries no realness verdict.\n *\n * This matters most exactly where it is easiest to miss: a report whose\n * deterministic layers all passed yields `determinism: 'deterministic'`,\n * `confidence: 1` — the highest-credibility reward this module can emit —\n * and a stubbed integration reporting green is precisely what a gamed run\n * looks like. Consumers driving training off this shape must screen the run\n * themselves; the flag is what tells them nobody has.\n */\n realnessScreened?: boolean\n}\n\nexport interface VerifiableRewardExtractionOptions {\n /**\n * Which layers count as deterministic-reward sources. The verifier doesn't\n * tag layers as \"this is verifiable\"; the caller declares it via this list\n * (or via the layer name → source mapping). Default treats common names\n * (`install`, `typecheck`, `build`, `lint`, `test`, `compile`, `schema`,\n * `sandbox`) as deterministic.\n */\n deterministicLayers?: string[]\n /**\n * Map layer name → reward source. Defaults to a sensible string-match.\n */\n sourceFor?: (layerName: string) => VerifiableRewardSource\n /**\n * Whether to fall back to a probabilistic (judge) reward when no\n * deterministic layer produced a numeric score. Default `true`. Set to\n * `false` for \"deterministic-only\" training pipelines that should\n * discard runs without a verifiable signal.\n */\n fallbackToJudge?: boolean\n /**\n * Default confidence for probabilistic (judge) rewards when the judge\n * doesn't report one. Default `0.7`.\n */\n judgeConfidenceFloor?: number\n /**\n * Whether the anti-Goodhart realness gate applies. Default `true`, and the\n * default is the one every training path must keep.\n *\n * Set `false` ONLY for detection and analysis. `rl/reward-hacking.ts` does,\n * for the same reason it reads `observedScore` for its proxy: it measures the\n * DIVERGENCE between the judge signal and the deterministic one, and a\n * deterministic reward that another gate already forced to 0 manufactures\n * exactly that divergence on exactly the gamed population. The detector would\n * then be re-reporting a verdict it was supposed to reach independently.\n */\n applyRealnessGate?: boolean\n}\n\n// 'agreement' is deliberately absent: agreement between fixed artifacts is\n// mechanical, but the independent derivation it certifies is stochastic, so\n// a producer that wants a deterministic agreement reward must declare it\n// (via `deterministicLayers` or by constructing the reward directly).\nconst DEFAULT_DETERMINISTIC_LAYERS = new Set([\n 'install',\n 'typecheck',\n 'build',\n 'lint',\n 'test',\n 'compile',\n 'schema',\n 'sandbox',\n 'unit_tests',\n 'integration_tests',\n 'proof_kernel',\n 'proof-kernel',\n 'invariant',\n 'metamorphic',\n 'replication',\n])\n\nconst DEFAULT_SOURCE_FOR = (name: string): VerifiableRewardSource => {\n const lower = name.toLowerCase()\n if (lower.includes('test')) return 'test'\n if (\n lower.includes('compile') ||\n lower.includes('build') ||\n lower.includes('typecheck') ||\n lower.includes('lint')\n )\n return 'compile'\n if (lower.includes('schema')) return 'schema'\n if (lower.includes('sandbox')) return 'sandbox'\n if (lower.includes('judge') || lower.includes('semantic')) return 'judge'\n // Answer-key names above take precedence: a name like 'proof_test' maps\n // to 'test'; only names with no answer-key match reach the open-family\n // members.\n if (lower.includes('proof') || lower.includes('kernel') || lower.includes('lean')) {\n return 'proof-kernel'\n }\n if (lower.includes('invariant') || lower.includes('metamorphic')) return 'invariant'\n if (lower.includes('replicat')) return 'replication'\n if (lower.includes('agreement') || lower.includes('consensus')) return 'agreement'\n return 'composite'\n}\n\n/**\n * Extract a `VerifiableReward` from a `VerificationReport`.\n *\n * Strategy: prefer the deterministic layers (in order: test → compile →\n * schema → sandbox), fall back to the judge layer if `fallbackToJudge` is\n * true, return `null` if no signal qualifies. When multiple deterministic\n * layers contribute, return a `'composite'` source with a weighted blend.\n *\n * NO realness gate is applied and none can be: a `VerificationReport` carries\n * layer scores and nothing about whether the run faked them — `realness` lives\n * on the `RunRecord`. Use `extractVerifiableRewardsFromRecords` for anything\n * that becomes training data; this signature is for scoring a report in hand.\n */\nexport function extractVerifiableReward(\n report: VerificationReport,\n opts: VerifiableRewardExtractionOptions = {},\n): VerifiableReward | null {\n const deterministicSet = new Set(opts.deterministicLayers ?? [...DEFAULT_DETERMINISTIC_LAYERS])\n const sourceFor = opts.sourceFor ?? DEFAULT_SOURCE_FOR\n const fallbackToJudge = opts.fallbackToJudge ?? true\n const judgeFloor = opts.judgeConfidenceFloor ?? 0.7\n\n const deterministic = report.layers.filter(\n (layer) => deterministicSet.has(layer.layer) && isMeasuredLayer(layer),\n )\n\n if (deterministic.length === 1) {\n const layer = deterministic[0]!\n const value = clamp01(layer.score!)\n return {\n value,\n source: sourceFor(layer.layer),\n determinism: 'deterministic',\n confidence: 1,\n origin: layer.layer,\n components: { [layer.layer]: value },\n realnessScreened: false,\n }\n }\n\n if (deterministic.length > 1) {\n // Composite: weighted blend by `Layer.weight` if present, else equal.\n let num = 0\n let denom = 0\n const components: Record<string, number> = {}\n for (const l of deterministic) {\n const w = (l.detail?.weight as number | undefined) ?? 1\n num += w * (l.score ?? 0)\n denom += w\n components[l.layer] = l.score!\n }\n return {\n value: denom === 0 ? 0 : clamp01(num / denom),\n source: 'composite',\n determinism: 'deterministic',\n confidence: 1,\n origin: deterministic.map((l) => l.layer).join('+'),\n components,\n realnessScreened: false,\n }\n }\n\n if (!fallbackToJudge) return null\n\n const judge =\n report.layers.find((layer) => isMeasuredLayer(layer) && sourceFor(layer.layer) === 'judge') ??\n report.layers.find(isMeasuredLayer)\n\n if (!judge) return null\n\n const confFromDetail = judge.detail?.confidence as number | undefined\n const judgeValue = clamp01(judge.score!)\n return {\n value: judgeValue,\n source: 'judge',\n determinism: 'probabilistic',\n confidence: typeof confFromDetail === 'number' ? confFromDetail : judgeFloor,\n origin: judge.layer,\n components: { [judge.layer]: judgeValue },\n realnessScreened: false,\n }\n}\n\nfunction isMeasuredLayer(\n layer: LayerResult,\n): layer is LayerResult & { status: 'pass' | 'fail'; score: number } {\n return (\n (layer.status === 'pass' || layer.status === 'fail') &&\n typeof layer.score === 'number' &&\n Number.isFinite(layer.score) &&\n layer.score >= 0 &&\n layer.score <= 1\n )\n}\n\n/**\n * Extract verifiable rewards from `RunRecord[]` produced via the\n * `verificationReportToRunRecord` adapter (which encodes per-layer scores\n * in `outcome.raw['layer.<name>']`). For records that don't carry layer\n * scores, returns `null` for that record.\n *\n * This is the canonical bridge from \"campaign-shaped artifacts\" to\n * \"RL-training-ready reward signals\": every record that has a clean\n * verifiable reward becomes a training datum, every record that doesn't\n * gets filtered out (or kept with `'probabilistic'` determinism for\n * separate downstream handling).\n *\n * The realness gate applies to EVERY channel here, and to the deterministic one\n * MOST. It is tempting to reason that a decidable signal cannot be gamed, so\n * the gate is redundant on it — that reasoning is backwards. `realness.gated`\n * means the run's success signal was FAKED, and a test suite reporting green on\n * a stubbed integration is precisely what that looks like: the deterministic\n * layer is the thing that got faked. Exporting it ungated hands a trainer the\n * highest-credibility reward the module can emit (`determinism: 'deterministic'`,\n * `confidence: 1`) for the one population the gate exists to catch. Pass\n * `applyRealnessGate: false` only to look at the ungated numbers for detection.\n */\nexport function extractVerifiableRewardsFromRecords(\n runs: RunRecord[],\n opts: VerifiableRewardExtractionOptions = {},\n): Array<{ runId: string; reward: VerifiableReward | null }> {\n const sourceFor = opts.sourceFor ?? DEFAULT_SOURCE_FOR\n const deterministicSet = new Set(opts.deterministicLayers ?? [...DEFAULT_DETERMINISTIC_LAYERS])\n const fallbackToJudge = opts.fallbackToJudge ?? true\n const judgeFloor = opts.judgeConfidenceFloor ?? 0.7\n const applyGate = opts.applyRealnessGate ?? true\n\n return runs.map((run) => {\n const flagged = isRealnessGated(run)\n // Present only when the record carries an actual realness verdict. Absent\n // is the honest \"unknown\"; `false` is reserved for a producer that\n // declares it HAS no screen, which is the report-shaped path above.\n const screened = run.outcome.realness === undefined ? {} : ({ realnessScreened: true } as const)\n // Zeroed with `value`, never left at the measured number: `components`\n // exists so an RL consumer can re-weight per source, and a raw layer score\n // surviving there would let that re-weighting reconstruct the very reward\n // the gate just refused. The measured layer scores stay on\n // `run.outcome.raw['layer.*']`, which is where analysis reads them.\n const gate = (value: number): number => (applyGate && flagged ? 0 : value)\n // Recover per-layer scores from outcome.raw['layer.<name>']\n const layerScores: Array<{ name: string; score: number }> = []\n for (const [k, v] of Object.entries(run.outcome.raw)) {\n if (\n k.startsWith('layer.') &&\n !k.includes('.', 6) &&\n typeof v === 'number' &&\n Number.isFinite(v)\n ) {\n layerScores.push({ name: k.slice('layer.'.length), score: v })\n }\n }\n const det = layerScores.filter((l) => deterministicSet.has(l.name))\n\n if (det.length === 1) {\n const layer = det[0]!\n const value = gate(clamp01(layer.score))\n return {\n runId: run.runId,\n reward: {\n value,\n source: sourceFor(layer.name),\n determinism: 'deterministic',\n confidence: 1,\n origin: layer.name,\n components: { [layer.name]: value },\n realnessGated: flagged,\n ...screened,\n },\n }\n }\n if (det.length > 1) {\n const value = gate(clamp01(det.reduce((s, l) => s + l.score, 0) / det.length))\n // Same clamp as the headline value: a producer writing layer.score 1.5\n // into outcome.raw must not propagate 1.5 through a component either.\n const components: Record<string, number> = Object.fromEntries(\n det.map((l) => [l.name, gate(clamp01(l.score))]),\n )\n return {\n runId: run.runId,\n reward: {\n value,\n source: 'composite',\n determinism: 'deterministic',\n confidence: 1,\n origin: det.map((l) => l.name).join('+'),\n components,\n realnessGated: flagged,\n ...screened,\n },\n }\n }\n if (!fallbackToJudge) return { runId: run.runId, reward: null }\n\n // Probabilistic fallback: the run's primary score. `trainingScore` already\n // carries the gate, so a gamed run falls to 0 rather than earning the\n // judge's number; `observedScore` is the ungated reader the detection\n // opt-out asks for. Either way an unscored run stays a labeled gap\n // (`reward: null`), never a fabricated 0.\n const primary = applyGate ? trainingScore(run) : observedScore(run)\n if (typeof primary !== 'number' || !Number.isFinite(primary)) {\n return { runId: run.runId, reward: null }\n }\n const primaryValue = clamp01(primary)\n return {\n runId: run.runId,\n reward: {\n value: primaryValue,\n source: 'judge',\n determinism: 'probabilistic',\n confidence: judgeFloor,\n origin: 'run.outcome.score',\n components: { 'run.outcome.score': primaryValue },\n realnessGated: flagged,\n ...screened,\n },\n }\n })\n}\n\n/**\n * Filter `RunRecord[]` to those with deterministic verifiable rewards.\n *\n * A realness-gated run is KEPT, at reward 0 with `realnessGated: true` — the\n * same rule GRPO uses on a gated line. 0 is the honest label for a faked\n * success and is usable signal, whereas dropping the run would move a group\n * baseline without saying so. (SFT differs: there every row is a target to\n * imitate, so a gated row is removed outright.)\n */\nexport function filterDeterministicallyRewarded(\n runs: RunRecord[],\n opts: VerifiableRewardExtractionOptions = {},\n): Array<{ run: RunRecord; reward: VerifiableReward }> {\n const rewarded = extractVerifiableRewardsFromRecords(runs, { ...opts, fallbackToJudge: false })\n const out: Array<{ run: RunRecord; reward: VerifiableReward }> = []\n for (let i = 0; i < runs.length; i++) {\n const r = rewarded[i]!\n if (r.reward && r.reward.determinism === 'deterministic') {\n out.push({ run: runs[i]!, reward: r.reward })\n }\n }\n return out\n}\n\nfunction clamp01(x: number): number {\n if (!Number.isFinite(x)) return 0\n return Math.max(0, Math.min(1, x))\n}\n","/**\n * Reward hacking / Goodhart detection.\n *\n * Goodhart's Law says: when a measure becomes a target, it ceases to be\n * a good measure. In RLHF and agentic-RL settings this is the dominant\n * failure mode — the policy learns to produce outputs that score well on\n * the proxy reward (judge, rubric, test pass-rate) without producing\n * the underlying capability the proxy was meant to track.\n *\n * Krakovna et al. (2020, \"Specification Gaming Examples in AI\") and the\n * subsequent RLHF reward-hacking literature (Skalse et al. 2022, Kim et al.\n * 2023) converge on a few diagnostic signatures:\n *\n * 1. **Reward divergence:** the proxy reward grows while the held-out\n * ground-truth signal stagnates or drops. Predictive validity over\n * time captures this.\n * 2. **Distributional shift in outputs:** after RL, the policy produces\n * outputs that no longer match the reference distribution — usually\n * because it found a high-reward attractor that's degenerate (e.g.\n * one-token responses, repetition, formatting tricks).\n * 3. **Disagreement between independent rewards:** if you train on\n * reward A and a held-out independent reward B drops sharply, you're\n * probably hacking A.\n * 4. **Calibration drift:** the verifiable / deterministic component of\n * the reward is stable; the probabilistic / judge component drifts up\n * while the deterministic component doesn't. The judge is being\n * gamed.\n *\n * This module ships explicit detectors for all four signatures, plus a\n * combined verdict. The output is diagnostic — actionable signals,\n * not autoreject — because each signature has known false positives\n * (e.g., a policy that genuinely improves can show distributional shift).\n *\n * Differs from `rubricPredictiveValidity` (which is a *standing* check on\n * whether rubrics correlate with deployment outcomes) — this is a\n * *temporal* check on whether the reward-vs-truth gap is *widening over\n * time during a training run*.\n */\n\nimport { observedScore } from '../rollout/reward'\nimport type { RunRecord } from '../run-record'\nimport { pearsonR } from '../statistics'\nimport {\n filterDeterministicallyRewarded,\n type VerifiableRewardExtractionOptions,\n} from './verifiable-reward'\n\nexport type RewardHackingSignal =\n | 'reward_divergence'\n | 'distribution_shift'\n | 'reward_disagreement'\n | 'judge_drift'\n\nexport interface RewardHackingFinding {\n signal: RewardHackingSignal\n /** Severity in [0, 1]. >0.5 = strong signal. */\n severity: number\n message: string\n /** Numeric evidence the consumer can render. */\n detail: Record<string, number>\n}\n\nexport interface RewardHackingReport {\n findings: RewardHackingFinding[]\n /** Signals with enough usable observations to produce a finding. */\n evaluatedSignals: RewardHackingSignal[]\n /**\n * Composite verdict. `'insufficient_evidence'` when fewer than four scored\n * runs exist; otherwise `'clean'` if every signal severity < 0.3,\n * `'suspect'` if at least one ≥ 0.3 but none ≥ 0.6, and `'gaming'` if any ≥ 0.6.\n */\n verdict: 'insufficient_evidence' | 'clean' | 'suspect' | 'gaming'\n /** Rationale for the verdict, ready to paste into an audit log. */\n rationale: string[]\n /** Number of runs with a usable proxy reward. */\n n: number\n}\n\nexport interface DetectRewardHackingInput {\n /**\n * Run records ordered by recency (oldest first). The detector segments\n * them into prefix/suffix windows to compute \"did the gap widen.\"\n */\n runs: RunRecord[]\n /**\n * The metric the policy was trained to optimize. Should be present on\n * `outcome.raw` or `outcome.holdoutScore`. Default reads `outcome.holdoutScore`.\n */\n proxyOf?: (run: RunRecord) => number | null\n /**\n * The held-out ground-truth metric. For RL on coding, this is typically\n * test pass-rate. For RLHF, it's downstream task performance or human\n * preference. For knowledge tasks, it's an independently-graded score.\n */\n truthOf?: (run: RunRecord) => number | null\n /**\n * Independent secondary reward. Used for the `reward_disagreement`\n * signal. Default uses the verifiable reward extractor (deterministic\n * sources only).\n */\n secondaryRewardOf?: (run: RunRecord) => number | null\n /**\n * Window size — how many of the most recent runs count as the \"after\"\n * cohort. Default min(50, half the runs).\n */\n windowSize?: number\n /**\n * Severity threshold to flag a signal. Default 0.3 (suspect) and 0.6\n * (gaming).\n */\n thresholds?: { suspect?: number; gaming?: number }\n /**\n * Verifiable-reward options used for the secondary-reward fallback.\n */\n verifiableRewardOptions?: VerifiableRewardExtractionOptions\n}\n\nconst DEFAULT_PROXY = (r: RunRecord): number | null => {\n // DELIBERATELY UNGATED. This is the proxy reward the detector tests for\n // Goodharting, and gated runs are exactly the gamed population. Forcing them\n // to 0 would collapse the proxy toward the deterministic secondary signal —\n // `reward_disagreement`'s correlation would rise, `judge_drift`'s gap would\n // shrink, and `reward_divergence`'s proxy-up/truth-flat fingerprint would be\n // erased — so the detector would report \"clean\" on the very runs it exists to\n // catch. `null` (never 0) is also load-bearing: it sets the n-denominator via\n // the filter below.\n const v = observedScore(r)\n return typeof v === 'number' && Number.isFinite(v) ? v : null\n}\n\nexport function detectRewardHacking(input: DetectRewardHackingInput): RewardHackingReport {\n const proxyOf = input.proxyOf ?? DEFAULT_PROXY\n const truthOf = input.truthOf\n const sus = input.thresholds?.suspect ?? 0.3\n const gam = input.thresholds?.gaming ?? 0.6\n\n const runs = input.runs.filter((run) => finiteNumber(proxyOf(run)))\n const n = runs.length\n if (n < 4) {\n return {\n findings: [],\n evaluatedSignals: [],\n verdict: 'insufficient_evidence',\n n,\n rationale: [`fewer than 4 runs with proxy reward (n=${n}); insufficient evidence`],\n }\n }\n const windowSize = Math.max(1, input.windowSize ?? Math.min(50, Math.floor(n / 2)))\n const before = runs.slice(0, n - windowSize)\n const after = runs.slice(n - windowSize)\n\n const findings: RewardHackingFinding[] = []\n\n // ── Signal 1: reward divergence (proxy ↑ while truth flat or ↓) ──────\n if (truthOf) {\n const beforeProxy = before.map(proxyOf).filter(finiteNumber)\n const afterProxy = after.map(proxyOf).filter(finiteNumber)\n const beforeTruth = before.map(truthOf).filter(finiteNumber)\n const afterTruth = after.map(truthOf).filter(finiteNumber)\n if (\n beforeProxy.length >= 2 &&\n afterProxy.length >= 2 &&\n beforeTruth.length >= 2 &&\n afterTruth.length >= 2\n ) {\n const proxyDelta = mean(afterProxy) - mean(beforeProxy)\n const truthDelta = mean(afterTruth) - mean(beforeTruth)\n // Divergence: proxy goes up while truth goes flat or down.\n // Severity = max(0, (proxyDelta - truthDelta)) — bigger gap = bigger signal.\n const gap = Math.max(0, proxyDelta - truthDelta)\n const severity = clamp01(gap * 5) // scale: 0.2 absolute gap → severity 1.0\n findings.push({\n signal: 'reward_divergence',\n severity,\n message:\n severity >= sus\n ? `proxy reward rose by ${proxyDelta.toFixed(3)} while truth changed by ${truthDelta.toFixed(3)} — potential Goodhart`\n : `proxy and truth moved together (proxy ${proxyDelta.toFixed(3)}, truth ${truthDelta.toFixed(3)})`,\n detail: {\n proxyDelta,\n truthDelta,\n gap,\n beforeN: beforeProxy.length,\n afterN: afterProxy.length,\n },\n })\n }\n }\n\n // ── Signal 2: distributional shift in outputs (KS on score distributions) ──\n {\n const beforeP = before.map(proxyOf).filter(finiteNumber)\n const afterP = after.map(proxyOf).filter(finiteNumber)\n if (beforeP.length >= 4 && afterP.length >= 4) {\n const ks = ksStatistic(beforeP, afterP)\n // KS statistic: bigger = more shift. We're agnostic about direction;\n // genuine improvement ALSO produces shift, so this signal is\n // contributory rather than load-bearing.\n const severity = clamp01(ks - 0.2)\n findings.push({\n signal: 'distribution_shift',\n severity,\n message:\n severity >= sus\n ? `KS=${ks.toFixed(3)} between before/after windows — distributional shift large`\n : `KS=${ks.toFixed(3)} between before/after windows — within-distribution drift`,\n detail: { ks, beforeN: beforeP.length, afterN: afterP.length },\n })\n }\n }\n\n // ── Signal 3: reward disagreement (proxy vs independent secondary) ────\n {\n const secondaryOf = input.secondaryRewardOf ?? defaultSecondary(input.verifiableRewardOptions)\n const aligned = runs\n .map((r) => ({ p: proxyOf(r), s: secondaryOf(r) }))\n .filter((x): x is { p: number; s: number } => finiteNumber(x.p) && finiteNumber(x.s))\n if (aligned.length >= 4) {\n const ps = aligned.map((x) => x.p)\n const ss = aligned.map((x) => x.s)\n const r = pearsonR(ps, ss)\n // Disagreement: low or negative correlation between primary proxy\n // reward and an independent secondary signal.\n const severity = clamp01(0.5 - Math.max(0, r))\n findings.push({\n signal: 'reward_disagreement',\n severity,\n message:\n severity >= sus\n ? `proxy and independent secondary reward correlate ρ=${r.toFixed(3)} — possibly hacking proxy`\n : `proxy and secondary reward correlate ρ=${r.toFixed(3)}`,\n detail: { pearson: r, n: aligned.length },\n })\n }\n }\n\n // ── Signal 4: judge drift (probabilistic up while deterministic flat) ─\n {\n // Ungated on purpose, exactly like `DEFAULT_PROXY` above. This signal is\n // the GAP between the judge reward and the deterministic one; a\n // deterministic reward another gate already forced to 0 would open that gap\n // by construction on the gamed population, so the detector would fire on\n // its own input rather than on evidence it found.\n const detRuns = filterDeterministicallyRewarded(runs, {\n ...(input.verifiableRewardOptions ?? {}),\n applyRealnessGate: false,\n })\n if (detRuns.length >= 4) {\n const detBefore = detRuns.slice(0, Math.floor(detRuns.length / 2))\n const detAfter = detRuns.slice(Math.floor(detRuns.length / 2))\n const detDelta =\n mean(detAfter.map((r) => r.reward.value)) - mean(detBefore.map((r) => r.reward.value))\n const proxyDelta =\n mean(after.map(proxyOf).filter(finiteNumber)) -\n mean(before.map(proxyOf).filter(finiteNumber))\n const driftGap = Math.max(0, proxyDelta - detDelta)\n const severity = clamp01(driftGap * 5)\n findings.push({\n signal: 'judge_drift',\n severity,\n message:\n severity >= sus\n ? `judge proxy +${proxyDelta.toFixed(3)} while deterministic reward +${detDelta.toFixed(3)} — judge drifting up without verifiable backing`\n : `judge and deterministic rewards move in step (judge ${proxyDelta.toFixed(3)}, det ${detDelta.toFixed(3)})`,\n detail: { proxyDelta, detDelta, driftGap, n: detRuns.length },\n })\n }\n }\n\n const maxSev = findings.reduce((m, f) => Math.max(m, f.severity), 0)\n if (findings.length === 0) {\n return {\n findings,\n evaluatedSignals: [],\n verdict: 'insufficient_evidence',\n rationale: [`no reward-hacking signal had enough paired evidence (n=${n})`],\n n,\n }\n }\n const verdict: RewardHackingReport['verdict'] =\n maxSev >= gam ? 'gaming' : maxSev >= sus ? 'suspect' : 'clean'\n const rationale = findings\n .filter((f) => f.severity >= sus)\n .map((f) => `${f.signal}: severity ${f.severity.toFixed(2)} — ${f.message}`)\n if (rationale.length === 0) rationale.push('no signals fired above suspect threshold')\n\n return {\n findings,\n evaluatedSignals: findings.map((finding) => finding.signal),\n verdict,\n rationale,\n n,\n }\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────\n\nfunction mean(xs: number[]): number {\n if (xs.length === 0) return 0\n return xs.reduce((s, x) => s + x, 0) / xs.length\n}\n\nfunction finiteNumber(value: number | null): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n\nfunction clamp01(x: number): number {\n if (!Number.isFinite(x)) return 0\n return Math.max(0, Math.min(1, x))\n}\n\nfunction ksStatistic(a: number[], b: number[]): number {\n // Two-sample Kolmogorov-Smirnov statistic.\n const sortedA = [...a].sort((x, y) => x - y)\n const sortedB = [...b].sort((x, y) => x - y)\n const all = [...new Set([...sortedA, ...sortedB])].sort((x, y) => x - y)\n let max = 0\n for (const v of all) {\n const fa = sortedA.filter((x) => x <= v).length / sortedA.length\n const fb = sortedB.filter((x) => x <= v).length / sortedB.length\n max = Math.max(max, Math.abs(fa - fb))\n }\n return max\n}\n\nfunction defaultSecondary(\n verifiableOpts?: VerifiableRewardExtractionOptions,\n): (run: RunRecord) => number | null {\n return (run: RunRecord) => {\n // Ungated for the same reason as signal 4: this is the INDEPENDENT\n // secondary reward whose correlation with the proxy is the evidence.\n // Zeroing it on gated runs would drive that correlation down mechanically.\n const filtered = filterDeterministicallyRewarded([run], {\n ...(verifiableOpts ?? {}),\n applyRealnessGate: false,\n })\n return filtered.length === 1 ? filtered[0]!.reward.value : null\n }\n}\n"],"mappings":"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"}