@tangle-network/agent-eval 0.222.1 → 0.223.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.
- package/CHANGELOG.md +91 -0
- package/dist/adapters/http.d.ts +2 -2
- package/dist/{agent-profile-CSEQRC9l.d.ts → agent-profile-CoD1Dotf.d.ts} +2 -2
- package/dist/{agent-profile-CSEQRC9l.d.ts.map → agent-profile-CoD1Dotf.d.ts.map} +1 -1
- package/dist/analyst/index.d.ts +11 -11
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +1 -1
- package/dist/{analyze-runs-CGLRgZGd.d.ts → analyze-runs-C-pP1sK9.d.ts} +2 -2
- package/dist/{analyze-runs-CGLRgZGd.d.ts.map → analyze-runs-C-pP1sK9.d.ts.map} +1 -1
- package/dist/{analyze-runs-CTheASYV.js → analyze-runs-Ck68mbgj.js} +7 -9
- package/dist/analyze-runs-Ck68mbgj.js.map +1 -0
- package/dist/{benchmark-command-Cr7i7YSX.js → benchmark-command-CH86twZj.js} +31 -36
- package/dist/benchmark-command-CH86twZj.js.map +1 -0
- package/dist/benchmarks/index.d.ts +4 -4
- package/dist/benchmarks/index.js +3 -3
- package/dist/campaign/index.d.ts +6 -6
- package/dist/campaign/index.js +9 -9
- package/dist/{campaign-ddr1N3yD.js → campaign-DdOgQfp8.js} +14 -19
- package/dist/campaign-DdOgQfp8.js.map +1 -0
- package/dist/{campaign-evidence-DN8rWuBj.js → campaign-evidence-MjCjkb5a.js} +3 -3
- package/dist/{campaign-evidence-DN8rWuBj.js.map → campaign-evidence-MjCjkb5a.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/contract/index.d.ts +9 -9
- package/dist/contract/index.js +11 -10
- package/dist/contract/index.js.map +1 -1
- package/dist/{default-registry-1f4ukAIX.d.ts → default-registry-B1DstPYg.d.ts} +7 -7
- package/dist/{default-registry-1f4ukAIX.d.ts.map → default-registry-B1DstPYg.d.ts.map} +1 -1
- package/dist/{define-agent-eval-sUYh9BFA.d.ts → define-agent-eval-CAffiFbN.d.ts} +7 -7
- package/dist/{define-agent-eval-sUYh9BFA.d.ts.map → define-agent-eval-CAffiFbN.d.ts.map} +1 -1
- package/dist/{define-agent-eval-BLoP4Lf9.js → define-agent-eval-Cc15qGik.js} +5 -5
- package/dist/{define-agent-eval-BLoP4Lf9.js.map → define-agent-eval-Cc15qGik.js.map} +1 -1
- package/dist/diagnosis/index.d.ts +2 -2
- package/dist/diagnosis/index.js +1 -1
- package/dist/{engine-BITiErg-.d.ts → engine-CaHaCc3W.d.ts} +2 -2
- package/dist/{engine-BITiErg-.d.ts.map → engine-CaHaCc3W.d.ts.map} +1 -1
- package/dist/{eval-campaign-Cgixrvf7.js → eval-campaign-C_mNDshv.js} +2 -2
- package/dist/{eval-campaign-Cgixrvf7.js.map → eval-campaign-C_mNDshv.js.map} +1 -1
- package/dist/{evaluation-Djq7-Ybj.d.ts → evaluation-BPqUvZ66.d.ts} +3 -3
- package/dist/{evaluation-Djq7-Ybj.d.ts.map → evaluation-BPqUvZ66.d.ts.map} +1 -1
- package/dist/evaluation.d.ts +1 -1
- package/dist/{exact-types-CndDT_h7.d.ts → exact-types-D98BKpxo.d.ts} +2 -2
- package/dist/{exact-types-CndDT_h7.d.ts.map → exact-types-D98BKpxo.d.ts.map} +1 -1
- package/dist/experiment/index.d.ts +1 -1
- package/dist/experiment/index.js +7 -7
- package/dist/{feedback-trajectory-DsHR9krG.d.ts → feedback-trajectory-SBGncrCP.d.ts} +3 -3
- package/dist/{feedback-trajectory-DsHR9krG.d.ts.map → feedback-trajectory-SBGncrCP.d.ts.map} +1 -1
- package/dist/{index-Bdcpx2s8.d.ts → index-B7v2QHlD.d.ts} +6 -6
- package/dist/{index-Bdcpx2s8.d.ts.map → index-B7v2QHlD.d.ts.map} +1 -1
- package/dist/{index-IzWTz5EP.d.ts → index-CUjeTDPv.d.ts} +6 -6
- package/dist/{index-IzWTz5EP.d.ts.map → index-CUjeTDPv.d.ts.map} +1 -1
- package/dist/index-DAk2quYu.d.ts.map +1 -1
- package/dist/{index-BdKzYYkC.d.ts → index-DUn0GNiZ.d.ts} +2 -2
- package/dist/{index-BdKzYYkC.d.ts.map → index-DUn0GNiZ.d.ts.map} +1 -1
- package/dist/{index-Cu12qxJb.d.ts → index-aDPVYBIM.d.ts} +8 -8
- package/dist/{index-Cu12qxJb.d.ts.map → index-aDPVYBIM.d.ts.map} +1 -1
- package/dist/index.d.ts +17 -17
- package/dist/index.js +13 -12
- package/dist/index.js.map +1 -1
- package/dist/{judge-gate-C2BCOt-M.js → judge-gate-Di_6bifh.js} +4 -4
- package/dist/judge-gate-Di_6bifh.js.map +1 -0
- package/dist/{llm-judge-cgbhTKQp.js → llm-judge-D6JtIqiU.js} +9 -9
- package/dist/{llm-judge-cgbhTKQp.js.map → llm-judge-D6JtIqiU.js.map} +1 -1
- package/dist/{matrix-8X6i5kJ6.d.ts → matrix-CBUzIZs4.d.ts} +2 -2
- package/dist/{matrix-8X6i5kJ6.d.ts.map → matrix-CBUzIZs4.d.ts.map} +1 -1
- package/dist/mean-BPuUNznQ.js +16 -0
- package/dist/mean-BPuUNznQ.js.map +1 -0
- package/dist/meta-eval/index.js +1 -1
- package/dist/multishot/golden/index.d.ts +1 -1
- package/dist/multishot/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/{paired-arms-D-54hO-k.js → paired-arms-D5H_58e8.js} +2 -2
- package/dist/{paired-arms-D-54hO-k.js.map → paired-arms-D5H_58e8.js.map} +1 -1
- package/dist/{paired-promotion-decision-FGvHA4p_.js → paired-promotion-decision-BiVumgvM.js} +2 -2
- package/dist/{paired-promotion-decision-FGvHA4p_.js.map → paired-promotion-decision-BiVumgvM.js.map} +1 -1
- package/dist/{mean-CdDYG-us.js → paired-tests-C2zUqgUW.js} +2 -7
- package/dist/paired-tests-C2zUqgUW.js.map +1 -0
- package/dist/{pareto-B9udAt2i.js → pareto-7_LEmYGG.js} +4 -3
- package/dist/{pareto-B9udAt2i.js.map → pareto-7_LEmYGG.js.map} +1 -1
- package/dist/{power-and-mde-ClI6RWw4.js → power-and-mde-CHvyDO_H.js} +2 -2
- package/dist/{power-and-mde-ClI6RWw4.js.map → power-and-mde-CHvyDO_H.js.map} +1 -1
- package/dist/{prime-protocol-BpQlTowq.d.ts → prime-protocol-COFkxas6.d.ts} +2 -2
- package/dist/{prime-protocol-BpQlTowq.d.ts.map → prime-protocol-COFkxas6.d.ts.map} +1 -1
- package/dist/{promotion-policy-CBYeD4zi.js → promotion-policy-D7B4_jEZ.js} +4 -4
- package/dist/{promotion-policy-CBYeD4zi.js.map → promotion-policy-D7B4_jEZ.js.map} +1 -1
- package/dist/{registry-D9A7Tybn.d.ts → registry-dhdDoAV8.d.ts} +3 -3
- package/dist/{registry-D9A7Tybn.d.ts.map → registry-dhdDoAV8.d.ts.map} +1 -1
- package/dist/reporting.js +3 -3
- package/dist/{reward-hacking-DZzHZlS1.js → reward-hacking-Bjk3MAjh.js} +7 -9
- package/dist/{reward-hacking-DZzHZlS1.js.map → reward-hacking-Bjk3MAjh.js.map} +1 -1
- package/dist/reward-hacking-C9hPVT0Q.d.ts.map +1 -1
- package/dist/rl.d.ts +3 -3
- package/dist/rl.js +6 -6
- package/dist/rollout/index.d.ts +2 -2
- package/dist/{run-record-CZy4-TnZ.js → run-record-BUm-Jggo.js} +2 -2
- package/dist/{run-record-CZy4-TnZ.js.map → run-record-BUm-Jggo.js.map} +1 -1
- package/dist/search/index.d.ts +2 -2
- package/dist/search/index.js +2 -2
- package/dist/{search-kernel-B77eE7Ac.d.ts → search-kernel-CBdwFZ05.d.ts} +2 -2
- package/dist/{search-kernel-B77eE7Ac.d.ts.map → search-kernel-CBdwFZ05.d.ts.map} +1 -1
- package/dist/{search-summary-Ct7SrmVc.js → search-summary-Bt3uuK53.js} +4 -4
- package/dist/{search-summary-Ct7SrmVc.js.map → search-summary-Bt3uuK53.js.map} +1 -1
- package/dist/{sequential-B1I0tRfL.js → sequential-De7BTAlV.js} +2 -2
- package/dist/{sequential-B1I0tRfL.js.map → sequential-De7BTAlV.js.map} +1 -1
- package/dist/{skillopt-optimization-method-qB1M554K.js → skillopt-optimization-method-BKFbJEyz.js} +9 -12
- package/dist/skillopt-optimization-method-BKFbJEyz.js.map +1 -0
- package/dist/{statistical-heldout-TP6ndL0B.js → statistical-heldout-C9hz_AF9.js} +3 -3
- package/dist/{statistical-heldout-TP6ndL0B.js.map → statistical-heldout-C9hz_AF9.js.map} +1 -1
- package/dist/{statistical-heldout-BYxHe3KO.d.ts → statistical-heldout-Cei036nE.d.ts} +3 -3
- package/dist/{statistical-heldout-BYxHe3KO.d.ts.map → statistical-heldout-Cei036nE.d.ts.map} +1 -1
- package/dist/{store-tool-spans-DMM6wMwB.d.ts → store-tool-spans-B3i3fl0A.d.ts} +4 -4
- package/dist/{store-tool-spans-DMM6wMwB.d.ts.map → store-tool-spans-B3i3fl0A.d.ts.map} +1 -1
- package/dist/storyboard/index.d.ts +17 -0
- package/dist/storyboard/index.d.ts.map +1 -1
- package/dist/storyboard/index.js +154 -107
- package/dist/storyboard/index.js.map +1 -1
- package/dist/{summary-report-CPfsrLac.js → summary-report-BLnTyQKH.js} +5 -4
- package/dist/{summary-report-CPfsrLac.js.map → summary-report-BLnTyQKH.js.map} +1 -1
- package/dist/{systemone-UHbKD6LH.d.ts → systemone-Cw2txK5m.d.ts} +5 -5
- package/dist/{systemone-UHbKD6LH.d.ts.map → systemone-Cw2txK5m.d.ts.map} +1 -1
- package/dist/{systemone-review-Cn9Uj0xG.d.ts → systemone-review-BLgxt-4Q.d.ts} +3 -3
- package/dist/{systemone-review-Cn9Uj0xG.d.ts.map → systemone-review-BLgxt-4Q.d.ts.map} +1 -1
- package/dist/systemone.d.ts +3 -3
- package/dist/{tool-groups-CFWmAGCV.d.ts → tool-groups-C3K-d3gd.d.ts} +3 -3
- package/dist/tool-groups-C3K-d3gd.d.ts.map +1 -0
- package/dist/{trace-questions-BFCQAzoT.d.ts → trace-questions-ej5AEJ5X.d.ts} +3 -3
- package/dist/{trace-questions-BFCQAzoT.d.ts.map → trace-questions-ej5AEJ5X.d.ts.map} +1 -1
- package/dist/trace-repair/index.d.ts +1 -1
- package/dist/trace-repair/index.js +2 -1
- package/dist/trace-repair/index.js.map +1 -1
- package/dist/traces.d.ts +3 -3
- package/dist/{tree-DrIChF4a.js → tree-DnBkWXun.js} +5 -4
- package/dist/{tree-DrIChF4a.js.map → tree-DnBkWXun.js.map} +1 -1
- package/dist/{types-DUR0dqET.d.ts → types-0oMg7zee.d.ts} +2 -2
- package/dist/{types-DUR0dqET.d.ts.map → types-0oMg7zee.d.ts.map} +1 -1
- package/dist/{types-e3HGaI7P.d.ts → types-C54Uk_a1.d.ts} +2 -2
- package/dist/{types-e3HGaI7P.d.ts.map → types-C54Uk_a1.d.ts.map} +1 -1
- package/dist/{types-BEvjDziM.d.ts → types-DzCO-S1m.d.ts} +8 -8
- package/dist/{types-BEvjDziM.d.ts.map → types-DzCO-S1m.d.ts.map} +1 -1
- package/dist/wire/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/analyze-runs-CTheASYV.js.map +0 -1
- package/dist/benchmark-command-Cr7i7YSX.js.map +0 -1
- package/dist/campaign-ddr1N3yD.js.map +0 -1
- package/dist/judge-gate-C2BCOt-M.js.map +0 -1
- package/dist/mean-CdDYG-us.js.map +0 -1
- package/dist/skillopt-optimization-method-qB1M554K.js.map +0 -1
- package/dist/tool-groups-CFWmAGCV.d.ts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as normalCdf } from "./paired-arms-
|
|
1
|
+
import { o as normalCdf } from "./paired-arms-D5H_58e8.js";
|
|
2
2
|
import { s as zQuantile } from "./internal-v6XfOeJW.js";
|
|
3
3
|
// src/statistics/power-and-mde.ts
|
|
4
4
|
/**
|
|
@@ -96,4 +96,4 @@ function mcnemarPower(opts) {
|
|
|
96
96
|
}
|
|
97
97
|
export { requiredSampleSize as a, requiredPairedSampleSize as i, mcnemarRequiredN as n, pairedMde as r, mcnemarPower as t };
|
|
98
98
|
|
|
99
|
-
//# sourceMappingURL=power-and-mde-
|
|
99
|
+
//# sourceMappingURL=power-and-mde-CHvyDO_H.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-and-mde-
|
|
1
|
+
{"version":3,"file":"power-and-mde-CHvyDO_H.js","names":[],"sources":["../src/statistics/power-and-mde.ts"],"sourcesContent":["import { normalCdf } from '../math/normal'\nimport { zQuantile } from './internal'\n\n// ── Power analysis + minimum detectable effect ───────────────────────\n\n/**\n * Required N per arm for a two-sample comparison at target effect size,\n * alpha, and power. Normal-approximation formula:\n * n = 2 * ( (z_{1-α/2} + z_{1-β}) / d )^2\n * where d is Cohen's d. Returns Infinity for effect ≤ 0.\n */\nexport function requiredSampleSize(opts: {\n effect: number\n alpha?: number\n power?: number\n twoSided?: boolean\n}): number {\n const effect = opts.effect\n if (!Number.isFinite(effect) || effect <= 0) return Infinity\n const alpha = opts.alpha ?? 0.05\n const power = opts.power ?? 0.8\n const twoSided = opts.twoSided ?? true\n const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha)\n const zBeta = zQuantile(power)\n const n = 2 * ((zAlpha + zBeta) / effect) ** 2\n return Math.ceil(n)\n}\n\n/**\n * Required number of paired observations for a target Cohen's dz.\n * Unlike the independent-groups formula, this has no two-arm factor of two.\n *\n * Normal quantiles with no t correction, so treat the result as a LOWER bound:\n * it returns 32 where the exact t-based answer is 34 at dz = 0.5, and 13 where\n * it is 15 at dz = 0.8 — a 6–13 % shortfall precisely in the range a caller\n * consults to decide whether 3–10 repetitions suffice.\n */\nexport function requiredPairedSampleSize(opts: {\n effect: number\n alpha?: number\n power?: number\n twoSided?: boolean\n}): number {\n const effect = opts.effect\n if (!Number.isFinite(effect) || effect <= 0) return Infinity\n const alpha = opts.alpha ?? 0.05\n const power = opts.power ?? 0.8\n const twoSided = opts.twoSided ?? true\n const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha)\n const zBeta = zQuantile(power)\n return Math.ceil(((zAlpha + zBeta) / effect) ** 2)\n}\n\n/**\n * Minimum detectable paired effect (standardised units) for a target paired\n * sample size: d_min = (z_{1-α/2} + z_β) / sqrt(n_paired). Multiply by\n * sd(deltas) for score units; treat as a lower bound — Wilcoxon and bootstrap\n * have asymptotic relative efficiency below 1 vs the t-test on heavy tails.\n */\nexport function pairedMde(opts: {\n nPaired: number\n alpha?: number\n power?: number\n twoSided?: boolean\n}): number {\n if (!Number.isFinite(opts.nPaired) || opts.nPaired <= 0) return Infinity\n const alpha = opts.alpha ?? 0.05\n const power = opts.power ?? 0.8\n const twoSided = opts.twoSided ?? true\n const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha)\n const zBeta = zQuantile(power)\n return (zAlpha + zBeta) / Math.sqrt(opts.nPaired)\n}\n\n/**\n * Number of paired observations needed for a McNemar test to reach a target\n * power — the pre-registration companion to {@link mcnemar}. Parametrised by the\n * expected discordant-cell probabilities `p10` (P[treatment wins on a pair]) and\n * `p01` (P[control wins]); concordant pairs carry no information, so the count\n * is driven entirely by the discordant rate. Lachin's (1992) asymptotic normal\n * approximation: with discordant rate `pDisc = p10 + p01` and marginal effect\n * `δ = p10 − p01`,\n * n = ( z_{1-α/2}·√pDisc + z_{1-β}·√(pDisc − δ²) )² / δ².\n * Returns Infinity when there is no effect (p10 === p01). Asymptotic — at the\n * tiny discordant counts where the exact {@link mcnemar} differs from the normal\n * approximation, treat the result as a lower bound and prefer the discordant-pair\n * floor.\n */\nexport function mcnemarRequiredN(opts: {\n p10: number\n p01: number\n alpha?: number\n power?: number\n twoSided?: boolean\n}): number {\n const { p10, p01 } = opts\n if (p10 < 0 || p01 < 0 || p10 + p01 > 1) {\n throw new Error(`mcnemarRequiredN: require p10,p01 ≥ 0 and p10+p01 ≤ 1 (got ${p10}, ${p01})`)\n }\n const delta = p10 - p01\n if (delta === 0) return Infinity\n const alpha = opts.alpha ?? 0.05\n const power = opts.power ?? 0.8\n const twoSided = opts.twoSided ?? true\n const pDisc = p10 + p01\n const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha)\n const zBeta = zQuantile(power)\n const n =\n (zAlpha * Math.sqrt(pDisc) + zBeta * Math.sqrt(Math.max(0, pDisc - delta * delta))) ** 2 /\n (delta * delta)\n return Math.ceil(n)\n}\n\n/**\n * Power of a McNemar test at a given number of paired observations, the inverse\n * of {@link mcnemarRequiredN} (same Lachin asymptotic model, same parameters).\n * Returns a value in [0, 1]; equals `alpha` when there is no effect.\n */\nexport function mcnemarPower(opts: {\n p10: number\n p01: number\n nPairs: number\n alpha?: number\n twoSided?: boolean\n}): number {\n const { p10, p01, nPairs } = opts\n if (p10 < 0 || p01 < 0 || p10 + p01 > 1) {\n throw new Error(`mcnemarPower: require p10,p01 ≥ 0 and p10+p01 ≤ 1 (got ${p10}, ${p01})`)\n }\n const alpha = opts.alpha ?? 0.05\n const twoSided = opts.twoSided ?? true\n const delta = p10 - p01\n if (delta === 0 || nPairs <= 0) return alpha\n const pDisc = p10 + p01\n const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha)\n const denom = Math.sqrt(Math.max(1e-12, pDisc - delta * delta))\n const zBeta = (Math.sqrt(nPairs) * Math.abs(delta) - zAlpha * Math.sqrt(pDisc)) / denom\n return Math.min(1, Math.max(0, normalCdf(zBeta)))\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,mBAAmB,MAKxB;CACT,MAAM,SAAS,KAAK;CACpB,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG,OAAO;CACpD,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,WAAW,KAAK,YAAY;CAGlC,MAAM,IAAI,MAFK,UAAU,WAAW,IAAI,QAAQ,IAAI,IAAI,KAEnC,IADP,UAAU,KACK,KAAK,WAAW;CAC7C,OAAO,KAAK,KAAK,CAAC;AACpB;;;;;;;;;;AAWA,SAAgB,yBAAyB,MAK9B;CACT,MAAM,SAAS,KAAK;CACpB,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG,OAAO;CACpD,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,WAAW,KAAK,YAAY;CAClC,MAAM,SAAS,UAAU,WAAW,IAAI,QAAQ,IAAI,IAAI,KAAK;CAC7D,MAAM,QAAQ,UAAU,KAAK;CAC7B,OAAO,KAAK,OAAO,SAAS,SAAS,WAAW,CAAC;AACnD;;;;;;;AAQA,SAAgB,UAAU,MAKf;CACT,IAAI,CAAC,OAAO,SAAS,KAAK,OAAO,KAAK,KAAK,WAAW,GAAG,OAAO;CAChE,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,WAAW,KAAK,YAAY;CAGlC,QAFe,UAAU,WAAW,IAAI,QAAQ,IAAI,IAAI,KAE3C,IADC,UAAU,KACH,KAAK,KAAK,KAAK,KAAK,OAAO;AAClD;;;;;;;;;;;;;;;AAgBA,SAAgB,iBAAiB,MAMtB;CACT,MAAM,EAAE,KAAK,QAAQ;CACrB,IAAI,MAAM,KAAK,MAAM,KAAK,MAAM,MAAM,GACpC,MAAM,IAAI,MAAM,8DAA8D,IAAI,IAAI,IAAI,EAAE;CAE9F,MAAM,QAAQ,MAAM;CACpB,IAAI,UAAU,GAAG,OAAO;CACxB,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,WAAW,KAAK,YAAY;CAClC,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,UAAU,WAAW,IAAI,QAAQ,IAAI,IAAI,KAAK;CAC7D,MAAM,QAAQ,UAAU,KAAK;CAC7B,MAAM,KACH,SAAS,KAAK,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK,KAAK,IAAI,GAAG,QAAQ,QAAQ,KAAK,CAAC,MAAM,KACtF,QAAQ;CACX,OAAO,KAAK,KAAK,CAAC;AACpB;;;;;;AAOA,SAAgB,aAAa,MAMlB;CACT,MAAM,EAAE,KAAK,KAAK,WAAW;CAC7B,IAAI,MAAM,KAAK,MAAM,KAAK,MAAM,MAAM,GACpC,MAAM,IAAI,MAAM,0DAA0D,IAAI,IAAI,IAAI,EAAE;CAE1F,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,WAAW,KAAK,YAAY;CAClC,MAAM,QAAQ,MAAM;CACpB,IAAI,UAAU,KAAK,UAAU,GAAG,OAAO;CACvC,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,UAAU,WAAW,IAAI,QAAQ,IAAI,IAAI,KAAK;CAC7D,MAAM,QAAQ,KAAK,KAAK,KAAK,IAAI,OAAO,QAAQ,QAAQ,KAAK,CAAC;CAC9D,MAAM,SAAS,KAAK,KAAK,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,SAAS,KAAK,KAAK,KAAK,KAAK;CAClF,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC;AAClD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { m as CustomTokenPricing } from "./cost-ledger-Czro9wmo.js";
|
|
2
|
-
import { p as AnalystUsageReceipt } from "./types-
|
|
2
|
+
import { p as AnalystUsageReceipt } from "./types-DzCO-S1m.js";
|
|
3
3
|
import { t as PrimeBridgeTransport } from "./prime-bridge-transport-Cfqir_Im.js";
|
|
4
4
|
/**
|
|
5
5
|
* The reply grammar an analyst arm holds a model to, independent of transport.
|
|
@@ -306,4 +306,4 @@ interface PrimeProtocolIdentity {
|
|
|
306
306
|
*/
|
|
307
307
|
declare function primeProtocolSha256(identity: PrimeProtocolIdentity): string;
|
|
308
308
|
export { runPrimeExchange as C, ReplyRowDecoded as D, ReplyEnvelope as E, ReplyRowRejection as O, projectPrimeTrajectory as S, ReplyContract as T, buildPrimePrompt as _, PrimeProjectionOutcome as a, normalizePrimeUsage as b, PrimeProtocolIdentity as c, PrimeRepairPromptSpec as d, PrimeRepairState as f, analystUsageReceiptFromPrimeUsage as g, PrimeTurnRecord as h, PrimeProjectionDelivery as i, PrimeRawUsage as l, PrimeRowDecoded as m, PrimeExchangeOutcome as n, PrimeProjectionSource as o, PrimeReplyContract as p, PrimeFailure as r, PrimePromptSpec as s, PrimeExchangeOptions as t, PrimeRejectedRow as u, buildPrimeRepairPrompt as v, DecodedReply as w, primeProtocolSha256 as x, mergePrimeRawUsage as y };
|
|
309
|
-
//# sourceMappingURL=prime-protocol-
|
|
309
|
+
//# sourceMappingURL=prime-protocol-COFkxas6.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prime-protocol-
|
|
1
|
+
{"version":3,"file":"prime-protocol-COFkxas6.d.ts","names":[],"sources":["../src/analyst/reply-contract.ts","../src/analyst/prime-protocol.ts"],"mappings":";;;;;;;;;;;;;KAaY,gBAAgB;EAAU;EAAU,KAAK;;EAAW;EAAW;;;UAG1D;EACf;;EAEA,QAAQ;;UAGO,cAAc;;EAE7B;;EAEA;;;;;EAKA;;;;;;EAMA,UAAU,cAAc,gBAAgB,gBAAgB;;;;;;EAMxD;;;;;;EAMA,eAAe,iBAAiB;;;;;;EAMhC;;;;;EAKA;;;UAIe;EACf;EACA;;UAGe,aAAa;EAC5B,MAAM;;EAEN,QAAQ;EACR,UAAU;;EAEV;;EAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;KC3CU,gBAAgB,QAAQ,gBAAgB;KAExC,mBAAmB,QAAQ,cAAc;UAIpC;EACf;;EAEA;EACA;;EAEA;EACA;;EAEA;;iBAGc,iBAAiB,MAAM;UAatB;EACf;EACA;EACA;;;iBAIc,uBAAuB,MAAM;;;;;;UAoE5B;;;;;;EAMf;;EAEA;;EAEA;;;;;;;EAOA;;;iBAQc,oBAAoB,eAAe;;;;;;iBAgBnC,mBAAmB,GAAG,eAAe,GAAG,gBAAgB;;;;;;;;;;;;;iBA6BxD,kCACd,OAAO,eACP,SAAS,qBACR;;;;;;;;KAqCS;EAEN;EACA;;EAEA;EAAqB;EAAiB;EAAgB;;EACtD;EAAiB;EAAiB;;UAEvB;EACf;;EAEA;;;UAIe;EACf;EACA,OAAO;;EAEP;;UAGe;EACf;EACA;;KAGU,qBAAqB;EAE3B;;EAEA;EACA,MAAM;EACN,UAAU;;EAEV;;EAEA;EACA,OAAO;EACP,OAAO;EACP,QAAQ;EACR;;EAGA;EACA,SAAS;EACT,OAAO;EACP,OAAO;EACP,QAAQ;;EAER;;UAGW,qBAAqB;EACpC,UAAU,mBAAmB;EAC7B;EACA,WAAW;;EAEX;EACA;;EAEA;;EAEA;EACA,SAAS;;;;;;;iBAQW,iBAAiB,MACrC,SAAS,qBAAqB,QAC7B,QAAQ,qBAAqB;;;;;;UAyLf,sBAAsB;;EAErC,QAAQ,iBAAiB;;EAEzB,UAAU,iBAAiB;;EAE3B;;UAGe;EACf;EACA;EACA;;KAGU,uBAAuB;EAE7B;EACA,gBAAgB;EAChB;EACA,UAAU;;EAEV;EAAW;EAAgB;;;;;;;;;iBASX,uBAAuB,OAC3C,QAAQ,sBAAsB,QAC9B;EAAU;IACT,QAAQ,uBAAuB;UA8BjB;EACf;EACA;EACA;EACA;;EAEA,QAAQ,SAAS;;;;;;;;;;;;iBAaH,oBAAoB,UAAU"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as decidePairedPromotion } from "./paired-promotion-decision-
|
|
3
|
-
import { o as pairHoldout, r as detectScale } from "./statistical-heldout-
|
|
1
|
+
import { r as pairedBootstrap } from "./paired-tests-C2zUqgUW.js";
|
|
2
|
+
import { t as decidePairedPromotion } from "./paired-promotion-decision-BiVumgvM.js";
|
|
3
|
+
import { o as pairHoldout, r as detectScale } from "./statistical-heldout-C9hz_AF9.js";
|
|
4
4
|
// src/campaign/gates/promotion-policy.ts
|
|
5
5
|
/**
|
|
6
6
|
* Build paired evidence for each objective and apply a promotion policy.
|
|
@@ -170,4 +170,4 @@ function paretoSignificanceGate(options) {
|
|
|
170
170
|
}
|
|
171
171
|
export { paretoPolicy as n, paretoSignificanceGate as r, buildEvidenceVector as t };
|
|
172
172
|
|
|
173
|
-
//# sourceMappingURL=promotion-policy-
|
|
173
|
+
//# sourceMappingURL=promotion-policy-D7B4_jEZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promotion-policy-CBYeD4zi.js","names":[],"sources":["../src/campaign/gates/promotion-policy.ts"],"sourcesContent":["/**\n * Build paired evidence for each objective and apply a promotion policy.\n * The default policy requires at least one gain and every regression floor to\n * clear. A floor can fail because a larger loss remains plausible; that does\n * not demonstrate an observed regression. Missing evidence stays unresolved.\n * Confidence intervals apply to each axis, without a multiplicity adjustment.\n *\n * Cost and latency remain aggregate constraints because GateContext does not\n * supply paired observations for them. Compose a budget gate when needed.\n */\n\nimport {\n decidePairedPromotion,\n type PairedDecisionMethod,\n type PairedDecisionStatistic,\n type PairedMcNemarEvidence,\n} from '../../paired-promotion-decision'\nimport type { Direction } from '../../pareto'\nimport {\n DECISION_PAIRED_DELTA_STATISTIC,\n type PairedBootstrapResult,\n pairedBootstrap,\n} from '../../statistics'\nimport type { Gate, GateContext, GateDecision, GateResult, JudgeScore, Scenario } from '../types'\nimport { detectScale, pairHoldout } from './statistical-heldout'\n\n/** Where an objective's per-cell scalar comes from. `composite` reads the\n * judge's composite; `dimension` reads a named per-dimension score. */\nexport type ObjectiveSource = { kind: 'composite' } | { kind: 'dimension'; dimension: string }\n\nexport interface PromotionObjective {\n /** Stable label used in reports + `contributingGates`. */\n name: string\n source: ObjectiveSource\n /** 'maximize' (quality dims) or 'minimize' (error/risk/length dims). Orients\n * the paired delta so a positive bootstrap always means \"candidate better\". */\n direction: Direction\n /** Declared binary support {0, binaryScale}, including zero-only observations.\n * Must be finite and positive; paired cell scores must be 0 or this scale.\n * Uses the risk-difference mean and rejects the 'median' statistic. */\n binaryScale?: number\n /** The good-direction paired-delta CI lower bound must EXCEED this to count\n * as a significant gain on this axis. Interpreted in the judge's native\n * scale. Default 0 (⇒ \"confidently better\"). */\n gainThreshold?: number\n /** A floor breach (regression) is declared when the good-direction CI lower\n * bound is below −floorTolerance, or when the exact small-sample test proves\n * a drop past it. Defaults to 0.05 times the declared binary scale, or\n * auto-scales off observed magnitudes (0.05 on [0,1], 5 on 0-100). */\n floorTolerance?: number\n}\n\n/** Per-axis verdict from the shared paired decision rule.\n * 'regressed' includes uncertainty that prevents clearing the regression floor. */\nexport type AxisVerdict = 'improved' | 'regressed' | 'flat' | 'few_runs' | 'indeterminate'\n\nexport interface AxisEvidence {\n name: string\n source: ObjectiveSource\n direction: Direction\n /** Paired bootstrap on the GOOD-DIRECTION delta (oriented by `direction`):\n * a positive value means the candidate is better on this axis.\n *\n * DIAGNOSTIC on a pass/fail axis: there the verdict is decided on Tango's\n * score interval instead, because a percentile bootstrap over a three-atom\n * delta lattice is not a valid interval at the nonzero margin `floorTolerance`\n * and `gainThreshold` create. `ci` carries the interval that decided. */\n bootstrap: PairedBootstrapResult\n /** Which paired statistic `bootstrap.low`/`.high` bracket. `'mean'` unless the\n * caller asked for the median — on a pass/fail axis the median and its whole\n * CI are pinned at 0 by tie domination and can see neither a gain nor a\n * regression. `bootstrap.median` still carries the median point estimate. */\n bootstrapStatistic: 'median' | 'mean'\n /** The interval the axis verdict was actually decided on, good-direction and\n * in the axis'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 axis; null otherwise. */\n mcnemar: PairedMcNemarEvidence | null\n /** `ci` has zero width or non-finite bounds. It cannot establish a gain or\n * clear a regression floor, regardless of the point estimate. */\n indeterminate: boolean\n /** Paired observations contributing to this axis. */\n n: number\n minimumRequired: number\n decisionMethod: PairedDecisionMethod\n gainThreshold: number\n floorTolerance: number\n verdict: AxisVerdict\n}\n\nexport interface EvidenceVector {\n /** One entry per objective — NOTHING averaged across axes. */\n axes: AxisEvidence[]\n /** Smallest paired n across axes that produced observations — the binding\n * evidence-sufficiency constraint. 0 when no axis produced observations. */\n minN: number\n /** Aggregate per-side cost from the gate context (a constraint input, not a\n * CI axis — see the module header). */\n cost: { candidate: number; baseline: number }\n}\n\n/** A promotion strategy: a pure function from the evidence vector to a verdict.\n * Many policies can run over the same `EvidenceVector` and disagree — that's\n * the point (competing strategies, shared evidence). */\nexport type PromotionPolicy = (ev: EvidenceVector) => GateResult\n\nexport interface BuildEvidenceVectorOptions {\n /** Minimum paired observations before an axis can claim significance; below\n * it the axis is `few_runs`. The exact small-sample test may require more\n * observations at the selected confidence. */\n minProductiveRuns?: number\n /** Confidence level for every axis bootstrap. Default 0.95. */\n confidence?: number\n /** Bootstrap resamples. Default 2000. */\n resamples?: number\n /** Fixed bootstrap seed for a deterministic, reproducible verdict. Default 1337. */\n seed?: number\n /** Paired statistic every axis 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\n/**\n * The Evidence Bus. For each objective, pair candidate vs baseline by full\n * cellId and bootstrap a CI on the good-direction paired delta. Reuses the\n * exact `pairHoldout` + `pairedBootstrap` machinery the held-out gate uses, so\n * a single source of truth governs pairing granularity + scale handling.\n */\nexport function buildEvidenceVector<TArtifact, TScenario extends Scenario>(\n ctx: GateContext<TArtifact, TScenario>,\n objectives: PromotionObjective[],\n opts: BuildEvidenceVectorOptions = {},\n): EvidenceVector {\n if (objectives.length === 0) {\n throw new Error('buildEvidenceVector: at least 1 objective required')\n }\n const confidence = opts.confidence ?? 0.95\n const resamples = opts.resamples ?? 2000\n const seed = opts.seed ?? 1337\n const baseline = ctx.baselineJudgeScores ?? ctx.judgeScores\n const scenarioIds = new Set(ctx.scenarios.map((s) => s.id))\n\n const axes: AxisEvidence[] = []\n for (const obj of objectives) {\n let select: (s: JudgeScore) => number | undefined\n if (obj.source.kind === 'composite') {\n select = (s) => s.composite\n } else {\n const dim = obj.source.dimension\n select = (s) => s.dimensions[dim]\n }\n const paired = pairHoldout(ctx.judgeScores, baseline, scenarioIds, select)\n // Orient to the good direction: maximize ⇒ bootstrap (candidate − baseline);\n // minimize ⇒ bootstrap (baseline − candidate) by swapping args, so a\n // positive bootstrap always reads as \"candidate better on this axis\".\n const before = obj.direction === 'maximize' ? paired.before : paired.after\n const after = obj.direction === 'maximize' ? paired.after : paired.before\n const n = paired.before.length\n const binaryScale = obj.binaryScale\n const floorTolerance =\n obj.floorTolerance ?? 0.05 * (binaryScale ?? detectScale([...paired.before, ...paired.after]))\n const gainThreshold = obj.gainThreshold ?? 0\n // Axes are decided on the MEAN paired delta — which for a pass/fail axis is\n // exactly the change in success rate. The median is structurally blind on\n // the shapes eval data lands in: with most pairs tied its bootstrap CI\n // collapses to [0,0] and the axis reads 'flat', hiding real gains AND real\n // regressions — pass/fail axes on ANY encoding ({0,1} and the 0-100 one\n // `detectScale` exists for), and low-cardinality axes even below half ties.\n // Orthogonal to `pairedDeltaTest`'s own small-sample switch: that picks the\n // TEST (bootstrap CI at n ≥ 20, exact sign test below), this picks the\n // ESTIMATOR the test is applied to. Both are needed — an exact sign test on\n // a tie-pinned median is still blind.\n const bootstrapStatistic = opts.statistic ?? DECISION_PAIRED_DELTA_STATISTIC\n // Both burdens of proof route through the ONE shared rule\n // (`decidePairedPromotion`), so a pass/fail axis is judged on Tango's score\n // interval — the only paired-binary construction valid at the nonzero\n // margins `gainThreshold` / `floorTolerance` create — and a zero-width\n // interval cannot buy a verdict in either direction.\n const improvement = decidePairedPromotion(before, after, {\n confidence,\n resamples,\n statistic: bootstrapStatistic,\n seed,\n threshold: gainThreshold,\n minPairs: opts.minProductiveRuns,\n binaryScale,\n })\n const regression = decidePairedPromotion(after, before, {\n confidence,\n resamples,\n statistic: bootstrapStatistic,\n seed,\n threshold: floorTolerance,\n minPairs: opts.minProductiveRuns,\n binaryScale,\n })\n const bootstrap =\n improvement.bootstrap ??\n pairedBootstrap(before, after, {\n confidence,\n resamples,\n statistic: bootstrapStatistic,\n seed,\n })\n // A floor breach fires on EITHER burden of proof, because they cover\n // different failures and the floor is the anti-Goodhart guard:\n // - `improvement.low < -floorTolerance` — the CREDIBLE WORST CASE exceeds\n // the tolerance. This is the contract `AxisEvidence.floorTolerance` and\n // `paretoPolicy`'s own reason string state, and it is the conservative\n // posture a safety axis needs: block unless the data can rule the\n // breach out, rather than waiting for the breach to be proven. Read off\n // the DECIDING interval, so a pass/fail axis is not screened by a\n // bootstrap that is pinned wherever ties dominate.\n // - `regression.promote` — a PROVEN drop past the tolerance. Adds the\n // small-sample path, where the decision is an exact sign test because\n // the bootstrap interval is descriptive only.\n // A tied binary axis still has uncertainty about unseen discordant pairs.\n // Its diagnostic bootstrap collapses to zero, so only the deciding score\n // interval can establish that a regression stays within the declared floor.\n const floorBreached = improvement.low < -floorTolerance || regression.promote\n // Floor check precedes the gain check: a credible regression must never be\n // masked as \"improved\". With the defaults (gainThreshold 0, positive floor)\n // the regions are disjoint and order is moot, but a consumer who sets a\n // negative gainThreshold (\"accept small dips\") could otherwise have a real\n // floor breach classified as a gain — anti-Goodhart wins the tie.\n const verdict: AxisVerdict = !improvement.sufficient\n ? 'few_runs'\n : improvement.indeterminate\n ? 'indeterminate'\n : floorBreached\n ? 'regressed'\n : improvement.promote\n ? 'improved'\n : 'flat'\n axes.push({\n name: obj.name,\n source: obj.source,\n direction: obj.direction,\n bootstrap,\n bootstrapStatistic,\n ci: { low: improvement.low, high: improvement.high },\n decisionStatistic: improvement.statistic,\n mcnemar: improvement.mcnemar,\n indeterminate: improvement.indeterminate,\n n,\n minimumRequired: improvement.minimumPairs,\n decisionMethod: improvement.method,\n gainThreshold,\n floorTolerance,\n verdict,\n })\n }\n const ns = axes.map((a) => a.n).filter((n) => n > 0)\n const minN = ns.length > 0 ? Math.min(...ns) : 0\n return { axes, minN, cost: { candidate: ctx.cost.candidate, baseline: ctx.cost.baseline } }\n}\n\n/**\n * Require a supported gain and every configured regression floor to clear.\n * A failed floor holds the candidate, including when uncertainty permits a loss.\n * Missing or indeterminate evidence requires more work; no gain holds release.\n */\nexport const paretoPolicy: PromotionPolicy = (ev) => {\n const contributingGates = ev.axes.map((ax) => ({\n name: `objective:${ax.name}`,\n status:\n ax.verdict === 'regressed'\n ? ('fail' as const)\n : ax.verdict === 'few_runs' || ax.verdict === 'indeterminate'\n ? ('not_evaluated' as const)\n : ('pass' as const),\n detail: {\n direction: ax.direction,\n source: ax.source,\n verdict: ax.verdict,\n n: ax.n,\n deltaMedian: ax.bootstrap.median,\n ciLow: ax.ci.low,\n ciHigh: ax.ci.high,\n decisionStatistic: ax.decisionStatistic,\n decisionMethod: ax.decisionMethod,\n mcnemar: ax.mcnemar,\n indeterminate: ax.indeterminate,\n bootstrapCiLow: ax.bootstrap.low,\n bootstrapCiHigh: ax.bootstrap.high,\n confidence: ax.bootstrap.confidence,\n gainThreshold: ax.gainThreshold,\n floorTolerance: ax.floorTolerance,\n },\n }))\n\n const regressed = ev.axes.filter((a) => a.verdict === 'regressed')\n const insufficient = ev.axes.filter(\n (a) => a.verdict === 'few_runs' || a.verdict === 'indeterminate',\n )\n const improved = ev.axes.filter((a) => a.verdict === 'improved')\n\n let decision: GateDecision\n const reasons: string[] = []\n if (regressed.length > 0) {\n // A gain on another axis cannot excuse an unresolved regression floor.\n decision = 'hold'\n for (const a of regressed) {\n reasons.push(\n `objective '${a.name}' did not clear its regression floor -${a.floorTolerance}: good-direction CI [${a.ci.low.toFixed(3)}, ${a.ci.high.toFixed(3)}] (n=${a.n})`,\n )\n }\n } else if (insufficient.length > 0) {\n // An unresolved axis cannot establish either a gain or a safe floor.\n decision = 'need_more_work'\n for (const a of insufficient) {\n reasons.push(\n a.verdict === 'few_runs'\n ? `objective '${a.name}' has only n=${a.n} paired runs — insufficient evidence to claim significance`\n : `objective '${a.name}' has an indeterminate deciding CI [${a.ci.low}, ${a.ci.high}] — insufficient evidence to clear its regression floor (n=${a.n})`,\n )\n }\n } else if (improved.length > 0) {\n decision = 'ship'\n reasons.push(\n `Supported objective gain: ${improved\n .map(\n (a) =>\n `'${a.name}' +${a.ci.low > 0 ? a.ci.low.toFixed(3) : a.bootstrap.mean.toFixed(3)} (CI.low ${a.ci.low.toFixed(3)})`,\n )\n .join(', ')}; every regression floor cleared`,\n )\n } else {\n // Every floor cleared, but no objective demonstrated a significant gain.\n decision = 'hold'\n reasons.push(\n 'no Pareto improvement: no objective shows a significant gain; every regression floor cleared',\n )\n }\n\n // `delta` surfaces the composite axis if present, else the first axis — a\n // single convenience scalar; the vector lives in `contributingGates`.\n const composite = ev.axes.find((a) => a.source.kind === 'composite') ?? ev.axes[0]\n return { decision, reasons, contributingGates, delta: composite?.bootstrap.median }\n}\n\nexport interface ParetoSignificanceGateOptions extends BuildEvidenceVectorOptions {\n /** The objective vector. Every axis is both a gain source and a safety floor. */\n objectives: PromotionObjective[]\n /** Strategy applied to the evidence vector. Default `paretoPolicy`. Override\n * to run a stricter/looser strategy over the SAME bus (competing policies). */\n policy?: PromotionPolicy\n /** Override the gate name in reports. */\n name?: string\n}\n\n/**\n * Wrap the bus + a policy as a `Gate`. Plugs into the existing\n * `runImprovementLoop({ gate })` slot and composes via `composeGate`; default\n * loop behavior is unchanged because consumers opt in by passing this gate.\n */\nexport function paretoSignificanceGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n options: ParetoSignificanceGateOptions,\n): Gate<TArtifact, TScenario> {\n if (options.objectives.length === 0) {\n throw new Error('paretoSignificanceGate: at least 1 objective required')\n }\n const policy = options.policy ?? paretoPolicy\n return {\n name: options.name ?? 'paretoSignificanceGate',\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n const ev = buildEvidenceVector(ctx, options.objectives, options)\n return policy(ev)\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"promotion-policy-D7B4_jEZ.js","names":[],"sources":["../src/campaign/gates/promotion-policy.ts"],"sourcesContent":["/**\n * Build paired evidence for each objective and apply a promotion policy.\n * The default policy requires at least one gain and every regression floor to\n * clear. A floor can fail because a larger loss remains plausible; that does\n * not demonstrate an observed regression. Missing evidence stays unresolved.\n * Confidence intervals apply to each axis, without a multiplicity adjustment.\n *\n * Cost and latency remain aggregate constraints because GateContext does not\n * supply paired observations for them. Compose a budget gate when needed.\n */\n\nimport {\n decidePairedPromotion,\n type PairedDecisionMethod,\n type PairedDecisionStatistic,\n type PairedMcNemarEvidence,\n} from '../../paired-promotion-decision'\nimport type { Direction } from '../../pareto'\nimport {\n DECISION_PAIRED_DELTA_STATISTIC,\n type PairedBootstrapResult,\n pairedBootstrap,\n} from '../../statistics'\nimport type { Gate, GateContext, GateDecision, GateResult, JudgeScore, Scenario } from '../types'\nimport { detectScale, pairHoldout } from './statistical-heldout'\n\n/** Where an objective's per-cell scalar comes from. `composite` reads the\n * judge's composite; `dimension` reads a named per-dimension score. */\nexport type ObjectiveSource = { kind: 'composite' } | { kind: 'dimension'; dimension: string }\n\nexport interface PromotionObjective {\n /** Stable label used in reports + `contributingGates`. */\n name: string\n source: ObjectiveSource\n /** 'maximize' (quality dims) or 'minimize' (error/risk/length dims). Orients\n * the paired delta so a positive bootstrap always means \"candidate better\". */\n direction: Direction\n /** Declared binary support {0, binaryScale}, including zero-only observations.\n * Must be finite and positive; paired cell scores must be 0 or this scale.\n * Uses the risk-difference mean and rejects the 'median' statistic. */\n binaryScale?: number\n /** The good-direction paired-delta CI lower bound must EXCEED this to count\n * as a significant gain on this axis. Interpreted in the judge's native\n * scale. Default 0 (⇒ \"confidently better\"). */\n gainThreshold?: number\n /** A floor breach (regression) is declared when the good-direction CI lower\n * bound is below −floorTolerance, or when the exact small-sample test proves\n * a drop past it. Defaults to 0.05 times the declared binary scale, or\n * auto-scales off observed magnitudes (0.05 on [0,1], 5 on 0-100). */\n floorTolerance?: number\n}\n\n/** Per-axis verdict from the shared paired decision rule.\n * 'regressed' includes uncertainty that prevents clearing the regression floor. */\nexport type AxisVerdict = 'improved' | 'regressed' | 'flat' | 'few_runs' | 'indeterminate'\n\nexport interface AxisEvidence {\n name: string\n source: ObjectiveSource\n direction: Direction\n /** Paired bootstrap on the GOOD-DIRECTION delta (oriented by `direction`):\n * a positive value means the candidate is better on this axis.\n *\n * DIAGNOSTIC on a pass/fail axis: there the verdict is decided on Tango's\n * score interval instead, because a percentile bootstrap over a three-atom\n * delta lattice is not a valid interval at the nonzero margin `floorTolerance`\n * and `gainThreshold` create. `ci` carries the interval that decided. */\n bootstrap: PairedBootstrapResult\n /** Which paired statistic `bootstrap.low`/`.high` bracket. `'mean'` unless the\n * caller asked for the median — on a pass/fail axis the median and its whole\n * CI are pinned at 0 by tie domination and can see neither a gain nor a\n * regression. `bootstrap.median` still carries the median point estimate. */\n bootstrapStatistic: 'median' | 'mean'\n /** The interval the axis verdict was actually decided on, good-direction and\n * in the axis'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 axis; null otherwise. */\n mcnemar: PairedMcNemarEvidence | null\n /** `ci` has zero width or non-finite bounds. It cannot establish a gain or\n * clear a regression floor, regardless of the point estimate. */\n indeterminate: boolean\n /** Paired observations contributing to this axis. */\n n: number\n minimumRequired: number\n decisionMethod: PairedDecisionMethod\n gainThreshold: number\n floorTolerance: number\n verdict: AxisVerdict\n}\n\nexport interface EvidenceVector {\n /** One entry per objective — NOTHING averaged across axes. */\n axes: AxisEvidence[]\n /** Smallest paired n across axes that produced observations — the binding\n * evidence-sufficiency constraint. 0 when no axis produced observations. */\n minN: number\n /** Aggregate per-side cost from the gate context (a constraint input, not a\n * CI axis — see the module header). */\n cost: { candidate: number; baseline: number }\n}\n\n/** A promotion strategy: a pure function from the evidence vector to a verdict.\n * Many policies can run over the same `EvidenceVector` and disagree — that's\n * the point (competing strategies, shared evidence). */\nexport type PromotionPolicy = (ev: EvidenceVector) => GateResult\n\nexport interface BuildEvidenceVectorOptions {\n /** Minimum paired observations before an axis can claim significance; below\n * it the axis is `few_runs`. The exact small-sample test may require more\n * observations at the selected confidence. */\n minProductiveRuns?: number\n /** Confidence level for every axis bootstrap. Default 0.95. */\n confidence?: number\n /** Bootstrap resamples. Default 2000. */\n resamples?: number\n /** Fixed bootstrap seed for a deterministic, reproducible verdict. Default 1337. */\n seed?: number\n /** Paired statistic every axis 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\n/**\n * The Evidence Bus. For each objective, pair candidate vs baseline by full\n * cellId and bootstrap a CI on the good-direction paired delta. Reuses the\n * exact `pairHoldout` + `pairedBootstrap` machinery the held-out gate uses, so\n * a single source of truth governs pairing granularity + scale handling.\n */\nexport function buildEvidenceVector<TArtifact, TScenario extends Scenario>(\n ctx: GateContext<TArtifact, TScenario>,\n objectives: PromotionObjective[],\n opts: BuildEvidenceVectorOptions = {},\n): EvidenceVector {\n if (objectives.length === 0) {\n throw new Error('buildEvidenceVector: at least 1 objective required')\n }\n const confidence = opts.confidence ?? 0.95\n const resamples = opts.resamples ?? 2000\n const seed = opts.seed ?? 1337\n const baseline = ctx.baselineJudgeScores ?? ctx.judgeScores\n const scenarioIds = new Set(ctx.scenarios.map((s) => s.id))\n\n const axes: AxisEvidence[] = []\n for (const obj of objectives) {\n let select: (s: JudgeScore) => number | undefined\n if (obj.source.kind === 'composite') {\n select = (s) => s.composite\n } else {\n const dim = obj.source.dimension\n select = (s) => s.dimensions[dim]\n }\n const paired = pairHoldout(ctx.judgeScores, baseline, scenarioIds, select)\n // Orient to the good direction: maximize ⇒ bootstrap (candidate − baseline);\n // minimize ⇒ bootstrap (baseline − candidate) by swapping args, so a\n // positive bootstrap always reads as \"candidate better on this axis\".\n const before = obj.direction === 'maximize' ? paired.before : paired.after\n const after = obj.direction === 'maximize' ? paired.after : paired.before\n const n = paired.before.length\n const binaryScale = obj.binaryScale\n const floorTolerance =\n obj.floorTolerance ?? 0.05 * (binaryScale ?? detectScale([...paired.before, ...paired.after]))\n const gainThreshold = obj.gainThreshold ?? 0\n // Axes are decided on the MEAN paired delta — which for a pass/fail axis is\n // exactly the change in success rate. The median is structurally blind on\n // the shapes eval data lands in: with most pairs tied its bootstrap CI\n // collapses to [0,0] and the axis reads 'flat', hiding real gains AND real\n // regressions — pass/fail axes on ANY encoding ({0,1} and the 0-100 one\n // `detectScale` exists for), and low-cardinality axes even below half ties.\n // Orthogonal to `pairedDeltaTest`'s own small-sample switch: that picks the\n // TEST (bootstrap CI at n ≥ 20, exact sign test below), this picks the\n // ESTIMATOR the test is applied to. Both are needed — an exact sign test on\n // a tie-pinned median is still blind.\n const bootstrapStatistic = opts.statistic ?? DECISION_PAIRED_DELTA_STATISTIC\n // Both burdens of proof route through the ONE shared rule\n // (`decidePairedPromotion`), so a pass/fail axis is judged on Tango's score\n // interval — the only paired-binary construction valid at the nonzero\n // margins `gainThreshold` / `floorTolerance` create — and a zero-width\n // interval cannot buy a verdict in either direction.\n const improvement = decidePairedPromotion(before, after, {\n confidence,\n resamples,\n statistic: bootstrapStatistic,\n seed,\n threshold: gainThreshold,\n minPairs: opts.minProductiveRuns,\n binaryScale,\n })\n const regression = decidePairedPromotion(after, before, {\n confidence,\n resamples,\n statistic: bootstrapStatistic,\n seed,\n threshold: floorTolerance,\n minPairs: opts.minProductiveRuns,\n binaryScale,\n })\n const bootstrap =\n improvement.bootstrap ??\n pairedBootstrap(before, after, {\n confidence,\n resamples,\n statistic: bootstrapStatistic,\n seed,\n })\n // A floor breach fires on EITHER burden of proof, because they cover\n // different failures and the floor is the anti-Goodhart guard:\n // - `improvement.low < -floorTolerance` — the CREDIBLE WORST CASE exceeds\n // the tolerance. This is the contract `AxisEvidence.floorTolerance` and\n // `paretoPolicy`'s own reason string state, and it is the conservative\n // posture a safety axis needs: block unless the data can rule the\n // breach out, rather than waiting for the breach to be proven. Read off\n // the DECIDING interval, so a pass/fail axis is not screened by a\n // bootstrap that is pinned wherever ties dominate.\n // - `regression.promote` — a PROVEN drop past the tolerance. Adds the\n // small-sample path, where the decision is an exact sign test because\n // the bootstrap interval is descriptive only.\n // A tied binary axis still has uncertainty about unseen discordant pairs.\n // Its diagnostic bootstrap collapses to zero, so only the deciding score\n // interval can establish that a regression stays within the declared floor.\n const floorBreached = improvement.low < -floorTolerance || regression.promote\n // Floor check precedes the gain check: a credible regression must never be\n // masked as \"improved\". With the defaults (gainThreshold 0, positive floor)\n // the regions are disjoint and order is moot, but a consumer who sets a\n // negative gainThreshold (\"accept small dips\") could otherwise have a real\n // floor breach classified as a gain — anti-Goodhart wins the tie.\n const verdict: AxisVerdict = !improvement.sufficient\n ? 'few_runs'\n : improvement.indeterminate\n ? 'indeterminate'\n : floorBreached\n ? 'regressed'\n : improvement.promote\n ? 'improved'\n : 'flat'\n axes.push({\n name: obj.name,\n source: obj.source,\n direction: obj.direction,\n bootstrap,\n bootstrapStatistic,\n ci: { low: improvement.low, high: improvement.high },\n decisionStatistic: improvement.statistic,\n mcnemar: improvement.mcnemar,\n indeterminate: improvement.indeterminate,\n n,\n minimumRequired: improvement.minimumPairs,\n decisionMethod: improvement.method,\n gainThreshold,\n floorTolerance,\n verdict,\n })\n }\n const ns = axes.map((a) => a.n).filter((n) => n > 0)\n const minN = ns.length > 0 ? Math.min(...ns) : 0\n return { axes, minN, cost: { candidate: ctx.cost.candidate, baseline: ctx.cost.baseline } }\n}\n\n/**\n * Require a supported gain and every configured regression floor to clear.\n * A failed floor holds the candidate, including when uncertainty permits a loss.\n * Missing or indeterminate evidence requires more work; no gain holds release.\n */\nexport const paretoPolicy: PromotionPolicy = (ev) => {\n const contributingGates = ev.axes.map((ax) => ({\n name: `objective:${ax.name}`,\n status:\n ax.verdict === 'regressed'\n ? ('fail' as const)\n : ax.verdict === 'few_runs' || ax.verdict === 'indeterminate'\n ? ('not_evaluated' as const)\n : ('pass' as const),\n detail: {\n direction: ax.direction,\n source: ax.source,\n verdict: ax.verdict,\n n: ax.n,\n deltaMedian: ax.bootstrap.median,\n ciLow: ax.ci.low,\n ciHigh: ax.ci.high,\n decisionStatistic: ax.decisionStatistic,\n decisionMethod: ax.decisionMethod,\n mcnemar: ax.mcnemar,\n indeterminate: ax.indeterminate,\n bootstrapCiLow: ax.bootstrap.low,\n bootstrapCiHigh: ax.bootstrap.high,\n confidence: ax.bootstrap.confidence,\n gainThreshold: ax.gainThreshold,\n floorTolerance: ax.floorTolerance,\n },\n }))\n\n const regressed = ev.axes.filter((a) => a.verdict === 'regressed')\n const insufficient = ev.axes.filter(\n (a) => a.verdict === 'few_runs' || a.verdict === 'indeterminate',\n )\n const improved = ev.axes.filter((a) => a.verdict === 'improved')\n\n let decision: GateDecision\n const reasons: string[] = []\n if (regressed.length > 0) {\n // A gain on another axis cannot excuse an unresolved regression floor.\n decision = 'hold'\n for (const a of regressed) {\n reasons.push(\n `objective '${a.name}' did not clear its regression floor -${a.floorTolerance}: good-direction CI [${a.ci.low.toFixed(3)}, ${a.ci.high.toFixed(3)}] (n=${a.n})`,\n )\n }\n } else if (insufficient.length > 0) {\n // An unresolved axis cannot establish either a gain or a safe floor.\n decision = 'need_more_work'\n for (const a of insufficient) {\n reasons.push(\n a.verdict === 'few_runs'\n ? `objective '${a.name}' has only n=${a.n} paired runs — insufficient evidence to claim significance`\n : `objective '${a.name}' has an indeterminate deciding CI [${a.ci.low}, ${a.ci.high}] — insufficient evidence to clear its regression floor (n=${a.n})`,\n )\n }\n } else if (improved.length > 0) {\n decision = 'ship'\n reasons.push(\n `Supported objective gain: ${improved\n .map(\n (a) =>\n `'${a.name}' +${a.ci.low > 0 ? a.ci.low.toFixed(3) : a.bootstrap.mean.toFixed(3)} (CI.low ${a.ci.low.toFixed(3)})`,\n )\n .join(', ')}; every regression floor cleared`,\n )\n } else {\n // Every floor cleared, but no objective demonstrated a significant gain.\n decision = 'hold'\n reasons.push(\n 'no Pareto improvement: no objective shows a significant gain; every regression floor cleared',\n )\n }\n\n // `delta` surfaces the composite axis if present, else the first axis — a\n // single convenience scalar; the vector lives in `contributingGates`.\n const composite = ev.axes.find((a) => a.source.kind === 'composite') ?? ev.axes[0]\n return { decision, reasons, contributingGates, delta: composite?.bootstrap.median }\n}\n\nexport interface ParetoSignificanceGateOptions extends BuildEvidenceVectorOptions {\n /** The objective vector. Every axis is both a gain source and a safety floor. */\n objectives: PromotionObjective[]\n /** Strategy applied to the evidence vector. Default `paretoPolicy`. Override\n * to run a stricter/looser strategy over the SAME bus (competing policies). */\n policy?: PromotionPolicy\n /** Override the gate name in reports. */\n name?: string\n}\n\n/**\n * Wrap the bus + a policy as a `Gate`. Plugs into the existing\n * `runImprovementLoop({ gate })` slot and composes via `composeGate`; default\n * loop behavior is unchanged because consumers opt in by passing this gate.\n */\nexport function paretoSignificanceGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n options: ParetoSignificanceGateOptions,\n): Gate<TArtifact, TScenario> {\n if (options.objectives.length === 0) {\n throw new Error('paretoSignificanceGate: at least 1 objective required')\n }\n const policy = options.policy ?? paretoPolicy\n return {\n name: options.name ?? 'paretoSignificanceGate',\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n const ev = buildEvidenceVector(ctx, options.objectives, options)\n return policy(ev)\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { o as CostLedgerHandle } from "./cost-ledger-Czro9wmo.js";
|
|
2
|
-
import { c as AnalystRunEvent, d as AnalystRunSummary, i as AnalystFinding, l as AnalystRunInputs, n as AnalystContext, t as Analyst, u as AnalystRunResult } from "./types-
|
|
2
|
+
import { c as AnalystRunEvent, d as AnalystRunSummary, i as AnalystFinding, l as AnalystRunInputs, n as AnalystContext, t as Analyst, u as AnalystRunResult } from "./types-DzCO-S1m.js";
|
|
3
3
|
import { x as ChatClient } from "./types-0KglnQ9o.js";
|
|
4
|
-
import { i as ExactAnalystRunEvent, o as ExactAnalystRunResult, u as ExactExecutionComponentIdentity } from "./exact-types-
|
|
4
|
+
import { i as ExactAnalystRunEvent, o as ExactAnalystRunResult, u as ExactExecutionComponentIdentity } from "./exact-types-D98BKpxo.js";
|
|
5
5
|
interface AnalystHooks {
|
|
6
6
|
/** Legacy runs may mutate ctx; exact runs provide a frozen observational context. */
|
|
7
7
|
onBeforeAnalyze?(args: {
|
|
@@ -165,4 +165,4 @@ declare class AnalystRegistry {
|
|
|
165
165
|
runStream(runId: string, inputs: AnalystRunInputs, runOpts?: RegistryRunOpts): AsyncGenerator<AnalystRunEvent, void, void>;
|
|
166
166
|
}
|
|
167
167
|
export { BudgetPolicy as a, RegistryRunOpts as c, AnalystRegistryOptions as i, ExactAnalystRunExecutionError as n, ExactAnalystBudgetPolicy as o, AnalystHooks as r, ExactRegistryRunOpts as s, AnalystRegistry as t };
|
|
168
|
-
//# sourceMappingURL=registry-
|
|
168
|
+
//# sourceMappingURL=registry-dhdDoAV8.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-
|
|
1
|
+
{"version":3,"file":"registry-dhdDoAV8.d.ts","names":[],"sources":["../src/analyst/registry-plan.ts","../src/analyst/registry-execution.ts","../src/analyst/registry.ts"],"mappings":";;;;UAiBiB;;EAEf,iBAAiB;IACf,SAAS;IACT,KAAK;IACL;aACS;;EAEX,gBAAgB;IACd,SAAS;IACT,SAAS;IACT,UAAU;IACV;aACS;;;;;;EAMX,SAAS;IACP,SAAS;IACT,OAAO;IACP;MACE,+BAA+B,QAAQ;;EAE3C,YAAY;IAAQ,QAAQ;aAA4B;;UAGzC;;EAEf;;EAEA,UAAU;;;;;;;EAOV,YAAY;IACV,SAAS;IACT;IACA;IACA;;;UAIa;;EAEf,OAAO;;EAEP,OAAO,aAAa,SAAS;;EAE7B,QAAQ;;EAER,gBAAgB;;EAEhB,gBAAgB;;EAEhB,eAAe;;UAGA;;EAEf;;EAEA;;EAEA,SAAS;;EAET;;EAEA,SAAS;;EAET,aAAa;;EAEb;;EAEA,OAAO;;;;;;;;;EASP,gBAAgB,cAAc,kBAAkB,eAAe,cAAc;;;;;;EAM7E;;;KAIU;WAEG;WACA;;WAGA;WACA;;WAEA,SAAS,SAAS;;;;;;;;;;UAWhB;WACN;WACA,QAAQ;WACR;WACA,QAAQ;WACR,YAAY;WACZ,oBAAoB;WACpB;WACA,MAAM,SAAS;WACf,eACL,cAAc,kBACd,SAAS,eAAe,cAAc;WAEjC;WACA;WACA;WACA;;;cChHE,sCAAsC;WACxC;WACA,QAAQ;EAEL,YAAA,iBAAiB,QAAQ,uBAAuB,UAAU;;cCN3D;mBACM;mBACA;EAEL,YAAA,UAAS;EAIrB,SAAS,SAAS;EAsBlB,QAAQ;IACN;IACA;IACA;IACA,MAAM;;EAUF,IACJ,eACA,QAAQ,kBACR,UAAS,kBACR,QAAQ;;EAUL,SACJ,eACA,QAAQ,kBACR,SAAS,uBACR,QAAQ;;EAQJ,eACL,eACA,QAAQ,kBACR,SAAS,uBACR,eAAe;;;;;;;;;;;;EAmBX,UACL,eACA,QAAQ,kBACR,UAAS,kBACR,eAAe"}
|
package/dist/reporting.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { i as summaryTable, l as RESEARCH_REPORT_HARD_PAIR_FLOOR, n as gainHistogram, t as researchReport } from "./summary-report-
|
|
2
|
-
import { a as wilcoxonSignedRank } from "./paired-arms-
|
|
1
|
+
import { i as summaryTable, l as RESEARCH_REPORT_HARD_PAIR_FLOOR, n as gainHistogram, t as researchReport } from "./summary-report-BLnTyQKH.js";
|
|
2
|
+
import { a as wilcoxonSignedRank } from "./paired-arms-D5H_58e8.js";
|
|
3
3
|
import { t as benjaminiHochberg } from "./multiplicity-DAd4xaDC.js";
|
|
4
|
-
import {
|
|
4
|
+
import { r as pairedBootstrap } from "./paired-tests-C2zUqgUW.js";
|
|
5
5
|
import { n as pairedEvalueSequence, t as evaluateInterimReleaseConfidence } from "./sequential-Bjs5roiK.js";
|
|
6
6
|
import { n as bootstrapCi, r as judgeReplayGate, t as evaluateReleaseConfidence } from "./release-confidence-CTUJz5mM.js";
|
|
7
7
|
import { t as rubricPredictiveValidity } from "./rubric-predictive-validity-CQp_M0vy.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { n as observedScore, s as trainingScore, t as isRealnessGated } from "./reward-P0VD_Aft.js";
|
|
2
2
|
import { r as pearsonR } from "./descriptive-cPwITVfy.js";
|
|
3
|
+
import { r as meanOrZero } from "./mean-BPuUNznQ.js";
|
|
3
4
|
// src/rl/verifiable-reward.ts
|
|
4
5
|
const DEFAULT_DETERMINISTIC_LAYERS = /* @__PURE__ */ new Set([
|
|
5
6
|
"install",
|
|
@@ -293,8 +294,8 @@ function detectRewardHacking(input) {
|
|
|
293
294
|
const beforeTruth = before.map(truthOf).filter(finiteNumber);
|
|
294
295
|
const afterTruth = after.map(truthOf).filter(finiteNumber);
|
|
295
296
|
if (beforeProxy.length >= 2 && afterProxy.length >= 2 && beforeTruth.length >= 2 && afterTruth.length >= 2) {
|
|
296
|
-
const proxyDelta =
|
|
297
|
-
const truthDelta =
|
|
297
|
+
const proxyDelta = meanOrZero(afterProxy) - meanOrZero(beforeProxy);
|
|
298
|
+
const truthDelta = meanOrZero(afterTruth) - meanOrZero(beforeTruth);
|
|
298
299
|
const gap = Math.max(0, proxyDelta - truthDelta);
|
|
299
300
|
const severity = clamp01(gap * 5);
|
|
300
301
|
findings.push({
|
|
@@ -358,8 +359,9 @@ function detectRewardHacking(input) {
|
|
|
358
359
|
});
|
|
359
360
|
if (detRuns.length >= 4) {
|
|
360
361
|
const detBefore = detRuns.slice(0, Math.floor(detRuns.length / 2));
|
|
361
|
-
const
|
|
362
|
-
const
|
|
362
|
+
const detAfter = detRuns.slice(Math.floor(detRuns.length / 2));
|
|
363
|
+
const detDelta = meanOrZero(detAfter.map((r) => r.reward.value)) - meanOrZero(detBefore.map((r) => r.reward.value));
|
|
364
|
+
const proxyDelta = meanOrZero(after.map(proxyOf).filter(finiteNumber)) - meanOrZero(before.map(proxyOf).filter(finiteNumber));
|
|
363
365
|
const driftGap = Math.max(0, proxyDelta - detDelta);
|
|
364
366
|
const severity = clamp01(driftGap * 5);
|
|
365
367
|
findings.push({
|
|
@@ -394,10 +396,6 @@ function detectRewardHacking(input) {
|
|
|
394
396
|
n
|
|
395
397
|
};
|
|
396
398
|
}
|
|
397
|
-
function mean(xs) {
|
|
398
|
-
if (xs.length === 0) return 0;
|
|
399
|
-
return xs.reduce((s, x) => s + x, 0) / xs.length;
|
|
400
|
-
}
|
|
401
399
|
function finiteNumber(value) {
|
|
402
400
|
return typeof value === "number" && Number.isFinite(value);
|
|
403
401
|
}
|
|
@@ -428,4 +426,4 @@ function defaultSecondary(verifiableOpts) {
|
|
|
428
426
|
}
|
|
429
427
|
export { extractVerifiableReward as n, extractVerifiableRewardsFromRecords as r, detectRewardHacking as t };
|
|
430
428
|
|
|
431
|
-
//# sourceMappingURL=reward-hacking-
|
|
429
|
+
//# sourceMappingURL=reward-hacking-Bjk3MAjh.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reward-hacking-DZzHZlS1.js","names":["clamp01"],"sources":["../src/rl/verifiable-reward.ts","../src/rl/reward-hacking.ts"],"sourcesContent":["/**\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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"}
|
|
1
|
+
{"version":3,"file":"reward-hacking-Bjk3MAjh.js","names":["clamp01"],"sources":["../src/rl/verifiable-reward.ts","../src/rl/reward-hacking.ts"],"sourcesContent":["/**\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 { meanOrZero } from '../statistics/mean'\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 = meanOrZero(afterProxy) - meanOrZero(beforeProxy)\n const truthDelta = meanOrZero(afterTruth) - meanOrZero(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 meanOrZero(detAfter.map((r) => r.reward.value)) -\n meanOrZero(detBefore.map((r) => r.reward.value))\n const proxyDelta =\n meanOrZero(after.map(proxyOf).filter(finiteNumber)) -\n meanOrZero(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 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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reward-hacking-C9hPVT0Q.d.ts","names":[],"sources":["../src/rl/verifiable-reward.ts","../src/rl/reward-hacking.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+DY,yBAAyB;UAEpB;;EAEf;;EAEA,QAAQ;;;;;;;;EAQR;;;;;;EAMA;;EAEA;;;;;;;EAOA,YAAY;;;;;;;;;;;EAWZ;;;;;;;;;;;;;;;;;;EAkBA;;UAGe;;;;;;;;EAQf;;;;EAIA,aAAa,sBAAsB;;;;;;;EAOnC;;;;;EAKA;;;;;;;;;;;;EAYA;;;;;;;;;;;;;;;iBA+Dc,wBACd,QAAQ,oBACR,OAAM,oCACL;;;;;;;;;;;;;;;;;;;;;;;iBAqGa,oCACd,MAAM,aACN,OAAM,oCACL;EAAQ;EAAe,QAAQ;;
|
|
1
|
+
{"version":3,"file":"reward-hacking-C9hPVT0Q.d.ts","names":[],"sources":["../src/rl/verifiable-reward.ts","../src/rl/reward-hacking.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+DY,yBAAyB;UAEpB;;EAEf;;EAEA,QAAQ;;;;;;;;EAQR;;;;;;EAMA;;EAEA;;;;;;;EAOA,YAAY;;;;;;;;;;;EAWZ;;;;;;;;;;;;;;;;;;EAkBA;;UAGe;;;;;;;;EAQf;;;;EAIA,aAAa,sBAAsB;;;;;;;EAOnC;;;;;EAKA;;;;;;;;;;;;EAYA;;;;;;;;;;;;;;;iBA+Dc,wBACd,QAAQ,oBACR,OAAM,oCACL;;;;;;;;;;;;;;;;;;;;;;;iBAqGa,oCACd,MAAM,aACN,OAAM,oCACL;EAAQ;EAAe,QAAQ;;KC1RtB;UAMK;EACf,QAAQ;;EAER;EACA;;EAEA,QAAQ;;UAGO;EACf,UAAU;;EAEV,kBAAkB;;;;;;EAMlB;;EAEA;;EAEA;;UAGe;;;;;EAKf,MAAM;;;;;EAKN,WAAW,KAAK;;;;;;EAMhB,WAAW,KAAK;;;;;;EAMhB,qBAAqB,KAAK;;;;;EAK1B;;;;;EAKA;IAAe;IAAkB;;;;;EAIjC,0BAA0B;;iBAgBZ,oBAAoB,OAAO,2BAA2B"}
|
package/dist/rl.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { _ as Span } from "./schema-CkIvnmLU.js";
|
|
|
2
2
|
import { a as RunRecord, c as RunSplitTag } from "./run-record-D2BFMPGl.js";
|
|
3
3
|
import { a as MintedRolloutLine, b as RolloutSplit, t as ChatMessage } from "./schema-DkFJr3Y8.js";
|
|
4
4
|
import { a as PairedPromotionDecision, u as PairedBootstrapResult } from "./paired-promotion-decision-BDnqepZ1.js";
|
|
5
|
-
import { Q as MintRolloutOptions, a as CampaignResult } from "./types-
|
|
5
|
+
import { Q as MintRolloutOptions, a as CampaignResult } from "./types-C54Uk_a1.js";
|
|
6
6
|
import { a as detectRewardHacking, c as VerifiableRewardSource, i as RewardHackingSignal, l as extractVerifiableReward, n as RewardHackingFinding, o as VerifiableReward, r as RewardHackingReport, s as VerifiableRewardExtractionOptions, t as DetectRewardHackingInput, u as extractVerifiableRewardsFromRecords } from "./reward-hacking-C9hPVT0Q.js";
|
|
7
7
|
import { a as RubricPredictiveValidityReport, d as FileSystemOutcomeStore, f as FileSystemOutcomeStoreOptions, h as OutcomeStore, p as InMemoryOutcomeStore, t as OutcomeMetricSpec, u as DeploymentOutcome } from "./rubric-predictive-validity-B3RzbwOx.js";
|
|
8
|
-
import { a as SystemOneReviewReport, t as PreparedSystemOneReview } from "./systemone-review-
|
|
9
|
-
import { n as TraceQuestionOutcome, r as TraceQuestionSpec } from "./trace-questions-
|
|
8
|
+
import { a as SystemOneReviewReport, t as PreparedSystemOneReview } from "./systemone-review-BLgxt-4Q.js";
|
|
9
|
+
import { n as TraceQuestionOutcome, r as TraceQuestionSpec } from "./trace-questions-ej5AEJ5X.js";
|
|
10
10
|
import { t as InterimReleaseConfidence } from "./sequential-Bb_pdTqG.js";
|
|
11
11
|
import { t as AdversarialMutation } from "./adversarial-DnQQFqTR.js";
|
|
12
12
|
import { a as SteeringChange, f as EvalCampaignOptions, h as runEvalCampaign, i as Researcher, n as ExperimentResult, p as EvalCampaignResult, r as FailureMode, t as ExperimentPlan } from "./researcher-CLnNJ7Or.js";
|
package/dist/rl.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { s as ValidationError } from "./errors-DeEEV9Vx.js";
|
|
2
2
|
import { a as hashCanonical, i as compareCodeUnits } from "./canonical-CicJlINA.js";
|
|
3
3
|
import { o as runTaskScore } from "./run-record-DEjx43cI.js";
|
|
4
|
-
import { a as wilcoxonSignedRank } from "./paired-arms-
|
|
4
|
+
import { a as wilcoxonSignedRank } from "./paired-arms-D5H_58e8.js";
|
|
5
5
|
import { a as medianInPlace } from "./internal-v6XfOeJW.js";
|
|
6
|
-
import {
|
|
7
|
-
import { t as decidePairedPromotion } from "./paired-promotion-decision-
|
|
8
|
-
import { n as campaignCellToRunRecord } from "./run-record-
|
|
9
|
-
import { n as extractVerifiableReward, r as extractVerifiableRewardsFromRecords, t as detectRewardHacking } from "./reward-hacking-
|
|
10
|
-
import { t as runEvalCampaign } from "./eval-campaign-
|
|
6
|
+
import { r as pairedBootstrap } from "./paired-tests-C2zUqgUW.js";
|
|
7
|
+
import { t as decidePairedPromotion } from "./paired-promotion-decision-BiVumgvM.js";
|
|
8
|
+
import { n as campaignCellToRunRecord } from "./run-record-BUm-Jggo.js";
|
|
9
|
+
import { n as extractVerifiableReward, r as extractVerifiableRewardsFromRecords, t as detectRewardHacking } from "./reward-hacking-Bjk3MAjh.js";
|
|
10
|
+
import { t as runEvalCampaign } from "./eval-campaign-C_mNDshv.js";
|
|
11
11
|
import { s as assertRewardGate } from "./schema-YUIXT8iW.js";
|
|
12
12
|
import { n as isTrainingLineEligible, o as toSftRows, t as isSplitEligible } from "./exporters-ByLcILYU.js";
|
|
13
13
|
import { t as mintRolloutRows } from "./mint-D9ZysYtY.js";
|
package/dist/rollout/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { A as isTrainableSplit, C as RolloutWorkspaceCapture, D as assertRolloutLine, E as assertMintedLines, F as licenseClassOf, I as licenseClassOfName, M as LICENSE_CLASSES, N as LicenseClass, O as gateGamedOutcome, P as isLicenseEligible, S as RolloutTask, T as assertMinted, _ as RolloutPolicy, a as MintedRolloutLine, b as RolloutSplit, c as ROLLOUT_ROLES, d as RolloutArtifacts, f as RolloutCapture, g as RolloutOutcome, h as RolloutLine, i as GatedEvidence, j as validateRolloutLine, k as isRolloutLine, l as ROLLOUT_SCHEMA, m as RolloutEvidenceAttempt, n as ChatRole, o as MintedRolloutOutcome, p as RolloutCostBlock, r as ChatToolCall, s as ROLLOUT_CAPTURES, t as ChatMessage, u as ROLLOUT_SPLITS, v as RolloutProvenance, w as ToolDef, x as RolloutStep, y as RolloutRole } from "../schema-DkFJr3Y8.js";
|
|
2
|
-
import { $ as MintRolloutResult, Q as MintRolloutOptions, et as RolloutScrubber, nt as mintRolloutRows, rt as unmintableReasons, tt as RolloutSearchLineage } from "../types-
|
|
2
|
+
import { $ as MintRolloutResult, Q as MintRolloutOptions, et as RolloutScrubber, nt as mintRolloutRows, rt as unmintableReasons, tt as RolloutSearchLineage } from "../types-C54Uk_a1.js";
|
|
3
3
|
import { a as trainingReward, i as observedSplitScore, n as ScorePreference, o as trainingScore, r as isRealnessGated, t as ScoreOrigin } from "../reward-ST4PXNXF.js";
|
|
4
4
|
import { a as SftRow, c as VerifiersTokenUsage, d as toRftItems, f as toSftRows, i as SftExportOptions, l as toJsonl, n as RewardRow, p as toVerifiersRolloutOutputs, r as RftItem, s as VerifiersRolloutOutput, t as RealnessLabels, u as toRewardRows } from "../exporters-B5Ze_RKh.js";
|
|
5
|
-
import { $ as HarborMetrics, A as harnessSessionRolloutLine, B as ReadClaudeTranscriptOptions, C as GateReport, Ct as GateEntryPoint, D as readOpencodeSessionMessages, E as DEFAULT_OPENCODE_DB, Et as gatedEvidenceOf, F as readCodexRollout, G as readRolloutJournal, H as parseClaudeTranscript, I as ClaudeTranscript, J as FromHarborOptions, K as readRolloutLedger, L as ClaudeTranscriptRef, M as gateReceiptRolloutLines, N as CodexRollout, O as HarnessSessionImportOptions, P as parseCodexRollout, Q as HarborImageSource, R as ClaudeUsageTotals, S as GateDisposition, St as GateCheckedOutcome, T as assertGateReport, Tt as gateErrors, U as readClaudeTranscript, V as findClaudeTranscripts, W as appendRolloutLines, X as HarborContentPart, Y as HarborAgent, Z as HarborFinalMetrics, _ as privacyHit, _t as GATE_CHECK_IDS, a as BuildOptions, at as HarborToolCall, b as EmittedEvidence, bt as GateCheckDisposition, c as ScrubReport, ct as relabelImportedSplit, d as ReleaseFormat, dt as HARBOR_TASK_CHECKS, et as HarborObservation, f as buildDatasetCard, ft as HarborTaskFile, g as defaultRolloutScrubber, gt as GATE_CHECKS, h as ScrubRule, ht as writeHarborTask, i as toTinkerDatumRows, it as HarborSubagentTrajectoryRef, j as harnessSessionRolloutLines, k as harnessSessionRefs, l as buildHfDataset, lt as toHarborTrajectories, m as ScrubCounts, mt as WriteHarborTaskOptions, n as TinkerDatumRow, nt as HarborStep, o as BuildSummary, ot as HarborTrajectory, p as PRIVACY_RULE_NAMES, pt as HarborTaskVerification, q as writeRolloutLedger, r as TinkerExportOptions, rt as HarborStepSource, s as RolloutReleaseCliArgs, st as fromHarborTrajectory, t as TinkerDatum, tt as HarborObservationResult, u as DatasetCardInputs, ut as toHarborTrajectory, v as scrubLines, vt as GATE_POLICIES, w as ReleaseRowRef, wt as GatePolicy, x as FormatGateCounts, xt as GateCheckId, y as scrubText, yt as GateCheck, z as DEFAULT_CLAUDE_PROJECTS_DIR } from "../index-
|
|
5
|
+
import { $ as HarborMetrics, A as harnessSessionRolloutLine, B as ReadClaudeTranscriptOptions, C as GateReport, Ct as GateEntryPoint, D as readOpencodeSessionMessages, E as DEFAULT_OPENCODE_DB, Et as gatedEvidenceOf, F as readCodexRollout, G as readRolloutJournal, H as parseClaudeTranscript, I as ClaudeTranscript, J as FromHarborOptions, K as readRolloutLedger, L as ClaudeTranscriptRef, M as gateReceiptRolloutLines, N as CodexRollout, O as HarnessSessionImportOptions, P as parseCodexRollout, Q as HarborImageSource, R as ClaudeUsageTotals, S as GateDisposition, St as GateCheckedOutcome, T as assertGateReport, Tt as gateErrors, U as readClaudeTranscript, V as findClaudeTranscripts, W as appendRolloutLines, X as HarborContentPart, Y as HarborAgent, Z as HarborFinalMetrics, _ as privacyHit, _t as GATE_CHECK_IDS, a as BuildOptions, at as HarborToolCall, b as EmittedEvidence, bt as GateCheckDisposition, c as ScrubReport, ct as relabelImportedSplit, d as ReleaseFormat, dt as HARBOR_TASK_CHECKS, et as HarborObservation, f as buildDatasetCard, ft as HarborTaskFile, g as defaultRolloutScrubber, gt as GATE_CHECKS, h as ScrubRule, ht as writeHarborTask, i as toTinkerDatumRows, it as HarborSubagentTrajectoryRef, j as harnessSessionRolloutLines, k as harnessSessionRefs, l as buildHfDataset, lt as toHarborTrajectories, m as ScrubCounts, mt as WriteHarborTaskOptions, n as TinkerDatumRow, nt as HarborStep, o as BuildSummary, ot as HarborTrajectory, p as PRIVACY_RULE_NAMES, pt as HarborTaskVerification, q as writeRolloutLedger, r as TinkerExportOptions, rt as HarborStepSource, s as RolloutReleaseCliArgs, st as fromHarborTrajectory, t as TinkerDatum, tt as HarborObservationResult, u as DatasetCardInputs, ut as toHarborTrajectory, v as scrubLines, vt as GATE_POLICIES, w as ReleaseRowRef, wt as GatePolicy, x as FormatGateCounts, xt as GateCheckId, y as scrubText, yt as GateCheck, z as DEFAULT_CLAUDE_PROJECTS_DIR } from "../index-DUn0GNiZ.js";
|
|
6
6
|
export { type BuildOptions, type BuildSummary, type ChatMessage, type ChatRole, type ChatToolCall, type ClaudeTranscript, type ClaudeTranscriptRef, type ClaudeUsageTotals, type CodexRollout, DEFAULT_CLAUDE_PROJECTS_DIR, DEFAULT_OPENCODE_DB, type DatasetCardInputs, type EmittedEvidence, type FormatGateCounts, type FromHarborOptions, GATE_CHECKS, GATE_CHECK_IDS, GATE_POLICIES, type GateCheck, type GateCheckDisposition, type GateCheckId, type GateCheckedOutcome, type GateDisposition, type GateEntryPoint, type GatePolicy, type GateReport, type GatedEvidence, HARBOR_TASK_CHECKS, type HarborAgent, type HarborContentPart, type HarborFinalMetrics, type HarborImageSource, type HarborMetrics, type HarborObservation, type HarborObservationResult, type HarborStep, type HarborStepSource, type HarborSubagentTrajectoryRef, type HarborTaskFile, type HarborTaskVerification, type HarborToolCall, type HarborTrajectory, type HarnessSessionImportOptions, LICENSE_CLASSES, type LicenseClass, type MintRolloutOptions, type MintRolloutResult, type MintedRolloutLine, type MintedRolloutOutcome, PRIVACY_RULE_NAMES, ROLLOUT_CAPTURES, ROLLOUT_ROLES, ROLLOUT_SCHEMA, ROLLOUT_SPLITS, type ReadClaudeTranscriptOptions, type RealnessLabels, type ReleaseFormat, type ReleaseRowRef, type RewardRow, type RftItem, type RolloutArtifacts, type RolloutCapture, type RolloutCostBlock, type RolloutEvidenceAttempt, type RolloutLine, type RolloutOutcome, type RolloutPolicy, type RolloutProvenance, type RolloutReleaseCliArgs, type RolloutRole, type RolloutScrubber, type RolloutSearchLineage, type RolloutSplit, type RolloutStep, type RolloutTask, type RolloutWorkspaceCapture, type ScoreOrigin, type ScorePreference, type ScrubCounts, type ScrubReport, type ScrubRule, type SftExportOptions, type SftRow, type TinkerDatum, type TinkerDatumRow, type TinkerExportOptions, type ToolDef, type VerifiersRolloutOutput, type VerifiersTokenUsage, type WriteHarborTaskOptions, appendRolloutLines, assertGateReport, assertMinted, assertMintedLines, assertRolloutLine, buildDatasetCard, buildHfDataset, defaultRolloutScrubber, findClaudeTranscripts, fromHarborTrajectory, gateErrors, gateGamedOutcome, gateReceiptRolloutLines, gatedEvidenceOf, harnessSessionRefs, harnessSessionRolloutLine, harnessSessionRolloutLines, isLicenseEligible, isRealnessGated, isRolloutLine, isTrainableSplit, licenseClassOf, licenseClassOfName, mintRolloutRows, observedSplitScore, parseClaudeTranscript, parseCodexRollout, privacyHit, readClaudeTranscript, readCodexRollout, readOpencodeSessionMessages, readRolloutJournal, readRolloutLedger, relabelImportedSplit, scrubLines, scrubText, toHarborTrajectories, toHarborTrajectory, toJsonl, toRewardRows, toRftItems, toSftRows, toTinkerDatumRows, toVerifiersRolloutOutputs, trainingReward, trainingScore, unmintableReasons, validateRolloutLine, writeHarborTask, writeRolloutLedger };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as validateRunRecord } from "./run-record-DEjx43cI.js";
|
|
2
|
-
import { t as mean } from "./mean-
|
|
2
|
+
import { t as mean } from "./mean-BPuUNznQ.js";
|
|
3
3
|
// src/campaign/run-record.ts
|
|
4
4
|
/**
|
|
5
5
|
* A campaign cell carried a judge score without a `dimensions` record.
|
|
@@ -217,4 +217,4 @@ function finiteMetrics(metrics) {
|
|
|
217
217
|
}
|
|
218
218
|
export { campaignCellToRunRecord as n, projectCampaignCellQuality as r, campaignCellCostProvenance as t };
|
|
219
219
|
|
|
220
|
-
//# sourceMappingURL=run-record-
|
|
220
|
+
//# sourceMappingURL=run-record-BUm-Jggo.js.map
|