@tangle-network/agent-eval 0.126.7 → 0.127.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 +44 -0
- package/README.md +2 -2
- package/dist/analyst/index.d.ts +51 -39
- package/dist/analyst/index.js +4 -4
- package/dist/belief-state/index.d.ts +34 -36
- package/dist/belief-state/index.js +2 -2
- package/dist/benchmarks/index.d.ts +30 -12
- package/dist/benchmarks/index.js +12 -10
- package/dist/builder-eval/index.js +1 -1
- package/dist/campaign/index.d.ts +432 -146
- package/dist/campaign/index.js +11 -9
- package/dist/{chunk-ARU2PZFM.js → chunk-424XHVKV.js} +43 -23
- package/dist/chunk-424XHVKV.js.map +1 -0
- package/dist/{chunk-NUKSVU3W.js → chunk-5DTSBUL2.js} +2 -2
- package/dist/{chunk-BTACVO2W.js → chunk-65QETEKG.js} +542 -258
- package/dist/chunk-65QETEKG.js.map +1 -0
- package/dist/{chunk-CGG5SLH3.js → chunk-ARCFIY7U.js} +2 -2
- package/dist/{chunk-P22LJ3Y2.js → chunk-CLELFRQG.js} +6 -4
- package/dist/chunk-CLELFRQG.js.map +1 -0
- package/dist/{chunk-LUNF2SEL.js → chunk-DJKY2TSY.js} +3 -3
- package/dist/chunk-DPUHNQLN.js +232 -0
- package/dist/chunk-DPUHNQLN.js.map +1 -0
- package/dist/{chunk-IPYXE555.js → chunk-EJGRPCO3.js} +36 -13
- package/dist/chunk-EJGRPCO3.js.map +1 -0
- package/dist/{chunk-K6IAZZ6L.js → chunk-FA3MFYNA.js} +132 -110
- package/dist/chunk-FA3MFYNA.js.map +1 -0
- package/dist/chunk-IG35YDN3.js +614 -0
- package/dist/chunk-IG35YDN3.js.map +1 -0
- package/dist/{chunk-DPZAEKA6.js → chunk-IMGJZBFG.js} +84 -56
- package/dist/chunk-IMGJZBFG.js.map +1 -0
- package/dist/{chunk-H5UD2323.js → chunk-IN455XKS.js} +53 -1
- package/dist/chunk-IN455XKS.js.map +1 -0
- package/dist/{chunk-PJQFMIOX.js → chunk-MHELPNRP.js} +31 -1
- package/dist/chunk-MHELPNRP.js.map +1 -0
- package/dist/{chunk-IILEIWGW.js → chunk-NL33OXEP.js} +41 -36
- package/dist/chunk-NL33OXEP.js.map +1 -0
- package/dist/{chunk-474LBSOX.js → chunk-OD5NQ2CS.js} +41 -16
- package/dist/chunk-OD5NQ2CS.js.map +1 -0
- package/dist/{chunk-QOTFXW5L.js → chunk-P5W7RQKK.js} +2 -2
- package/dist/{chunk-IR3KBHOY.js → chunk-P6FYH6K4.js} +39 -3
- package/dist/chunk-P6FYH6K4.js.map +1 -0
- package/dist/{chunk-J5SQWP6Y.js → chunk-PBE2LOSS.js} +18 -2
- package/dist/chunk-PBE2LOSS.js.map +1 -0
- package/dist/{chunk-E3IADPLG.js → chunk-PRB53E3J.js} +50 -224
- package/dist/chunk-PRB53E3J.js.map +1 -0
- package/dist/{chunk-X4UCIOTZ.js → chunk-S5YLIBFX.js} +2 -2
- package/dist/{chunk-7VYEGHWF.js → chunk-SZLVEKMJ.js} +41 -20
- package/dist/chunk-SZLVEKMJ.js.map +1 -0
- package/dist/{chunk-LKKT3IVV.js → chunk-TSN7JT6D.js} +6 -4
- package/dist/{chunk-LKKT3IVV.js.map → chunk-TSN7JT6D.js.map} +1 -1
- package/dist/{chunk-M7AH34KV.js → chunk-UTA2TGC4.js} +32 -16
- package/dist/chunk-UTA2TGC4.js.map +1 -0
- package/dist/{chunk-MAX3TN3C.js → chunk-UWZZKKU7.js} +7 -19
- package/dist/chunk-UWZZKKU7.js.map +1 -0
- package/dist/{chunk-ZVCHKKOP.js → chunk-WED5KLZQ.js} +39 -15
- package/dist/chunk-WED5KLZQ.js.map +1 -0
- package/dist/{chunk-OCFJACJU.js → chunk-X6VOPMLD.js} +74 -14
- package/dist/chunk-X6VOPMLD.js.map +1 -0
- package/dist/{chunk-MOXWMGPC.js → chunk-Y5CYOL42.js} +159 -70
- package/dist/chunk-Y5CYOL42.js.map +1 -0
- package/dist/{chunk-FO7HEH76.js → chunk-YJBNWCAA.js} +2 -2
- package/dist/chunk-ZPUEEM5U.js +179 -0
- package/dist/chunk-ZPUEEM5U.js.map +1 -0
- package/dist/{chunk-KE2VWPZX.js → chunk-ZUUWPZCV.js} +3 -3
- package/dist/cli.js +4 -4
- package/dist/contract/index.d.ts +421 -124
- package/dist/contract/index.js +242 -115
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +34 -32
- package/dist/control.js +2 -2
- package/dist/hosted/index.d.ts +142 -41
- package/dist/hosted/index.js +23 -1
- package/dist/index.d.ts +364 -255
- package/dist/index.js +315 -226
- package/dist/index.js.map +1 -1
- package/dist/meta-eval/index.d.ts +32 -32
- package/dist/meta-eval/index.js +2 -2
- package/dist/multishot/index.d.ts +1 -0
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.js +2 -2
- package/dist/reporting.d.ts +97 -74
- package/dist/reporting.js +5 -4
- package/dist/rl.d.ts +177 -131
- package/dist/rl.js +232 -126
- package/dist/rl.js.map +1 -1
- package/dist/rollout/index.d.ts +53 -63
- package/dist/rollout/index.js +7 -5
- package/dist/{run-campaign-FFRM3RH5.js → run-campaign-3A2EBYBE.js} +4 -3
- package/dist/supervisor-run/index.d.ts +7 -15
- package/dist/supervisor-run/index.js +2 -2
- package/dist/traces.d.ts +66 -41
- package/dist/traces.js +9 -5
- package/dist/wire/index.d.ts +1 -0
- package/dist/wire/index.js +2 -2
- package/docs/concepts.md +18 -1
- package/docs/hosted-ingest-spec.md +37 -40
- package/docs/insight-report.md +57 -7
- package/docs/research-report-methodology.md +20 -21
- package/docs/rollout.md +13 -8
- package/package.json +4 -3
- package/dist/chunk-474LBSOX.js.map +0 -1
- package/dist/chunk-7VYEGHWF.js.map +0 -1
- package/dist/chunk-ARU2PZFM.js.map +0 -1
- package/dist/chunk-BTACVO2W.js.map +0 -1
- package/dist/chunk-DPZAEKA6.js.map +0 -1
- package/dist/chunk-E3IADPLG.js.map +0 -1
- package/dist/chunk-H5UD2323.js.map +0 -1
- package/dist/chunk-IILEIWGW.js.map +0 -1
- package/dist/chunk-IPYXE555.js.map +0 -1
- package/dist/chunk-IR3KBHOY.js.map +0 -1
- package/dist/chunk-J5SQWP6Y.js.map +0 -1
- package/dist/chunk-K6IAZZ6L.js.map +0 -1
- package/dist/chunk-M7AH34KV.js.map +0 -1
- package/dist/chunk-MAX3TN3C.js.map +0 -1
- package/dist/chunk-MOXWMGPC.js.map +0 -1
- package/dist/chunk-OCFJACJU.js.map +0 -1
- package/dist/chunk-P22LJ3Y2.js.map +0 -1
- package/dist/chunk-PJQFMIOX.js.map +0 -1
- package/dist/chunk-ZVCHKKOP.js.map +0 -1
- package/dist/chunk-ZZUXHH3R.js +0 -99
- package/dist/chunk-ZZUXHH3R.js.map +0 -1
- /package/dist/{chunk-NUKSVU3W.js.map → chunk-5DTSBUL2.js.map} +0 -0
- /package/dist/{chunk-CGG5SLH3.js.map → chunk-ARCFIY7U.js.map} +0 -0
- /package/dist/{chunk-LUNF2SEL.js.map → chunk-DJKY2TSY.js.map} +0 -0
- /package/dist/{chunk-QOTFXW5L.js.map → chunk-P5W7RQKK.js.map} +0 -0
- /package/dist/{chunk-X4UCIOTZ.js.map → chunk-S5YLIBFX.js.map} +0 -0
- /package/dist/{chunk-FO7HEH76.js.map → chunk-YJBNWCAA.js.map} +0 -0
- /package/dist/{chunk-KE2VWPZX.js.map → chunk-ZUUWPZCV.js.map} +0 -0
- /package/dist/{run-campaign-FFRM3RH5.js.map → run-campaign-3A2EBYBE.js.map} +0 -0
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
runCampaign,
|
|
14
14
|
summarizeAgentReceiptIntegrity,
|
|
15
15
|
tryAcquireAtomicFileLock
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WED5KLZQ.js";
|
|
17
17
|
import {
|
|
18
18
|
clamp01,
|
|
19
19
|
combineAbortSignals,
|
|
@@ -21,17 +21,21 @@ import {
|
|
|
21
21
|
} from "./chunk-WGXIEX7P.js";
|
|
22
22
|
import {
|
|
23
23
|
detectRewardHacking
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-424XHVKV.js";
|
|
25
|
+
import {
|
|
26
|
+
campaignCellExecutionEvidence,
|
|
27
|
+
projectCampaignCellQuality
|
|
28
|
+
} from "./chunk-ZPUEEM5U.js";
|
|
25
29
|
import {
|
|
26
30
|
costReceiptFromLlm,
|
|
27
31
|
costReceiptFromLlmError,
|
|
28
32
|
maximumChargeForLlmRequest,
|
|
29
33
|
stripFencedJson
|
|
30
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-PBE2LOSS.js";
|
|
31
35
|
import {
|
|
32
36
|
pairedBootstrap,
|
|
33
37
|
weightedComposite
|
|
34
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-MHELPNRP.js";
|
|
35
39
|
import {
|
|
36
40
|
CostLedger,
|
|
37
41
|
costForTokenPricing
|
|
@@ -1199,11 +1203,11 @@ function renderPrBody(result, gate, diff) {
|
|
|
1199
1203
|
lines.push("");
|
|
1200
1204
|
lines.push("### Contributing gates");
|
|
1201
1205
|
lines.push("");
|
|
1202
|
-
lines.push("| gate |
|
|
1206
|
+
lines.push("| gate | status | detail |");
|
|
1203
1207
|
lines.push("|---|---|---|");
|
|
1204
1208
|
for (const c of gate.contributingGates) {
|
|
1205
1209
|
const detail = typeof c.detail === "object" ? JSON.stringify(c.detail).slice(0, 80) : String(c.detail).slice(0, 80);
|
|
1206
|
-
lines.push(`| ${c.name} | ${c.
|
|
1210
|
+
lines.push(`| ${c.name} | ${c.status} | ${detail} |`);
|
|
1207
1211
|
}
|
|
1208
1212
|
lines.push("");
|
|
1209
1213
|
lines.push("### Promoted surface");
|
|
@@ -1505,14 +1509,18 @@ function parseReflectionResponse(raw, maxProposals) {
|
|
|
1505
1509
|
|
|
1506
1510
|
// src/campaign/score-utils.ts
|
|
1507
1511
|
function campaignMeanComposite(campaign) {
|
|
1508
|
-
const
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
if (cellComposites.length > 0) {
|
|
1512
|
-
composites.push(cellComposites.reduce((a, b) => a + b, 0) / cellComposites.length);
|
|
1513
|
-
}
|
|
1512
|
+
const mean3 = campaignMeanCompositeOrNull(campaign);
|
|
1513
|
+
if (mean3 === null) {
|
|
1514
|
+
throw new Error("campaignMeanComposite: campaign has no complete cell-quality scores");
|
|
1514
1515
|
}
|
|
1515
|
-
return
|
|
1516
|
+
return mean3;
|
|
1517
|
+
}
|
|
1518
|
+
function campaignMeanCompositeOrNull(campaign) {
|
|
1519
|
+
const scores = campaign.cells.flatMap((cell) => {
|
|
1520
|
+
const score = projectCampaignCellQuality(cell).score;
|
|
1521
|
+
return score === void 0 ? [] : [score];
|
|
1522
|
+
});
|
|
1523
|
+
return scores.length === 0 ? null : scores.reduce((sum, score) => sum + score, 0) / scores.length;
|
|
1516
1524
|
}
|
|
1517
1525
|
function assertFiniteRankKey(key, label, expectedLength) {
|
|
1518
1526
|
if (!Array.isArray(key) || key.length === 0) {
|
|
@@ -1544,11 +1552,10 @@ function campaignBreakdown(campaign) {
|
|
|
1544
1552
|
const notesByScenario = /* @__PURE__ */ new Map();
|
|
1545
1553
|
const emittedByScenario = /* @__PURE__ */ new Map();
|
|
1546
1554
|
for (const cell of campaign.cells) {
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
);
|
|
1550
|
-
|
|
1551
|
-
const cellComposite = judgeScores.reduce((a, s) => a + s.composite, 0) / judgeScores.length;
|
|
1555
|
+
const quality = projectCampaignCellQuality(cell);
|
|
1556
|
+
if (quality.score === void 0) continue;
|
|
1557
|
+
const judgeScores = Object.values(quality.successfulJudgeScores);
|
|
1558
|
+
const cellComposite = quality.score;
|
|
1552
1559
|
const arr = byScenario.get(cell.scenarioId) ?? [];
|
|
1553
1560
|
arr.push(cellComposite);
|
|
1554
1561
|
byScenario.set(cell.scenarioId, arr);
|
|
@@ -1981,7 +1988,7 @@ function assertOptimizationProvenance(methodName, value) {
|
|
|
1981
1988
|
);
|
|
1982
1989
|
};
|
|
1983
1990
|
if (!value || typeof value !== "object") fail("value");
|
|
1984
|
-
if (
|
|
1991
|
+
if (value.source?.kind !== "package" || !["observed", "declared"].includes(value.source.evidence) || typeof value.source.package !== "string" || !value.source.package.trim() || typeof value.source.version !== "string" || !value.source.version.trim()) {
|
|
1985
1992
|
fail("source");
|
|
1986
1993
|
}
|
|
1987
1994
|
for (const [field, entry] of [
|
|
@@ -3671,7 +3678,7 @@ function assertExternalTextOptimizationConfig(config) {
|
|
|
3671
3678
|
"externalTextOptimizationMethod: maxOptimizerCostUsd must be finite and non-negative"
|
|
3672
3679
|
);
|
|
3673
3680
|
}
|
|
3674
|
-
if (
|
|
3681
|
+
if (config.source?.kind !== "package" || typeof config.source.package !== "string" || !config.source.package.trim() || config.source.package.trim() !== config.source.package || typeof config.source.version !== "string" || !config.source.version.trim() || config.source.version.trim() !== config.source.version) {
|
|
3675
3682
|
throw new Error("externalTextOptimizationMethod: source must identify a package and version");
|
|
3676
3683
|
}
|
|
3677
3684
|
for (const [field, value] of [
|
|
@@ -4213,7 +4220,13 @@ function composeGate(...gates) {
|
|
|
4213
4220
|
const decisions = results.map((r) => r.res.decision);
|
|
4214
4221
|
const overall = decisions.every((d) => d === "ship") ? "ship" : decisions.includes("arch_ceiling") ? "arch_ceiling" : decisions.includes("model_ceiling") ? "model_ceiling" : decisions.includes("hold") ? "hold" : "need_more_work";
|
|
4215
4222
|
const contributing = results.flatMap(
|
|
4216
|
-
(r) => r.res.contributingGates.length > 0 ? r.res.contributingGates : [
|
|
4223
|
+
(r) => r.res.contributingGates.length > 0 ? r.res.contributingGates : [
|
|
4224
|
+
{
|
|
4225
|
+
name: r.gate.name,
|
|
4226
|
+
status: r.res.decision === "ship" ? "pass" : r.res.decision === "need_more_work" ? "not_evaluated" : "fail",
|
|
4227
|
+
detail: r.res
|
|
4228
|
+
}
|
|
4229
|
+
]
|
|
4217
4230
|
);
|
|
4218
4231
|
const reasons = results.flatMap(
|
|
4219
4232
|
(r) => r.res.reasons.map((reason) => `[${r.gate.name}] ${reason}`)
|
|
@@ -4230,28 +4243,36 @@ function composeGate(...gates) {
|
|
|
4230
4243
|
|
|
4231
4244
|
// src/canary.ts
|
|
4232
4245
|
function runCanaries(runs, opts = {}) {
|
|
4233
|
-
const
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
...opts.distributionShift ? detectDistributionShift(runs, opts.distributionShift) : []
|
|
4246
|
+
const detections = [
|
|
4247
|
+
detectSilentFallback(runs, opts.silentFallback ?? {}),
|
|
4248
|
+
detectCalibrationDrift(runs, opts.calibrationDrift ?? {})
|
|
4237
4249
|
];
|
|
4250
|
+
if (opts.distributionShift) {
|
|
4251
|
+
detections.push(detectDistributionShift(runs, opts.distributionShift));
|
|
4252
|
+
}
|
|
4253
|
+
const alerts = detections.flatMap((detection) => detection.alerts);
|
|
4238
4254
|
const counts = {
|
|
4239
4255
|
silent_judge_fallback: 0,
|
|
4240
4256
|
judge_calibration_drift: 0,
|
|
4241
4257
|
distribution_shift: 0
|
|
4242
4258
|
};
|
|
4243
4259
|
for (const a of alerts) counts[a.kind]++;
|
|
4244
|
-
return {
|
|
4260
|
+
return {
|
|
4261
|
+
alerts,
|
|
4262
|
+
counts,
|
|
4263
|
+
evaluations: detections.map((detection) => detection.evaluation)
|
|
4264
|
+
};
|
|
4245
4265
|
}
|
|
4246
4266
|
function detectSilentFallback(runs, opts) {
|
|
4247
4267
|
const constant = opts.constant ?? 0.3;
|
|
4248
4268
|
const threshold = opts.consecutiveThreshold ?? 3;
|
|
4249
4269
|
const eps = opts.epsilon ?? 1e-9;
|
|
4250
4270
|
const alerts = [];
|
|
4271
|
+
let observations = 0;
|
|
4251
4272
|
let streak = 0;
|
|
4252
4273
|
let streakStartRunId = null;
|
|
4253
4274
|
let streakValues = [];
|
|
4254
|
-
let
|
|
4275
|
+
let reportedInStreak = false;
|
|
4255
4276
|
for (let i = 0; i < runs.length; i++) {
|
|
4256
4277
|
const run = runs[i];
|
|
4257
4278
|
const meta = run.judgeMetadata;
|
|
@@ -4259,14 +4280,16 @@ function detectSilentFallback(runs, opts) {
|
|
|
4259
4280
|
streak = 0;
|
|
4260
4281
|
streakStartRunId = null;
|
|
4261
4282
|
streakValues = [];
|
|
4283
|
+
reportedInStreak = false;
|
|
4262
4284
|
continue;
|
|
4263
4285
|
}
|
|
4286
|
+
observations += 1;
|
|
4264
4287
|
const isFallback = meta.fallback === true || Math.abs(meta.confidence - constant) <= eps;
|
|
4265
4288
|
if (isFallback) {
|
|
4266
4289
|
streak += 1;
|
|
4267
4290
|
if (streak === 1) streakStartRunId = run.runId;
|
|
4268
4291
|
streakValues.push(meta.confidence);
|
|
4269
|
-
if (streak >= threshold &&
|
|
4292
|
+
if (streak >= threshold && !reportedInStreak) {
|
|
4270
4293
|
alerts.push({
|
|
4271
4294
|
kind: "silent_judge_fallback",
|
|
4272
4295
|
severity: "error",
|
|
@@ -4279,16 +4302,28 @@ function detectSilentFallback(runs, opts) {
|
|
|
4279
4302
|
fallbackConstant: constant
|
|
4280
4303
|
}
|
|
4281
4304
|
});
|
|
4282
|
-
|
|
4305
|
+
reportedInStreak = true;
|
|
4283
4306
|
}
|
|
4284
4307
|
} else {
|
|
4285
4308
|
streak = 0;
|
|
4286
4309
|
streakStartRunId = null;
|
|
4287
4310
|
streakValues = [];
|
|
4288
|
-
|
|
4311
|
+
reportedInStreak = false;
|
|
4289
4312
|
}
|
|
4290
4313
|
}
|
|
4291
|
-
return
|
|
4314
|
+
return {
|
|
4315
|
+
alerts,
|
|
4316
|
+
evaluation: observations >= threshold ? {
|
|
4317
|
+
kind: "silent_judge_fallback",
|
|
4318
|
+
status: "evaluated",
|
|
4319
|
+
observations
|
|
4320
|
+
} : {
|
|
4321
|
+
kind: "silent_judge_fallback",
|
|
4322
|
+
status: "not_evaluated",
|
|
4323
|
+
observations,
|
|
4324
|
+
reason: `requires at least ${threshold} run(s) with judge metadata`
|
|
4325
|
+
}
|
|
4326
|
+
};
|
|
4292
4327
|
}
|
|
4293
4328
|
function detectCalibrationDrift(runs, opts) {
|
|
4294
4329
|
const historyWindow = opts.historyWindow ?? 50;
|
|
@@ -4301,32 +4336,55 @@ function detectCalibrationDrift(runs, opts) {
|
|
|
4301
4336
|
conf.push(r.judgeMetadata.confidence);
|
|
4302
4337
|
}
|
|
4303
4338
|
}
|
|
4304
|
-
if (conf.length < minRecent + 1) return [];
|
|
4305
4339
|
const recent = conf.slice(-Math.min(recentWindow, conf.length));
|
|
4306
4340
|
const historical = conf.slice(0, -recent.length).slice(-historyWindow);
|
|
4307
|
-
if (recent.length < minRecent || historical.length < minRecent)
|
|
4341
|
+
if (recent.length < minRecent || historical.length < minRecent) {
|
|
4342
|
+
return {
|
|
4343
|
+
alerts: [],
|
|
4344
|
+
evaluation: {
|
|
4345
|
+
kind: "judge_calibration_drift",
|
|
4346
|
+
status: "not_evaluated",
|
|
4347
|
+
observations: conf.length,
|
|
4348
|
+
reason: `requires at least ${minRecent} recent and ${minRecent} historical confidence observation(s); found ${recent.length} recent and ${historical.length} historical`
|
|
4349
|
+
}
|
|
4350
|
+
};
|
|
4351
|
+
}
|
|
4308
4352
|
const ks = ksTwoSample(recent, historical);
|
|
4309
4353
|
const c = alpha <= 0.01 ? 1.63 : alpha <= 0.05 ? 1.36 : alpha <= 0.1 ? 1.22 : 1;
|
|
4310
4354
|
const critical = c * Math.sqrt((recent.length + historical.length) / (recent.length * historical.length));
|
|
4311
4355
|
if (ks.d > critical) {
|
|
4312
|
-
return
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4356
|
+
return {
|
|
4357
|
+
alerts: [
|
|
4358
|
+
{
|
|
4359
|
+
kind: "judge_calibration_drift",
|
|
4360
|
+
severity: "warn",
|
|
4361
|
+
message: `judge calibration drift: KS D=${ks.d.toFixed(4)} exceeds critical=${critical.toFixed(4)} at alpha=${alpha} (recent n=${recent.length}, history n=${historical.length})`,
|
|
4362
|
+
evidence: {
|
|
4363
|
+
ksD: ks.d,
|
|
4364
|
+
critical,
|
|
4365
|
+
alpha,
|
|
4366
|
+
recentN: recent.length,
|
|
4367
|
+
historyN: historical.length,
|
|
4368
|
+
recentMean: mean2(recent),
|
|
4369
|
+
historyMean: mean2(historical)
|
|
4370
|
+
}
|
|
4325
4371
|
}
|
|
4372
|
+
],
|
|
4373
|
+
evaluation: {
|
|
4374
|
+
kind: "judge_calibration_drift",
|
|
4375
|
+
status: "evaluated",
|
|
4376
|
+
observations: recent.length + historical.length
|
|
4326
4377
|
}
|
|
4327
|
-
|
|
4378
|
+
};
|
|
4328
4379
|
}
|
|
4329
|
-
return
|
|
4380
|
+
return {
|
|
4381
|
+
alerts: [],
|
|
4382
|
+
evaluation: {
|
|
4383
|
+
kind: "judge_calibration_drift",
|
|
4384
|
+
status: "evaluated",
|
|
4385
|
+
observations: recent.length + historical.length
|
|
4386
|
+
}
|
|
4387
|
+
};
|
|
4330
4388
|
}
|
|
4331
4389
|
function ksTwoSample(a, b) {
|
|
4332
4390
|
const sortedA = [...a].sort((x, y) => x - y);
|
|
@@ -4357,10 +4415,19 @@ function detectDistributionShift(runs, opts) {
|
|
|
4357
4415
|
const b = cat(r);
|
|
4358
4416
|
if (typeof b === "string" && b.length > 0) cats.push({ run: r, bucket: b });
|
|
4359
4417
|
}
|
|
4360
|
-
if (cats.length < minRecent + 1) return [];
|
|
4361
4418
|
const recent = cats.slice(-Math.min(recentWindow, cats.length));
|
|
4362
4419
|
const historical = cats.slice(0, -recent.length).slice(-historyWindow);
|
|
4363
|
-
if (recent.length < minRecent || historical.length < minRecent)
|
|
4420
|
+
if (recent.length < minRecent || historical.length < minRecent) {
|
|
4421
|
+
return {
|
|
4422
|
+
alerts: [],
|
|
4423
|
+
evaluation: {
|
|
4424
|
+
kind: "distribution_shift",
|
|
4425
|
+
status: "not_evaluated",
|
|
4426
|
+
observations: cats.length,
|
|
4427
|
+
reason: `requires at least ${minRecent} recent and ${minRecent} historical categorized run(s); found ${recent.length} recent and ${historical.length} historical`
|
|
4428
|
+
}
|
|
4429
|
+
};
|
|
4430
|
+
}
|
|
4364
4431
|
const buckets = /* @__PURE__ */ new Set();
|
|
4365
4432
|
for (const r of recent) buckets.add(r.bucket);
|
|
4366
4433
|
for (const h of historical) buckets.add(h.bucket);
|
|
@@ -4385,25 +4452,39 @@ function detectDistributionShift(runs, opts) {
|
|
|
4385
4452
|
df = Math.max(1, df - 1);
|
|
4386
4453
|
const critical = chiSquareCritical(df, alpha);
|
|
4387
4454
|
if (chi > critical) {
|
|
4388
|
-
return
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4455
|
+
return {
|
|
4456
|
+
alerts: [
|
|
4457
|
+
{
|
|
4458
|
+
kind: "distribution_shift",
|
|
4459
|
+
severity: "warn",
|
|
4460
|
+
message: `eval-set distribution shift: \u03C7\xB2=${chi.toFixed(2)} df=${df} exceeds critical=${critical.toFixed(2)} at alpha=${alpha}`,
|
|
4461
|
+
evidence: {
|
|
4462
|
+
chi,
|
|
4463
|
+
df,
|
|
4464
|
+
critical,
|
|
4465
|
+
alpha,
|
|
4466
|
+
recentCounts,
|
|
4467
|
+
historicalCounts: histCounts,
|
|
4468
|
+
recentN: recent.length,
|
|
4469
|
+
historyN: historical.length
|
|
4470
|
+
}
|
|
4402
4471
|
}
|
|
4472
|
+
],
|
|
4473
|
+
evaluation: {
|
|
4474
|
+
kind: "distribution_shift",
|
|
4475
|
+
status: "evaluated",
|
|
4476
|
+
observations: recent.length + historical.length
|
|
4403
4477
|
}
|
|
4404
|
-
|
|
4478
|
+
};
|
|
4405
4479
|
}
|
|
4406
|
-
return
|
|
4480
|
+
return {
|
|
4481
|
+
alerts: [],
|
|
4482
|
+
evaluation: {
|
|
4483
|
+
kind: "distribution_shift",
|
|
4484
|
+
status: "evaluated",
|
|
4485
|
+
observations: recent.length + historical.length
|
|
4486
|
+
}
|
|
4487
|
+
};
|
|
4407
4488
|
}
|
|
4408
4489
|
function chiSquareCritical(df, alpha) {
|
|
4409
4490
|
const TABLE = {
|
|
@@ -4555,150 +4636,289 @@ function defaultProductionGate(options) {
|
|
|
4555
4636
|
const seed = options.bootstrapSeed ?? 1337;
|
|
4556
4637
|
const minProductiveRuns = options.minProductiveRuns ?? 3;
|
|
4557
4638
|
const heldoutStatistic = options.heldoutStatistic ?? "mean";
|
|
4558
|
-
const
|
|
4639
|
+
const explicitlyRequired = new Set(options.requiredChecks ?? []);
|
|
4559
4640
|
return {
|
|
4560
4641
|
name: "defaultProductionGate",
|
|
4561
4642
|
async decide(ctx) {
|
|
4562
4643
|
const reasons = [];
|
|
4563
4644
|
const contributing = [];
|
|
4645
|
+
const requiredUnavailable = /* @__PURE__ */ new Set();
|
|
4646
|
+
const unavailable = (name, required, reason, detail = {}) => {
|
|
4647
|
+
contributing.push({
|
|
4648
|
+
name,
|
|
4649
|
+
status: "not_evaluated",
|
|
4650
|
+
detail: { reason, required, ...detail }
|
|
4651
|
+
});
|
|
4652
|
+
if (required) {
|
|
4653
|
+
requiredUnavailable.add(name);
|
|
4654
|
+
reasons.push(`${name}: ${reason}`);
|
|
4655
|
+
}
|
|
4656
|
+
};
|
|
4564
4657
|
const scenarioIds = new Set(options.holdoutScenarios.map((s) => s.id));
|
|
4565
|
-
|
|
4566
|
-
|
|
4658
|
+
let delta;
|
|
4659
|
+
if (!ctx.baselineJudgeScores) {
|
|
4660
|
+
unavailable(
|
|
4661
|
+
"heldout-significance",
|
|
4662
|
+
true,
|
|
4663
|
+
"baselineJudgeScores is required for a held-out comparison"
|
|
4664
|
+
);
|
|
4665
|
+
} else {
|
|
4666
|
+
const sig = heldoutSignificance(
|
|
4667
|
+
pairHoldout(ctx.judgeScores, ctx.baselineJudgeScores, scenarioIds, (s) => s.composite),
|
|
4668
|
+
{
|
|
4669
|
+
deltaThreshold,
|
|
4670
|
+
minProductiveRuns,
|
|
4671
|
+
confidence,
|
|
4672
|
+
resamples,
|
|
4673
|
+
seed,
|
|
4674
|
+
statistic: heldoutStatistic
|
|
4675
|
+
}
|
|
4676
|
+
);
|
|
4677
|
+
delta = heldoutStatistic === "median" ? sig.bootstrap.median : sig.bootstrap.mean;
|
|
4678
|
+
const heldoutPass = sig.significant;
|
|
4679
|
+
contributing.push({
|
|
4680
|
+
name: "heldout-significance",
|
|
4681
|
+
status: sig.fewRuns ? "not_evaluated" : heldoutPass ? "pass" : "fail",
|
|
4682
|
+
detail: {
|
|
4683
|
+
n: sig.n,
|
|
4684
|
+
delta,
|
|
4685
|
+
deltaMean: sig.bootstrap.mean,
|
|
4686
|
+
deltaMedianDiagnostic: sig.medianBootstrap.median,
|
|
4687
|
+
deltaMedian: sig.medianBootstrap.median,
|
|
4688
|
+
tieFraction: sig.tieFraction,
|
|
4689
|
+
ciLow: sig.bootstrap.low,
|
|
4690
|
+
ciHigh: sig.bootstrap.high,
|
|
4691
|
+
confidence: sig.bootstrap.confidence,
|
|
4692
|
+
deltaThreshold,
|
|
4693
|
+
fewRuns: sig.fewRuns
|
|
4694
|
+
}
|
|
4695
|
+
});
|
|
4696
|
+
if (sig.fewRuns) {
|
|
4697
|
+
requiredUnavailable.add("heldout-significance");
|
|
4698
|
+
}
|
|
4699
|
+
if (!heldoutPass) {
|
|
4700
|
+
const tieNote = sig.tieFraction >= TIE_WARN_FRACTION ? `; ${(sig.tieFraction * 100).toFixed(0)}% tied scenarios` : "";
|
|
4701
|
+
reasons.push(
|
|
4702
|
+
sig.fewRuns ? `held-out: only ${sig.n} paired runs (< ${minProductiveRuns}) \u2014 too few to claim significance` : `held-out CI.low ${sig.bootstrap.low.toFixed(3)} \u2264 threshold ${deltaThreshold} (${heldoutStatistic} \u0394 ${delta.toFixed(3)}, ${(sig.bootstrap.confidence * 100).toFixed(0)}% CI [${sig.bootstrap.low.toFixed(3)}, ${sig.bootstrap.high.toFixed(3)}]${tieNote})`
|
|
4703
|
+
);
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
const dimensionsProvided = options.criticalDimensions !== void 0;
|
|
4707
|
+
const criticalDimensions = [...new Set(options.criticalDimensions ?? [])];
|
|
4708
|
+
const dimensionsRequired = dimensionsProvided || explicitlyRequired.has("dimension-regression");
|
|
4709
|
+
if (!dimensionsProvided) {
|
|
4710
|
+
unavailable(
|
|
4711
|
+
"dimension-regression",
|
|
4712
|
+
dimensionsRequired,
|
|
4713
|
+
"criticalDimensions is not configured"
|
|
4714
|
+
);
|
|
4715
|
+
} else if (criticalDimensions.length === 0) {
|
|
4716
|
+
unavailable(
|
|
4717
|
+
"dimension-regression",
|
|
4718
|
+
true,
|
|
4719
|
+
"criticalDimensions must contain at least one dimension"
|
|
4720
|
+
);
|
|
4721
|
+
} else if (!ctx.baselineJudgeScores) {
|
|
4722
|
+
unavailable(
|
|
4723
|
+
"dimension-regression",
|
|
4724
|
+
true,
|
|
4725
|
+
"baselineJudgeScores is required to measure dimension regressions",
|
|
4726
|
+
{ guarded: criticalDimensions }
|
|
4727
|
+
);
|
|
4728
|
+
} else {
|
|
4729
|
+
const dimRegs = dimensionRegressions(
|
|
4567
4730
|
ctx.judgeScores,
|
|
4568
|
-
ctx.baselineJudgeScores
|
|
4731
|
+
ctx.baselineJudgeScores,
|
|
4569
4732
|
scenarioIds,
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4733
|
+
criticalDimensions,
|
|
4734
|
+
{ tolerance: options.regressionTolerance, confidence, resamples, seed }
|
|
4735
|
+
);
|
|
4736
|
+
const measured = new Set(dimRegs.map((result) => result.dimension));
|
|
4737
|
+
const missingDimensions = criticalDimensions.filter((dimension) => !measured.has(dimension));
|
|
4738
|
+
const regressed = dimRegs.filter((result) => result.regressed);
|
|
4739
|
+
const dimensionStatus = regressed.length > 0 ? "fail" : missingDimensions.length > 0 ? "not_evaluated" : "pass";
|
|
4740
|
+
contributing.push({
|
|
4741
|
+
name: "dimension-regression",
|
|
4742
|
+
status: dimensionStatus,
|
|
4743
|
+
detail: {
|
|
4744
|
+
guarded: criticalDimensions,
|
|
4745
|
+
missingDimensions,
|
|
4746
|
+
regressions: dimRegs.map((result) => ({
|
|
4747
|
+
dimension: result.dimension,
|
|
4748
|
+
ciLow: result.bootstrap.low,
|
|
4749
|
+
median: result.bootstrap.median,
|
|
4750
|
+
tolerance: result.tolerance,
|
|
4751
|
+
n: result.n,
|
|
4752
|
+
regressed: result.regressed
|
|
4753
|
+
}))
|
|
4754
|
+
}
|
|
4755
|
+
});
|
|
4756
|
+
if (missingDimensions.length > 0) {
|
|
4757
|
+
requiredUnavailable.add("dimension-regression");
|
|
4758
|
+
reasons.push(`critical dimension(s) were not scored: ${missingDimensions.join(", ")}`);
|
|
4579
4759
|
}
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
name: "heldout-significance",
|
|
4585
|
-
passed: heldoutPass,
|
|
4586
|
-
detail: {
|
|
4587
|
-
n: sig.n,
|
|
4588
|
-
delta,
|
|
4589
|
-
deltaMean: sig.bootstrap.mean,
|
|
4590
|
-
deltaMedianDiagnostic: sig.medianBootstrap.median,
|
|
4591
|
-
// Back-compat: prior consumers read `deltaMedian`. It now always carries
|
|
4592
|
-
// the median diagnostic (the ship decision keys on `delta`/mean).
|
|
4593
|
-
deltaMedian: sig.medianBootstrap.median,
|
|
4594
|
-
tieFraction: sig.tieFraction,
|
|
4595
|
-
ciLow: sig.bootstrap.low,
|
|
4596
|
-
ciHigh: sig.bootstrap.high,
|
|
4597
|
-
confidence: sig.bootstrap.confidence,
|
|
4598
|
-
deltaThreshold,
|
|
4599
|
-
fewRuns: sig.fewRuns
|
|
4760
|
+
if (regressed.length > 0) {
|
|
4761
|
+
reasons.push(
|
|
4762
|
+
`critical dimension(s) regressed: ${regressed.map((result) => `${result.dimension} CI.low ${result.bootstrap.low.toFixed(3)} < -${result.tolerance}`).join("; ")}`
|
|
4763
|
+
);
|
|
4600
4764
|
}
|
|
4601
|
-
});
|
|
4602
|
-
if (!heldoutPass) {
|
|
4603
|
-
const tieNote = sig.tieFraction >= TIE_WARN_FRACTION ? `; ${(sig.tieFraction * 100).toFixed(0)}% tied scenarios` : "";
|
|
4604
|
-
reasons.push(
|
|
4605
|
-
sig.fewRuns ? `held-out: only ${sig.n} paired runs (< ${minProductiveRuns}) \u2014 too few to claim significance` : `held-out CI.low ${sig.bootstrap.low.toFixed(3)} \u2264 threshold ${deltaThreshold} (${heldoutStatistic} \u0394 ${delta.toFixed(3)}, ${(sig.bootstrap.confidence * 100).toFixed(0)}% CI [${sig.bootstrap.low.toFixed(3)}, ${sig.bootstrap.high.toFixed(3)}]${tieNote})`
|
|
4606
|
-
);
|
|
4607
4765
|
}
|
|
4608
|
-
const
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4766
|
+
const budgetUsd = options.budgetUsd;
|
|
4767
|
+
const budgetConfigured = budgetUsd !== void 0;
|
|
4768
|
+
const budgetRequired = budgetConfigured || explicitlyRequired.has("budget");
|
|
4769
|
+
if (!budgetConfigured) {
|
|
4770
|
+
unavailable("budget", budgetRequired, "budgetUsd is not configured");
|
|
4771
|
+
} else if (!ctx.costLedger) {
|
|
4772
|
+
unavailable("budget", true, "costLedger is required to measure complete run spend", {
|
|
4773
|
+
budgetUsd
|
|
4774
|
+
});
|
|
4775
|
+
} else {
|
|
4776
|
+
const cost = ctx.costLedger.summary();
|
|
4777
|
+
const accountingComplete = cost.accountingComplete && cost.pendingCalls === 0 && cost.unresolvedCalls === 0;
|
|
4778
|
+
const budgetPass = cost.totalCostUsd <= budgetUsd;
|
|
4779
|
+
contributing.push({
|
|
4780
|
+
name: "budget",
|
|
4781
|
+
status: !accountingComplete ? "not_evaluated" : budgetPass ? "pass" : "fail",
|
|
4782
|
+
detail: {
|
|
4783
|
+
totalCostUsd: cost.totalCostUsd,
|
|
4784
|
+
budgetUsd,
|
|
4785
|
+
accountingComplete,
|
|
4786
|
+
pendingCalls: cost.pendingCalls,
|
|
4787
|
+
unresolvedCalls: cost.unresolvedCalls
|
|
4788
|
+
}
|
|
4789
|
+
});
|
|
4790
|
+
if (!accountingComplete) {
|
|
4791
|
+
requiredUnavailable.add("budget");
|
|
4792
|
+
reasons.push("budget: cost accounting is incomplete");
|
|
4793
|
+
} else if (!budgetPass) {
|
|
4794
|
+
reasons.push(`spend ${cost.totalCostUsd.toFixed(2)} > budget ${budgetUsd}`);
|
|
4630
4795
|
}
|
|
4631
|
-
});
|
|
4632
|
-
if (!dimPass) {
|
|
4633
|
-
reasons.push(
|
|
4634
|
-
`critical dimension(s) regressed: ${regressed.map((d) => `${d.dimension} CI.low ${d.bootstrap.low.toFixed(3)} < -${d.tolerance}`).join("; ")}`
|
|
4635
|
-
);
|
|
4636
4796
|
}
|
|
4637
|
-
const
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4797
|
+
const redTeamConfigured = options.redTeamBattery !== void 0;
|
|
4798
|
+
const redTeamRequired = redTeamConfigured || explicitlyRequired.has("red-team");
|
|
4799
|
+
if (!options.redTeamBattery) {
|
|
4800
|
+
unavailable("red-team", redTeamRequired, "redTeamBattery is not configured");
|
|
4801
|
+
} else if (options.redTeamBattery.length === 0) {
|
|
4802
|
+
unavailable("red-team", true, "redTeamBattery must contain at least one case");
|
|
4803
|
+
} else {
|
|
4804
|
+
const redTeam = probeRedTeam(ctx.candidateArtifacts, options.redTeamBattery);
|
|
4805
|
+
const incomplete = redTeam.unsupportedCases.length > 0 || redTeam.unscoredArtifacts.length > 0 || redTeam.evaluatedCases === 0;
|
|
4806
|
+
const redTeamStatus = redTeam.findings.length > 0 ? "fail" : incomplete ? "not_evaluated" : "pass";
|
|
4807
|
+
contributing.push({
|
|
4808
|
+
name: "red-team",
|
|
4809
|
+
status: redTeamStatus,
|
|
4810
|
+
detail: {
|
|
4811
|
+
evaluatedCases: redTeam.evaluatedCases,
|
|
4812
|
+
unsupportedCases: redTeam.unsupportedCases,
|
|
4813
|
+
unscoredArtifacts: redTeam.unscoredArtifacts,
|
|
4814
|
+
failures: redTeam.findings.length,
|
|
4815
|
+
sample: redTeam.findings.slice(0, 3)
|
|
4816
|
+
}
|
|
4817
|
+
});
|
|
4818
|
+
if (incomplete) {
|
|
4819
|
+
requiredUnavailable.add("red-team");
|
|
4645
4820
|
}
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4821
|
+
if (redTeam.unsupportedCases.length > 0) {
|
|
4822
|
+
reasons.push(
|
|
4823
|
+
`red-team: static artifact screening cannot evaluate case(s): ${redTeam.unsupportedCases.join(", ")}`
|
|
4824
|
+
);
|
|
4825
|
+
}
|
|
4826
|
+
if (redTeam.unscoredArtifacts.length > 0) {
|
|
4827
|
+
reasons.push(
|
|
4828
|
+
`red-team: no text could be extracted from artifact(s): ${redTeam.unscoredArtifacts.join(", ")}`
|
|
4829
|
+
);
|
|
4830
|
+
}
|
|
4831
|
+
if (redTeam.evaluatedCases === 0) {
|
|
4832
|
+
reasons.push("red-team: no artifact/case pair was evaluated");
|
|
4833
|
+
}
|
|
4834
|
+
if (redTeam.findings.length > 0) {
|
|
4835
|
+
reasons.push(`red-team probe failed (${redTeam.findings.length} findings)`);
|
|
4659
4836
|
}
|
|
4660
|
-
});
|
|
4661
|
-
if (!redTeamFindings.passed) {
|
|
4662
|
-
reasons.push(`red-team probe failed (${redTeamFindings.findings.length} findings)`);
|
|
4663
|
-
}
|
|
4664
|
-
let rewardHackingReport = null;
|
|
4665
|
-
if (options.recentRuns && options.recentRuns.length >= 10) {
|
|
4666
|
-
rewardHackingReport = detectRewardHacking({ runs: options.recentRuns });
|
|
4667
4837
|
}
|
|
4668
|
-
const
|
|
4669
|
-
const
|
|
4670
|
-
|
|
4671
|
-
)
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
`reward-hacking detector flagged ${gamingFindings.length} gaming-severity findings (verdict=${rewardHackingReport.verdict})`
|
|
4838
|
+
const rewardHacking = options.rewardHacking;
|
|
4839
|
+
const rewardHackingConfigured = rewardHacking !== void 0;
|
|
4840
|
+
const rewardHackingRequired = rewardHackingConfigured || explicitlyRequired.has("reward-hacking");
|
|
4841
|
+
if (!rewardHackingConfigured) {
|
|
4842
|
+
unavailable("reward-hacking", rewardHackingRequired, "rewardHacking is not configured");
|
|
4843
|
+
} else if (!options.recentRuns) {
|
|
4844
|
+
unavailable("reward-hacking", rewardHackingRequired, "recentRuns is not configured");
|
|
4845
|
+
} else if (options.recentRuns.length < 10) {
|
|
4846
|
+
unavailable(
|
|
4847
|
+
"reward-hacking",
|
|
4848
|
+
rewardHackingRequired,
|
|
4849
|
+
`recentRuns has ${options.recentRuns.length} record(s); at least 10 are required`
|
|
4681
4850
|
);
|
|
4851
|
+
} else {
|
|
4852
|
+
const rewardHackingReport = detectRewardHacking({
|
|
4853
|
+
runs: options.recentRuns,
|
|
4854
|
+
...rewardHacking
|
|
4855
|
+
});
|
|
4856
|
+
if (!rewardHackingReport.evaluatedSignals.includes("reward_divergence")) {
|
|
4857
|
+
unavailable(
|
|
4858
|
+
"reward-hacking",
|
|
4859
|
+
true,
|
|
4860
|
+
"independent proxy/truth evidence is insufficient to evaluate reward divergence",
|
|
4861
|
+
{ report: rewardHackingReport }
|
|
4862
|
+
);
|
|
4863
|
+
} else {
|
|
4864
|
+
const gamingThreshold = rewardHacking.thresholds?.gaming ?? 0.6;
|
|
4865
|
+
const gamingFindings = rewardHackingReport.findings.filter(
|
|
4866
|
+
(finding) => finding.severity >= gamingThreshold
|
|
4867
|
+
);
|
|
4868
|
+
const rewardHackingPass = gamingFindings.length === 0 && rewardHackingReport.verdict !== "gaming";
|
|
4869
|
+
contributing.push({
|
|
4870
|
+
name: "reward-hacking",
|
|
4871
|
+
status: rewardHackingPass ? "pass" : "fail",
|
|
4872
|
+
detail: { report: rewardHackingReport, gamingFindingCount: gamingFindings.length }
|
|
4873
|
+
});
|
|
4874
|
+
if (!rewardHackingPass) {
|
|
4875
|
+
reasons.push(
|
|
4876
|
+
`reward-hacking detector flagged ${gamingFindings.length} gaming-severity findings (verdict=${rewardHackingReport.verdict})`
|
|
4877
|
+
);
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4682
4880
|
}
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4881
|
+
const canary = options.canary;
|
|
4882
|
+
const canaryConfigured = canary !== void 0;
|
|
4883
|
+
const canaryRequired = canaryConfigured || explicitlyRequired.has("canary");
|
|
4884
|
+
if (!canaryConfigured && !canaryRequired) {
|
|
4885
|
+
unavailable("canary", false, "canary is not configured");
|
|
4886
|
+
} else if (!options.recentRuns) {
|
|
4887
|
+
unavailable("canary", canaryRequired, "recentRuns is not configured");
|
|
4888
|
+
} else {
|
|
4889
|
+
const canaryReport = runCanaries(options.recentRuns, canary ?? {});
|
|
4890
|
+
const incomplete = canaryReport.evaluations.filter(
|
|
4891
|
+
(evaluation) => evaluation.status === "not_evaluated"
|
|
4892
|
+
);
|
|
4893
|
+
if (incomplete.length > 0) {
|
|
4894
|
+
unavailable(
|
|
4895
|
+
"canary",
|
|
4896
|
+
true,
|
|
4897
|
+
`insufficient evidence for: ${incomplete.map((evaluation) => evaluation.kind).join(", ")}`,
|
|
4898
|
+
{ report: canaryReport }
|
|
4899
|
+
);
|
|
4900
|
+
} else {
|
|
4901
|
+
const errorAlerts = canaryReport.alerts.filter((alert) => alert.severity === "error");
|
|
4902
|
+
const canaryPass = errorAlerts.length === 0;
|
|
4903
|
+
contributing.push({
|
|
4904
|
+
name: "canary",
|
|
4905
|
+
status: canaryPass ? "pass" : "fail",
|
|
4906
|
+
detail: {
|
|
4907
|
+
totalAlerts: canaryReport.alerts.length,
|
|
4908
|
+
errorAlerts: errorAlerts.length,
|
|
4909
|
+
report: canaryReport
|
|
4910
|
+
}
|
|
4911
|
+
});
|
|
4912
|
+
if (!canaryPass) {
|
|
4913
|
+
reasons.push(`canary error alerts: ${errorAlerts.length}`);
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4696
4916
|
}
|
|
4697
|
-
const
|
|
4698
|
-
const
|
|
4917
|
+
const decision = contributing.some((check) => check.status === "fail") || requiredUnavailable.size > 0 ? "hold" : "ship";
|
|
4918
|
+
const notEvaluated = contributing.filter((check) => check.status === "not_evaluated").map((check) => check.name);
|
|
4699
4919
|
return {
|
|
4700
4920
|
decision,
|
|
4701
|
-
reasons: reasons.length > 0 ? reasons : ["all
|
|
4921
|
+
reasons: reasons.length > 0 ? reasons : notEvaluated.length > 0 ? [`evaluated checks passed; not evaluated: ${notEvaluated.join(", ")}`] : ["all checks passed"],
|
|
4702
4922
|
contributingGates: contributing,
|
|
4703
4923
|
delta
|
|
4704
4924
|
};
|
|
@@ -4706,18 +4926,32 @@ function defaultProductionGate(options) {
|
|
|
4706
4926
|
};
|
|
4707
4927
|
}
|
|
4708
4928
|
function probeRedTeam(artifacts, battery) {
|
|
4929
|
+
const supportedCases = battery.filter(
|
|
4930
|
+
(testCase) => testCase.payload.expected === "ignore" && !testCase.payload.forbiddenTools?.length
|
|
4931
|
+
);
|
|
4932
|
+
const unsupportedCases = battery.filter((testCase) => !supportedCases.includes(testCase)).map((testCase) => testCase.id);
|
|
4709
4933
|
const findings = [];
|
|
4710
|
-
|
|
4934
|
+
const unscoredArtifacts = [];
|
|
4935
|
+
let evaluatedCases = 0;
|
|
4936
|
+
for (const [cellId, artifact] of artifacts) {
|
|
4711
4937
|
const text = extractText(artifact);
|
|
4712
|
-
if (text === void 0)
|
|
4713
|
-
|
|
4938
|
+
if (text === void 0) {
|
|
4939
|
+
unscoredArtifacts.push(cellId);
|
|
4940
|
+
continue;
|
|
4941
|
+
}
|
|
4942
|
+
for (const rtCase of supportedCases) {
|
|
4943
|
+
evaluatedCases += 1;
|
|
4714
4944
|
const finding = scoreRedTeamOutput(text, [], rtCase);
|
|
4715
4945
|
if (!finding.passed) {
|
|
4716
|
-
findings.push({
|
|
4946
|
+
findings.push({
|
|
4947
|
+
cellId,
|
|
4948
|
+
scenarioId: rtCase.id,
|
|
4949
|
+
reason: finding.reason ?? "red-team probe failed"
|
|
4950
|
+
});
|
|
4717
4951
|
}
|
|
4718
4952
|
}
|
|
4719
4953
|
}
|
|
4720
|
-
return {
|
|
4954
|
+
return { evaluatedCases, findings, unsupportedCases, unscoredArtifacts };
|
|
4721
4955
|
}
|
|
4722
4956
|
function extractText(artifact) {
|
|
4723
4957
|
if (typeof artifact === "string") return artifact;
|
|
@@ -4758,6 +4992,7 @@ function heldOutGate(options) {
|
|
|
4758
4992
|
);
|
|
4759
4993
|
const delta = sig.bootstrap.mean;
|
|
4760
4994
|
const passed = sig.significant;
|
|
4995
|
+
const status = sig.fewRuns ? "not_evaluated" : passed ? "pass" : "fail";
|
|
4761
4996
|
const tieNote = sig.tieFraction >= TIE_WARN_FRACTION ? `, ${(sig.tieFraction * 100).toFixed(0)}% tied` : "";
|
|
4762
4997
|
const ci = `${(sig.bootstrap.confidence * 100).toFixed(0)}% CI [${sig.bootstrap.low.toFixed(3)}, ${sig.bootstrap.high.toFixed(3)}]`;
|
|
4763
4998
|
return {
|
|
@@ -4770,7 +5005,7 @@ function heldOutGate(options) {
|
|
|
4770
5005
|
contributingGates: [
|
|
4771
5006
|
{
|
|
4772
5007
|
name: "heldOutGate",
|
|
4773
|
-
|
|
5008
|
+
status,
|
|
4774
5009
|
detail: {
|
|
4775
5010
|
deltaMean: delta,
|
|
4776
5011
|
deltaMedianDiagnostic: sig.medianBootstrap.median,
|
|
@@ -4885,7 +5120,7 @@ function buildEvidenceVector(ctx, objectives, opts = {}) {
|
|
|
4885
5120
|
var paretoPolicy = (ev) => {
|
|
4886
5121
|
const contributingGates = ev.axes.map((ax) => ({
|
|
4887
5122
|
name: `objective:${ax.name}`,
|
|
4888
|
-
|
|
5123
|
+
status: ax.verdict === "regressed" ? "fail" : ax.verdict === "few_runs" ? "not_evaluated" : "pass",
|
|
4889
5124
|
detail: {
|
|
4890
5125
|
direction: ax.direction,
|
|
4891
5126
|
source: ax.source,
|
|
@@ -5871,19 +6106,21 @@ async function runOptimization(opts) {
|
|
|
5871
6106
|
dispatch: (scenario, ctx) => opts.dispatchWithSurface(surface, scenario, ctx),
|
|
5872
6107
|
runDir: `${opts.runDir}/gen-${gen}/candidate-${i}`
|
|
5873
6108
|
});
|
|
5874
|
-
const composite = campaignMeanComposite(campaign);
|
|
5875
|
-
const rankKey = selectionRankKey(campaign);
|
|
5876
|
-
assertFiniteRankKey(
|
|
5877
|
-
rankKey,
|
|
5878
|
-
`selectionRankKey for generation ${gen} candidate ${i}`,
|
|
5879
|
-
winnerRankKey.length
|
|
5880
|
-
);
|
|
5881
6109
|
const coverage = campaignCoverage(
|
|
5882
6110
|
campaign.cells,
|
|
5883
6111
|
opts.scenarios,
|
|
5884
6112
|
opts.reps ?? 1,
|
|
5885
6113
|
requireJudgeScore
|
|
5886
6114
|
);
|
|
6115
|
+
const composite = campaignMeanCompositeOrNull(campaign);
|
|
6116
|
+
const rankKey = coverage.complete ? selectionRankKey(campaign) : null;
|
|
6117
|
+
if (rankKey) {
|
|
6118
|
+
assertFiniteRankKey(
|
|
6119
|
+
rankKey,
|
|
6120
|
+
`selectionRankKey for generation ${gen} candidate ${i}`,
|
|
6121
|
+
winnerRankKey.length
|
|
6122
|
+
);
|
|
6123
|
+
}
|
|
5887
6124
|
return {
|
|
5888
6125
|
surfaceHash: hash,
|
|
5889
6126
|
surface,
|
|
@@ -5906,9 +6143,12 @@ async function runOptimization(opts) {
|
|
|
5906
6143
|
}
|
|
5907
6144
|
surfaceResults.sort((a, b) => {
|
|
5908
6145
|
if (a.coverage.complete !== b.coverage.complete) return a.coverage.complete ? -1 : 1;
|
|
5909
|
-
return compareRankKeys(b.rankKey, a.rankKey);
|
|
6146
|
+
if (a.rankKey && b.rankKey) return compareRankKeys(b.rankKey, a.rankKey);
|
|
6147
|
+
return a.surfaceHash.localeCompare(b.surfaceHash);
|
|
5910
6148
|
});
|
|
5911
|
-
const eligibleResults = surfaceResults.filter(
|
|
6149
|
+
const eligibleResults = surfaceResults.filter(
|
|
6150
|
+
(result) => result.coverage.complete && result.composite !== null && result.rankKey !== null
|
|
6151
|
+
);
|
|
5912
6152
|
const top = eligibleResults[0];
|
|
5913
6153
|
const promoted = top && compareRankKeys(top.rankKey, winnerRankKey) > 0 ? [top] : [];
|
|
5914
6154
|
if (promoted[0]) {
|
|
@@ -5928,7 +6168,7 @@ async function runOptimization(opts) {
|
|
|
5928
6168
|
const candidate = {
|
|
5929
6169
|
surfaceHash: s.surfaceHash,
|
|
5930
6170
|
composite: s.composite,
|
|
5931
|
-
ci95: [s.composite, s.composite],
|
|
6171
|
+
ci95: s.composite === null ? null : [s.composite, s.composite],
|
|
5932
6172
|
parentSurfaceHash,
|
|
5933
6173
|
parentComposite,
|
|
5934
6174
|
...s.coverage.complete ? { observedDeltaFromParent: s.composite - parentComposite } : {},
|
|
@@ -6109,7 +6349,7 @@ async function runImprovementLoop(opts) {
|
|
|
6109
6349
|
const dispatchTimeoutMs = opts.dispatchTimeoutMs ?? DEFAULT_DISPATCH_TIMEOUT_MS;
|
|
6110
6350
|
const optimization = await runOptimization({ ...opts, dispatchTimeoutMs, costLedger });
|
|
6111
6351
|
const winnerIsBaseline = optimization.winnerSurfaceHash === surfaceHash(opts.baselineSurface);
|
|
6112
|
-
const { runCampaign: runCampaign2 } = await import("./run-campaign-
|
|
6352
|
+
const { runCampaign: runCampaign2 } = await import("./run-campaign-3A2EBYBE.js");
|
|
6113
6353
|
const holdoutDeferred = (opts.holdout ?? "measured") === "deferred";
|
|
6114
6354
|
const baselineOnHoldout = holdoutDeferred ? await runCampaign2({
|
|
6115
6355
|
...opts,
|
|
@@ -6200,7 +6440,11 @@ async function runImprovementLoop(opts) {
|
|
|
6200
6440
|
"holdout deferred \u2014 improvement-set search completed without a held-out measurement; nothing to promote from this run"
|
|
6201
6441
|
],
|
|
6202
6442
|
contributingGates: [
|
|
6203
|
-
{
|
|
6443
|
+
{
|
|
6444
|
+
name: "holdout-deferred",
|
|
6445
|
+
status: "not_evaluated",
|
|
6446
|
+
detail: { holdout: "deferred" }
|
|
6447
|
+
}
|
|
6204
6448
|
]
|
|
6205
6449
|
} : winnerIsBaseline ? {
|
|
6206
6450
|
decision: "hold",
|
|
@@ -6208,7 +6452,7 @@ async function runImprovementLoop(opts) {
|
|
|
6208
6452
|
"no candidate beat the training baseline \u2014 winner == baseline (empty diff); nothing to promote"
|
|
6209
6453
|
],
|
|
6210
6454
|
contributingGates: [
|
|
6211
|
-
{ name: "no-op-guard",
|
|
6455
|
+
{ name: "no-op-guard", status: "fail", detail: { winnerIsBaseline: true } }
|
|
6212
6456
|
],
|
|
6213
6457
|
delta: 0
|
|
6214
6458
|
} : await opts.gate.decide({
|
|
@@ -6289,13 +6533,7 @@ function loopProvenanceArgsFromResult(input) {
|
|
|
6289
6533
|
};
|
|
6290
6534
|
}
|
|
6291
6535
|
function meanHoldoutComposite(campaign) {
|
|
6292
|
-
|
|
6293
|
-
for (const cell of campaign.cells) {
|
|
6294
|
-
if (cell.error) continue;
|
|
6295
|
-
const cs = Object.values(cell.judgeScores).map((s) => s.composite);
|
|
6296
|
-
if (cs.length) xs.push(cs.reduce((a, b) => a + b, 0) / cs.length);
|
|
6297
|
-
}
|
|
6298
|
-
return xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : 0;
|
|
6536
|
+
return campaignMeanComposite(campaign);
|
|
6299
6537
|
}
|
|
6300
6538
|
function buildLoopProvenanceRecord(args) {
|
|
6301
6539
|
if (!args.runId.trim() || !args.runDir.trim()) {
|
|
@@ -6305,6 +6543,7 @@ function buildLoopProvenanceRecord(args) {
|
|
|
6305
6543
|
if (!Number.isFinite(timestampMs) || new Date(timestampMs).toISOString() !== args.timestamp) {
|
|
6306
6544
|
throw new Error("buildLoopProvenanceRecord: timestamp must be a canonical ISO instant");
|
|
6307
6545
|
}
|
|
6546
|
+
assertGateContributions(args.gate.contributingGates, "buildLoopProvenanceRecord");
|
|
6308
6547
|
const agentReceipts = args.costReceipts.filter((receipt) => receipt.channel === "agent");
|
|
6309
6548
|
const integrity = summarizeAgentReceiptIntegrity(agentReceipts);
|
|
6310
6549
|
const models = [...new Set(agentReceipts.map((receipt) => receipt.model))].sort();
|
|
@@ -6401,6 +6640,9 @@ function buildLoopProvenanceRecord(args) {
|
|
|
6401
6640
|
if (promotedHash) {
|
|
6402
6641
|
const promoted = candidateByHash.get(promotedHash);
|
|
6403
6642
|
incumbentSurfaceHash = promoted.surfaceHash;
|
|
6643
|
+
if (promoted.composite === null) {
|
|
6644
|
+
throw new Error("buildLoopProvenanceRecord: promoted candidate is missing a composite");
|
|
6645
|
+
}
|
|
6404
6646
|
incumbentComposite = promoted.composite;
|
|
6405
6647
|
}
|
|
6406
6648
|
}
|
|
@@ -6410,8 +6652,6 @@ function buildLoopProvenanceRecord(args) {
|
|
|
6410
6652
|
);
|
|
6411
6653
|
}
|
|
6412
6654
|
const holdoutDeferred = args.holdout === "deferred";
|
|
6413
|
-
const baselineHoldoutComposite = meanHoldoutComposite(args.baselineOnHoldout);
|
|
6414
|
-
const winnerHoldoutComposite = meanHoldoutComposite(args.winnerOnHoldout);
|
|
6415
6655
|
if (args.baselineOnHoldout.splitDigest !== args.winnerOnHoldout.splitDigest) {
|
|
6416
6656
|
throw new Error("buildLoopProvenanceRecord: baseline and winner use different holdout splits");
|
|
6417
6657
|
}
|
|
@@ -6425,7 +6665,17 @@ function buildLoopProvenanceRecord(args) {
|
|
|
6425
6665
|
"buildLoopProvenanceRecord: neutralized campaign uses a different holdout split"
|
|
6426
6666
|
);
|
|
6427
6667
|
}
|
|
6428
|
-
|
|
6668
|
+
if (holdoutDeferred && args.neutralizedOnHoldout) {
|
|
6669
|
+
throw new Error(
|
|
6670
|
+
"buildLoopProvenanceRecord: a deferred holdout cannot include a neutralized measurement"
|
|
6671
|
+
);
|
|
6672
|
+
}
|
|
6673
|
+
const holdoutMeasurement = holdoutDeferred ? { kind: "deferred" } : {
|
|
6674
|
+
kind: "measured",
|
|
6675
|
+
baseline: meanHoldoutComposite(args.baselineOnHoldout),
|
|
6676
|
+
winner: meanHoldoutComposite(args.winnerOnHoldout),
|
|
6677
|
+
...args.neutralizedOnHoldout ? { neutralized: meanHoldoutComposite(args.neutralizedOnHoldout) } : {}
|
|
6678
|
+
};
|
|
6429
6679
|
const diff = surfaceContentHash(args.baselineSurface) === surfaceContentHash(args.winnerSurface) ? "" : renderSurfaceDiff(args.winnerSurface, args.baselineSurface);
|
|
6430
6680
|
const recordWithoutDigest = {
|
|
6431
6681
|
schema: "tangle.loop-provenance",
|
|
@@ -6445,12 +6695,12 @@ function buildLoopProvenanceRecord(args) {
|
|
|
6445
6695
|
splitDigest: args.baselineOnHoldout.splitDigest,
|
|
6446
6696
|
baselineCampaignDigest: campaignMeasurementDigest(args.baselineOnHoldout),
|
|
6447
6697
|
winnerCampaignDigest: campaignMeasurementDigest(args.winnerOnHoldout),
|
|
6448
|
-
...args.neutralizedSurface && args.neutralizedOnHoldout &&
|
|
6698
|
+
...args.neutralizedSurface && args.neutralizedOnHoldout && holdoutMeasurement.kind === "measured" && holdoutMeasurement.neutralized !== void 0 ? {
|
|
6449
6699
|
neutralized: {
|
|
6450
6700
|
contentHash: surfaceContentHash(args.neutralizedSurface),
|
|
6451
6701
|
campaignDigest: campaignMeasurementDigest(args.neutralizedOnHoldout),
|
|
6452
|
-
composite:
|
|
6453
|
-
lift:
|
|
6702
|
+
composite: holdoutMeasurement.neutralized,
|
|
6703
|
+
lift: holdoutMeasurement.neutralized - holdoutMeasurement.baseline
|
|
6454
6704
|
}
|
|
6455
6705
|
} : {}
|
|
6456
6706
|
},
|
|
@@ -6465,16 +6715,16 @@ function buildLoopProvenanceRecord(args) {
|
|
|
6465
6715
|
delta: args.gate.delta,
|
|
6466
6716
|
contributingGates: args.gate.contributingGates.map((g) => ({
|
|
6467
6717
|
name: g.name,
|
|
6468
|
-
|
|
6718
|
+
status: g.status,
|
|
6469
6719
|
detail: durableGateDetail(g.detail)
|
|
6470
6720
|
}))
|
|
6471
6721
|
},
|
|
6472
6722
|
// Deferred holdout records the MODE, never a fabricated 0-lift: the
|
|
6473
6723
|
// held-out comparison intentionally did not run in this loop.
|
|
6474
|
-
...
|
|
6475
|
-
baselineHoldoutComposite,
|
|
6476
|
-
winnerHoldoutComposite,
|
|
6477
|
-
heldOutLift:
|
|
6724
|
+
...holdoutMeasurement.kind === "deferred" ? { holdout: "deferred" } : {
|
|
6725
|
+
baselineHoldoutComposite: holdoutMeasurement.baseline,
|
|
6726
|
+
winnerHoldoutComposite: holdoutMeasurement.winner,
|
|
6727
|
+
heldOutLift: holdoutMeasurement.winner - holdoutMeasurement.baseline
|
|
6478
6728
|
},
|
|
6479
6729
|
backend: {
|
|
6480
6730
|
verdict: integrity.verdict,
|
|
@@ -6540,6 +6790,8 @@ function campaignMeasurementDigest(campaign) {
|
|
|
6540
6790
|
durationMs: cell.durationMs,
|
|
6541
6791
|
seed: cell.seed,
|
|
6542
6792
|
cached: cell.cached,
|
|
6793
|
+
errorStage: cell.errorStage ?? null,
|
|
6794
|
+
errorJudge: cell.errorJudge ?? null,
|
|
6543
6795
|
error: cell.error ?? null
|
|
6544
6796
|
}))
|
|
6545
6797
|
});
|
|
@@ -6552,6 +6804,7 @@ function verifyLoopProvenanceRecord(record) {
|
|
|
6552
6804
|
if (recordDigest !== canonicalDigest(recordWithoutDigest)) {
|
|
6553
6805
|
throw new Error("loop provenance record digest does not match its contents");
|
|
6554
6806
|
}
|
|
6807
|
+
assertGateContributions(record.gate?.contributingGates, "loop provenance");
|
|
6555
6808
|
return record;
|
|
6556
6809
|
}
|
|
6557
6810
|
function canonicalDigest(value) {
|
|
@@ -6583,10 +6836,32 @@ function durableGateDetail(detail) {
|
|
|
6583
6836
|
});
|
|
6584
6837
|
}
|
|
6585
6838
|
}
|
|
6586
|
-
function
|
|
6587
|
-
if (!
|
|
6588
|
-
throw new Error(
|
|
6839
|
+
function assertGateContributions(value, source) {
|
|
6840
|
+
if (!Array.isArray(value)) {
|
|
6841
|
+
throw new Error(`${source}: gate contributingGates must be an array`);
|
|
6589
6842
|
}
|
|
6843
|
+
const statuses = /* @__PURE__ */ new Set(["pass", "fail", "not_evaluated"]);
|
|
6844
|
+
for (const [index, contribution] of value.entries()) {
|
|
6845
|
+
if (!contribution || typeof contribution !== "object") {
|
|
6846
|
+
throw new Error(`${source}: gate contribution ${index} must be an object`);
|
|
6847
|
+
}
|
|
6848
|
+
const item = contribution;
|
|
6849
|
+
if (typeof item.name !== "string" || item.name.length === 0) {
|
|
6850
|
+
throw new Error(`${source}: gate contribution ${index} must have a non-empty name`);
|
|
6851
|
+
}
|
|
6852
|
+
if (!statuses.has(String(item.status))) {
|
|
6853
|
+
throw new Error(
|
|
6854
|
+
`${source}: gate contribution '${item.name}' must have status pass, fail, or not_evaluated`
|
|
6855
|
+
);
|
|
6856
|
+
}
|
|
6857
|
+
if ("passed" in item) {
|
|
6858
|
+
throw new Error(
|
|
6859
|
+
`${source}: gate contribution '${item.name}' uses obsolete passed; use status instead`
|
|
6860
|
+
);
|
|
6861
|
+
}
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
function validateCandidateMeasurement(candidate, expectedParentHash, expectedParentComposite, promoted) {
|
|
6590
6865
|
if (!candidate.parentSurfaceHash || !/^[a-f0-9]{16}$/.test(candidate.parentSurfaceHash)) {
|
|
6591
6866
|
throw new Error(
|
|
6592
6867
|
"buildLoopProvenanceRecord: parentSurfaceHash must be 16 lowercase hex characters"
|
|
@@ -6600,11 +6875,6 @@ function validateCandidateMeasurement(candidate, expectedParentHash, expectedPar
|
|
|
6600
6875
|
"buildLoopProvenanceRecord: candidate parentComposite does not match the incumbent"
|
|
6601
6876
|
);
|
|
6602
6877
|
}
|
|
6603
|
-
if (candidate.eligibleForPromotion === void 0 || !candidate.coverage) {
|
|
6604
|
-
throw new Error(
|
|
6605
|
-
"buildLoopProvenanceRecord: candidate measurement requires eligibility and coverage"
|
|
6606
|
-
);
|
|
6607
|
-
}
|
|
6608
6878
|
if (candidate.observedDeltaFromParent !== void 0) {
|
|
6609
6879
|
if (!Number.isFinite(candidate.observedDeltaFromParent)) {
|
|
6610
6880
|
throw new Error("buildLoopProvenanceRecord: observedDeltaFromParent must be finite");
|
|
@@ -6632,12 +6902,15 @@ function validateCandidateMeasurement(candidate, expectedParentHash, expectedPar
|
|
|
6632
6902
|
);
|
|
6633
6903
|
}
|
|
6634
6904
|
const complete = coverage.scorableCells === coverage.expectedCells && coverage.unscorableCells.length === 0;
|
|
6635
|
-
if (candidate.eligibleForPromotion !==
|
|
6905
|
+
if (candidate.eligibleForPromotion !== complete) {
|
|
6636
6906
|
throw new Error(
|
|
6637
6907
|
"buildLoopProvenanceRecord: candidate eligibility contradicts its coverage receipt"
|
|
6638
6908
|
);
|
|
6639
6909
|
}
|
|
6640
6910
|
if (complete) {
|
|
6911
|
+
if (candidate.composite === null || !Number.isFinite(candidate.composite)) {
|
|
6912
|
+
throw new Error("buildLoopProvenanceRecord: complete candidate composite must be finite");
|
|
6913
|
+
}
|
|
6641
6914
|
if (candidate.observedDeltaFromParent === void 0) {
|
|
6642
6915
|
throw new Error(
|
|
6643
6916
|
"buildLoopProvenanceRecord: complete candidate is missing observedDeltaFromParent"
|
|
@@ -6647,24 +6920,34 @@ function validateCandidateMeasurement(candidate, expectedParentHash, expectedPar
|
|
|
6647
6920
|
if (Math.abs(candidate.observedDeltaFromParent - recomputed) > 1e-12) {
|
|
6648
6921
|
throw new Error("buildLoopProvenanceRecord: observed delta does not match measured scores");
|
|
6649
6922
|
}
|
|
6650
|
-
} else
|
|
6651
|
-
|
|
6923
|
+
} else {
|
|
6924
|
+
if (candidate.composite !== null && !Number.isFinite(candidate.composite)) {
|
|
6925
|
+
throw new Error("buildLoopProvenanceRecord: candidate composite must be finite or null");
|
|
6926
|
+
}
|
|
6927
|
+
if (candidate.observedDeltaFromParent !== void 0) {
|
|
6928
|
+
throw new Error("buildLoopProvenanceRecord: incomplete candidate cannot carry observed delta");
|
|
6929
|
+
}
|
|
6652
6930
|
}
|
|
6653
6931
|
if (promoted && (!complete || (candidate.observedDeltaFromParent ?? 0) <= 0)) {
|
|
6654
6932
|
throw new Error("buildLoopProvenanceRecord: promoted candidate must improve the incumbent");
|
|
6655
6933
|
}
|
|
6656
6934
|
}
|
|
6657
|
-
var DECISION_OK = ["ship"];
|
|
6658
6935
|
function hashId(parts) {
|
|
6659
6936
|
return createHash2("sha256").update(parts.join(":")).digest("hex");
|
|
6660
6937
|
}
|
|
6661
|
-
function gateStatus(decision) {
|
|
6662
|
-
return DECISION_OK.includes(decision) ? { code: "OK" } : { code: "ERROR", message: `gate decision: ${decision}` };
|
|
6663
|
-
}
|
|
6664
6938
|
function loopProvenanceSpans(record, opts = {}) {
|
|
6665
6939
|
const traceId = hashId(["trace", record.runId]).slice(0, 32);
|
|
6666
|
-
const
|
|
6667
|
-
const
|
|
6940
|
+
const baseTimeMs = opts.baseTimeMs ?? (Date.parse(record.timestamp) || Date.now());
|
|
6941
|
+
const durationMs = Math.max(1, record.totalDurationMs);
|
|
6942
|
+
if (!Number.isSafeInteger(baseTimeMs) || baseTimeMs < 0) {
|
|
6943
|
+
throw new RangeError("loop provenance baseTimeMs must be a non-negative safe integer");
|
|
6944
|
+
}
|
|
6945
|
+
if (!Number.isSafeInteger(durationMs)) {
|
|
6946
|
+
throw new RangeError("loop provenance duration must be a safe integer number of milliseconds");
|
|
6947
|
+
}
|
|
6948
|
+
const baseTime = BigInt(baseTimeMs);
|
|
6949
|
+
const baseNano = (baseTime * 1000000n).toString();
|
|
6950
|
+
const endNano = ((baseTime + BigInt(durationMs)) * 1000000n).toString();
|
|
6668
6951
|
const spans = [];
|
|
6669
6952
|
const rootSpanId = hashId(["root", record.runId]).slice(0, 16);
|
|
6670
6953
|
const rootAttributes = {
|
|
@@ -6687,7 +6970,7 @@ function loopProvenanceSpans(record, opts = {}) {
|
|
|
6687
6970
|
startTimeUnixNano: baseNano,
|
|
6688
6971
|
endTimeUnixNano: endNano,
|
|
6689
6972
|
attributes: rootAttributes,
|
|
6690
|
-
status:
|
|
6973
|
+
status: { code: "OK" },
|
|
6691
6974
|
"tangle.runId": record.runId
|
|
6692
6975
|
});
|
|
6693
6976
|
const byGen = /* @__PURE__ */ new Map();
|
|
@@ -6698,7 +6981,9 @@ function loopProvenanceSpans(record, opts = {}) {
|
|
|
6698
6981
|
}
|
|
6699
6982
|
for (const [generation, cands] of [...byGen.entries()].sort((a, b) => a[0] - b[0])) {
|
|
6700
6983
|
const genSpanId = hashId(["gen", record.runId, String(generation)]).slice(0, 16);
|
|
6701
|
-
const
|
|
6984
|
+
const measuredComposites = cands.flatMap(
|
|
6985
|
+
(candidate) => candidate.composite === null ? [] : [candidate.composite]
|
|
6986
|
+
);
|
|
6702
6987
|
spans.push({
|
|
6703
6988
|
traceId,
|
|
6704
6989
|
spanId: genSpanId,
|
|
@@ -6710,7 +6995,7 @@ function loopProvenanceSpans(record, opts = {}) {
|
|
|
6710
6995
|
"tangle.runId": record.runId,
|
|
6711
6996
|
"tangle.generation": generation,
|
|
6712
6997
|
"tangle.populationSize": cands.length,
|
|
6713
|
-
"tangle.bestComposite":
|
|
6998
|
+
...measuredComposites.length > 0 ? { "tangle.bestComposite": Math.max(...measuredComposites) } : {}
|
|
6714
6999
|
},
|
|
6715
7000
|
"tangle.runId": record.runId,
|
|
6716
7001
|
"tangle.generation": generation
|
|
@@ -6728,13 +7013,13 @@ function loopProvenanceSpans(record, opts = {}) {
|
|
|
6728
7013
|
"tangle.contentHash": c.contentHash,
|
|
6729
7014
|
"tangle.parentSurfaceHash": c.parentSurfaceHash,
|
|
6730
7015
|
"tangle.parentComposite": c.parentComposite,
|
|
6731
|
-
"tangle.composite": c.composite,
|
|
6732
7016
|
"tangle.eligibleForPromotion": c.eligibleForPromotion,
|
|
6733
7017
|
"tangle.expectedCells": c.coverage.expectedCells,
|
|
6734
7018
|
"tangle.scorableCells": c.coverage.scorableCells,
|
|
6735
7019
|
"tangle.unscorableCells": c.coverage.unscorableCells.length,
|
|
6736
7020
|
"tangle.promoted": c.promoted
|
|
6737
7021
|
};
|
|
7022
|
+
if (c.composite !== null) attributes["tangle.composite"] = c.composite;
|
|
6738
7023
|
if (c.observedDeltaFromParent !== void 0) {
|
|
6739
7024
|
attributes["tangle.observedDeltaFromParent"] = c.observedDeltaFromParent;
|
|
6740
7025
|
}
|
|
@@ -6777,7 +7062,7 @@ function loopProvenanceSpans(record, opts = {}) {
|
|
|
6777
7062
|
startTimeUnixNano: endNano,
|
|
6778
7063
|
endTimeUnixNano: endNano,
|
|
6779
7064
|
attributes: gateAttributes,
|
|
6780
|
-
status:
|
|
7065
|
+
status: { code: "OK" },
|
|
6781
7066
|
"tangle.runId": record.runId
|
|
6782
7067
|
});
|
|
6783
7068
|
return spans;
|
|
@@ -6790,26 +7075,25 @@ function provenanceSpansPath(runDir) {
|
|
|
6790
7075
|
}
|
|
6791
7076
|
function snapshotFromHoldout(index, surfaceHash2, surface, campaign) {
|
|
6792
7077
|
const cells = campaign.cells.map((cell) => {
|
|
6793
|
-
const
|
|
6794
|
-
const
|
|
7078
|
+
const execution = campaignCellExecutionEvidence(cell);
|
|
7079
|
+
const quality = projectCampaignCellQuality(cell);
|
|
6795
7080
|
const score = {
|
|
6796
7081
|
scenarioId: cell.scenarioId,
|
|
6797
7082
|
rep: cell.rep,
|
|
6798
|
-
compositeMean:
|
|
6799
|
-
dimensions:
|
|
6800
|
-
|
|
6801
|
-
|
|
7083
|
+
compositeMean: quality.score ?? null,
|
|
7084
|
+
dimensions: quality.judgeScores?.perJudge ?? {},
|
|
7085
|
+
terminalOutcome: execution.terminalOutcome,
|
|
7086
|
+
executionErrorCount: execution.executionErrorCount ?? null
|
|
6802
7087
|
};
|
|
6803
7088
|
if (cell.error) score.errorMessage = cell.error;
|
|
6804
7089
|
return score;
|
|
6805
7090
|
});
|
|
6806
|
-
const compositeMean = cells.length === 0 ? 0 : cells.reduce((s, c) => s + c.compositeMean, 0) / cells.length;
|
|
6807
7091
|
return {
|
|
6808
7092
|
index,
|
|
6809
7093
|
surfaceHash: surfaceHash2,
|
|
6810
7094
|
surface,
|
|
6811
7095
|
cells,
|
|
6812
|
-
compositeMean,
|
|
7096
|
+
compositeMean: campaignMeanCompositeOrNull(campaign),
|
|
6813
7097
|
costUsd: campaign.aggregates.totalCostUsd,
|
|
6814
7098
|
durationMs: campaign.durationMs
|
|
6815
7099
|
};
|
|
@@ -7346,4 +7630,4 @@ export {
|
|
|
7346
7630
|
emitLoopProvenance,
|
|
7347
7631
|
skillOptOptimizationMethod
|
|
7348
7632
|
};
|
|
7349
|
-
//# sourceMappingURL=chunk-
|
|
7633
|
+
//# sourceMappingURL=chunk-65QETEKG.js.map
|