@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24
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/README.md +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +16 -9
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +547 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- package/ts/src/unified.ts +316 -54
|
@@ -1,207 +1,75 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
export const BENCHMARK_IDS = [
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return out;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return value;
|
|
50
|
-
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
if (!Number.isInteger(out)) throw new Error(`${label} must be an integer`);
|
|
58
|
-
return out;
|
|
59
|
-
}
|
|
60
|
-
function strings(value, label, empty = false) {
|
|
61
|
-
if (!Array.isArray(value) || !empty && value.length === 0) throw new Error(`${label} must be an array`);
|
|
62
|
-
const out = value.map((item, index)=>text(item, `${label}[${index}]`));
|
|
63
|
-
if (new Set(out).size !== out.length) throw new Error(`${label} must be unique`);
|
|
64
|
-
return out;
|
|
65
|
-
}
|
|
66
|
-
function digest(value, label) {
|
|
67
|
-
const out = text(value, label);
|
|
68
|
-
if (!/^[0-9a-f]{64}$/.test(out)) throw new Error(`${label} must be lowercase SHA-256`);
|
|
69
|
-
return out;
|
|
70
|
-
}
|
|
71
|
-
function commit(value, label) {
|
|
72
|
-
const out = text(value, label);
|
|
73
|
-
if (!/^[0-9a-f]{40}$/.test(out)) throw new Error(`${label} must be a lowercase 40-character Git commit`);
|
|
74
|
-
return out;
|
|
75
|
-
}
|
|
76
|
-
function url(value, label) {
|
|
77
|
-
const out = text(value, label);
|
|
78
|
-
let parsed;
|
|
79
|
-
try {
|
|
80
|
-
parsed = new URL(out);
|
|
81
|
-
} catch {
|
|
82
|
-
throw new Error(`${label} must be a valid URL`);
|
|
83
|
-
}
|
|
84
|
-
;
|
|
85
|
-
if (parsed.protocol !== 'https:') throw new Error(`${label} must use https`);
|
|
86
|
-
if (parsed.username || parsed.password) throw new Error(`${label} must not contain credentials`);
|
|
87
|
-
return out;
|
|
88
|
-
}
|
|
89
|
-
function instant(value, label) {
|
|
90
|
-
const out = text(value, label);
|
|
91
|
-
const date = new Date(out);
|
|
92
|
-
if (!Number.isFinite(date.valueOf()) || date.toISOString() !== out) throw new Error(`${label} must be canonical ISO UTC with milliseconds`);
|
|
93
|
-
return out;
|
|
94
|
-
}
|
|
95
|
-
function zone(value, label) {
|
|
96
|
-
const out = text(value, label);
|
|
97
|
-
if (out === 'UTC') return out;
|
|
98
|
-
try {
|
|
99
|
-
new Intl.DateTimeFormat('en', {
|
|
100
|
-
timeZone: out
|
|
101
|
-
}).format(new Date(0));
|
|
102
|
-
} catch {
|
|
103
|
-
throw new Error(`${label} must be UTC or an IANA timezone`);
|
|
104
|
-
}
|
|
105
|
-
;
|
|
106
|
-
if (!out.includes('/')) throw new Error(`${label} must be UTC or an IANA timezone`);
|
|
107
|
-
return out;
|
|
108
|
-
}
|
|
109
|
-
function revision(value, label) {
|
|
110
|
-
const root = obj(value, label);
|
|
111
|
-
exact(root, label, [
|
|
112
|
-
'kind',
|
|
113
|
-
'value'
|
|
114
|
-
]);
|
|
115
|
-
const kind = lit(root.kind, `${label}.kind`, [
|
|
116
|
-
'git_commit',
|
|
117
|
-
'content_sha256',
|
|
118
|
-
'not_separately_declared'
|
|
119
|
-
]);
|
|
120
|
-
if (kind === 'not_separately_declared') {
|
|
121
|
-
if (root.value !== null) throw new Error(`${label}.value must be null`);
|
|
122
|
-
return {
|
|
123
|
-
kind,
|
|
124
|
-
value: null
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
;
|
|
128
|
-
return {
|
|
129
|
-
kind,
|
|
130
|
-
value: kind === 'git_commit' ? commit(root.value, `${label}.value`) : digest(root.value, `${label}.value`)
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
function pin(value, label) {
|
|
134
|
-
const root = obj(value, label);
|
|
135
|
-
exact(root, label, [
|
|
136
|
-
'url',
|
|
137
|
-
'revision',
|
|
138
|
-
'source_scope'
|
|
139
|
-
]);
|
|
140
|
-
return {
|
|
141
|
-
url: url(root.url, `${label}.url`),
|
|
142
|
-
revision: revision(root.revision, `${label}.revision`),
|
|
143
|
-
source_scope: text(root.source_scope, `${label}.source_scope`)
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
function licenseDetermination(value, label) {
|
|
147
|
-
const root = obj(value, label);
|
|
148
|
-
exact(root, label, [
|
|
149
|
-
'value',
|
|
150
|
-
'determination',
|
|
151
|
-
'source_url'
|
|
152
|
-
]);
|
|
153
|
-
const determination = lit(root.determination, `${label}.determination`, [
|
|
154
|
-
'declared',
|
|
155
|
-
'not_separately_declared'
|
|
156
|
-
]);
|
|
157
|
-
const resolved = text(root.value, `${label}.value`);
|
|
158
|
-
if (determination === 'not_separately_declared' && resolved !== 'not_separately_declared') throw new Error(`${label}.value must be not_separately_declared`);
|
|
159
|
-
return {
|
|
160
|
-
value: resolved,
|
|
161
|
-
determination,
|
|
162
|
-
source_url: url(root.source_url, `${label}.source_url`)
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
export function stableEvaluationJson(value) {
|
|
166
|
-
const sort = (item)=>Array.isArray(item) ? item.map(sort) : item && typeof item === 'object' ? Object.fromEntries(Object.entries(item).sort(([a], [b])=>a.localeCompare(b)).map(([key, child])=>[
|
|
167
|
-
key,
|
|
168
|
-
sort(child)
|
|
169
|
-
])) : item;
|
|
170
|
-
return `${JSON.stringify(sort(value), null, 2)}\n`;
|
|
171
|
-
}
|
|
172
|
-
export function evaluationFingerprint(value) {
|
|
173
|
-
return createHash('sha256').update(stableEvaluationJson(value)).digest('hex');
|
|
174
|
-
}
|
|
175
|
-
const sensitiveKeys = new Set([
|
|
176
|
-
'apikey',
|
|
177
|
-
'accesstoken',
|
|
178
|
-
'refreshtoken',
|
|
179
|
-
'authorization',
|
|
180
|
-
'password',
|
|
181
|
-
'clientsecret',
|
|
182
|
-
'credentials',
|
|
183
|
-
'cookie',
|
|
184
|
-
'rawprompt',
|
|
185
|
-
'goldanswer',
|
|
186
|
-
'oraclepayload',
|
|
187
|
-
'credential',
|
|
188
|
-
'rawanswer',
|
|
189
|
-
'privatepayload',
|
|
190
|
-
'secretpayload',
|
|
191
|
-
'trajectorypayload'
|
|
192
|
-
]);
|
|
2
|
+
export const BENCHMARK_IDS = ['trek', 'travelplanner', 'chinatravel', 'travelbench', 'tau2', 'locomo', 'bfcl'];
|
|
3
|
+
export const FAILURE_CATEGORIES = ['schema_or_format', 'grounding_or_provenance', 'constraint_or_feasibility', 'time_or_location_continuity', 'cost_or_cardinality', 'tool_selection_or_arguments', 'policy_or_write_safety', 'preference_elicitation', 'memory_retrieval_or_temporal_reasoning', 'reliability_cost_or_latency'];
|
|
4
|
+
function obj(value, label) { if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
5
|
+
throw new Error(`${label} must be an object`); return value; }
|
|
6
|
+
function exact(value, label, keys) { const unknown = Object.keys(value).filter(key => !keys.includes(key)); const missing = keys.filter(key => !(key in value)); if (unknown.length)
|
|
7
|
+
throw new Error(`${label} contains undeclared fields: ${unknown.join(',')}`); if (missing.length)
|
|
8
|
+
throw new Error(`${label} missing fields: ${missing.join(',')}`); }
|
|
9
|
+
function text(value, label) { if (typeof value !== 'string' || value.length === 0)
|
|
10
|
+
throw new Error(`${label} must be non-empty text`); return value; }
|
|
11
|
+
function id(value, label) { const out = text(value, label); if (!/^[a-z0-9][a-z0-9:._-]+$/.test(out))
|
|
12
|
+
throw new Error(`${label} must be a stable identifier`); return out; }
|
|
13
|
+
function lit(value, label, values) { const out = text(value, label); if (!values.includes(out))
|
|
14
|
+
throw new Error(`${label} invalid literal`); return out; }
|
|
15
|
+
function boolean(value, label) { if (typeof value !== 'boolean')
|
|
16
|
+
throw new Error(`${label} must be boolean`); return value; }
|
|
17
|
+
function finite(value, label, min = 0) { if (typeof value !== 'number' || !Number.isFinite(value) || value < min)
|
|
18
|
+
throw new Error(`${label} must be a finite number >= ${min}`); return value; }
|
|
19
|
+
function integer(value, label, min = 0) { const out = finite(value, label, min); if (!Number.isInteger(out))
|
|
20
|
+
throw new Error(`${label} must be an integer`); return out; }
|
|
21
|
+
function strings(value, label, empty = false) { if (!Array.isArray(value) || (!empty && value.length === 0))
|
|
22
|
+
throw new Error(`${label} must be an array`); const out = value.map((item, index) => text(item, `${label}[${index}]`)); if (new Set(out).size !== out.length)
|
|
23
|
+
throw new Error(`${label} must be unique`); return out; }
|
|
24
|
+
function digest(value, label) { const out = text(value, label); if (!/^[0-9a-f]{64}$/.test(out))
|
|
25
|
+
throw new Error(`${label} must be lowercase SHA-256`); return out; }
|
|
26
|
+
function commit(value, label) { const out = text(value, label); if (!/^[0-9a-f]{40}$/.test(out))
|
|
27
|
+
throw new Error(`${label} must be a lowercase 40-character Git commit`); return out; }
|
|
28
|
+
function url(value, label) { const out = text(value, label); let parsed; try {
|
|
29
|
+
parsed = new URL(out);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new Error(`${label} must be a valid URL`);
|
|
33
|
+
} ; if (parsed.protocol !== 'https:')
|
|
34
|
+
throw new Error(`${label} must use https`); if (parsed.username || parsed.password)
|
|
35
|
+
throw new Error(`${label} must not contain credentials`); return out; }
|
|
36
|
+
function instant(value, label) { const out = text(value, label); const date = new Date(out); if (!Number.isFinite(date.valueOf()) || date.toISOString() !== out)
|
|
37
|
+
throw new Error(`${label} must be canonical ISO UTC with milliseconds`); return out; }
|
|
38
|
+
function zone(value, label) { const out = text(value, label); if (out === 'UTC')
|
|
39
|
+
return out; try {
|
|
40
|
+
new Intl.DateTimeFormat('en', { timeZone: out }).format(new Date(0));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new Error(`${label} must be UTC or an IANA timezone`);
|
|
44
|
+
} ; if (!out.includes('/'))
|
|
45
|
+
throw new Error(`${label} must be UTC or an IANA timezone`); return out; }
|
|
46
|
+
function revision(value, label) { const root = obj(value, label); exact(root, label, ['kind', 'value']); const kind = lit(root.kind, `${label}.kind`, ['git_commit', 'content_sha256', 'not_separately_declared']); if (kind === 'not_separately_declared') {
|
|
47
|
+
if (root.value !== null)
|
|
48
|
+
throw new Error(`${label}.value must be null`);
|
|
49
|
+
return { kind, value: null };
|
|
50
|
+
} ; return { kind, value: kind === 'git_commit' ? commit(root.value, `${label}.value`) : digest(root.value, `${label}.value`) }; }
|
|
51
|
+
function pin(value, label) { const root = obj(value, label); exact(root, label, ['url', 'revision', 'source_scope']); return { url: url(root.url, `${label}.url`), revision: revision(root.revision, `${label}.revision`), source_scope: text(root.source_scope, `${label}.source_scope`) }; }
|
|
52
|
+
function licenseDetermination(value, label) { const root = obj(value, label); exact(root, label, ['value', 'determination', 'source_url']); const determination = lit(root.determination, `${label}.determination`, ['declared', 'not_separately_declared']); const resolved = text(root.value, `${label}.value`); if (determination === 'not_separately_declared' && resolved !== 'not_separately_declared')
|
|
53
|
+
throw new Error(`${label}.value must be not_separately_declared`); return { value: resolved, determination, source_url: url(root.source_url, `${label}.source_url`) }; }
|
|
54
|
+
export function stableEvaluationJson(value) { const sort = (item) => Array.isArray(item) ? item.map(sort) : item && typeof item === 'object' ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => [key, sort(child)])) : item; return `${JSON.stringify(sort(value), null, 2)}\n`; }
|
|
55
|
+
export function evaluationFingerprint(value) { return createHash('sha256').update(stableEvaluationJson(value)).digest('hex'); }
|
|
56
|
+
const sensitiveKeys = new Set(['apikey', 'accesstoken', 'refreshtoken', 'authorization', 'password', 'clientsecret', 'credentials', 'cookie', 'rawprompt', 'goldanswer', 'oraclepayload', 'credential', 'rawanswer', 'privatepayload', 'secretpayload', 'trajectorypayload']);
|
|
193
57
|
export function assertPublicArtifactSafe(value, label = 'artifact') {
|
|
194
|
-
const walk = (item, path)=>{
|
|
195
|
-
if (Array.isArray(item))
|
|
58
|
+
const walk = (item, path) => {
|
|
59
|
+
if (Array.isArray(item))
|
|
60
|
+
return item.forEach((child, index) => walk(child, `${path}[${index}]`));
|
|
196
61
|
if (typeof item === 'string') {
|
|
197
62
|
const withoutHttpsUrls = item.replace(/https:\/\/[^\s]+/g, '');
|
|
198
63
|
const highConfidenceSecret = /Bearer\s+[A-Za-z0-9._~-]{24,}|\bsk-[A-Za-z0-9_-]{20,}|\bgh[pousr]_[A-Za-z0-9_]{20,}|\bAKIA[0-9A-Z]{16}\b/;
|
|
199
|
-
if (/(?:^|[^A-Za-z0-9])(?:\/|~\/)|[A-Za-z]:[\\/]|file:\/\//.test(withoutHttpsUrls) || highConfidenceSecret.test(item))
|
|
64
|
+
if (/(?:^|[^A-Za-z0-9])(?:\/|~\/)|[A-Za-z]:[\\/]|file:\/\//.test(withoutHttpsUrls) || highConfidenceSecret.test(item))
|
|
65
|
+
throw new Error(`${path} contains an absolute path or secret`);
|
|
200
66
|
return;
|
|
201
67
|
}
|
|
202
|
-
if (!item || typeof item !== 'object')
|
|
203
|
-
|
|
204
|
-
|
|
68
|
+
if (!item || typeof item !== 'object')
|
|
69
|
+
return;
|
|
70
|
+
for (const [key, child] of Object.entries(item)) {
|
|
71
|
+
if (sensitiveKeys.has(key.replace(/[^a-z0-9]/gi, '').toLowerCase()) && child !== '' && child !== null)
|
|
72
|
+
throw new Error(`${path}.${key} contains credentials or raw sensitive payload`);
|
|
205
73
|
walk(child, `${path}.${key}`);
|
|
206
74
|
}
|
|
207
75
|
};
|
|
@@ -209,698 +77,207 @@ export function assertPublicArtifactSafe(value, label = 'artifact') {
|
|
|
209
77
|
}
|
|
210
78
|
function registryEntry(value, label) {
|
|
211
79
|
const root = obj(value, label);
|
|
212
|
-
exact(root, label, [
|
|
213
|
-
'schema_version',
|
|
214
|
-
'benchmark_id',
|
|
215
|
-
'provenance',
|
|
216
|
-
'license',
|
|
217
|
-
'task_scopes',
|
|
218
|
-
'native_metrics',
|
|
219
|
-
'source_fence',
|
|
220
|
-
'countability_default'
|
|
221
|
-
]);
|
|
80
|
+
exact(root, label, ['schema_version', 'benchmark_id', 'provenance', 'license', 'task_scopes', 'native_metrics', 'source_fence', 'countability_default']);
|
|
222
81
|
const provenance = obj(root.provenance, `${label}.provenance`);
|
|
223
|
-
exact(provenance, `${label}.provenance`, [
|
|
224
|
-
'official_entry',
|
|
225
|
-
'data',
|
|
226
|
-
'evaluator'
|
|
227
|
-
]);
|
|
82
|
+
exact(provenance, `${label}.provenance`, ['official_entry', 'data', 'evaluator']);
|
|
228
83
|
const license = obj(root.license, `${label}.license`);
|
|
229
|
-
exact(license, `${label}.license`, [
|
|
230
|
-
'upstream_rights',
|
|
231
|
-
'repo_storage_policy'
|
|
232
|
-
]);
|
|
84
|
+
exact(license, `${label}.license`, ['upstream_rights', 'repo_storage_policy']);
|
|
233
85
|
const rights = obj(license.upstream_rights, `${label}.license.upstream_rights`);
|
|
234
|
-
exact(rights, `${label}.license.upstream_rights`, [
|
|
235
|
-
'code',
|
|
236
|
-
'data',
|
|
237
|
-
'evaluator'
|
|
238
|
-
]);
|
|
86
|
+
exact(rights, `${label}.license.upstream_rights`, ['code', 'data', 'evaluator']);
|
|
239
87
|
const metrics = obj(root.native_metrics, `${label}.native_metrics`);
|
|
240
|
-
exact(metrics, `${label}.native_metrics`, [
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
]);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const values = metrics.values.map((raw, index)=>{
|
|
250
|
-
const metric = obj(raw, `${label}.metric[${index}]`);
|
|
251
|
-
exact(metric, `${label}.metric[${index}]`, [
|
|
252
|
-
'receipt_key',
|
|
253
|
-
'upstream_label',
|
|
254
|
-
'scope',
|
|
255
|
-
'source_url'
|
|
256
|
-
]);
|
|
257
|
-
return {
|
|
258
|
-
receipt_key: id(metric.receipt_key, `${label}.metric.receipt_key`),
|
|
259
|
-
upstream_label: text(metric.upstream_label, `${label}.metric.upstream_label`),
|
|
260
|
-
scope: text(metric.scope, `${label}.metric.scope`),
|
|
261
|
-
source_url: url(metric.source_url, `${label}.metric.source_url`)
|
|
262
|
-
};
|
|
263
|
-
});
|
|
264
|
-
if (status === 'declared' && values.length === 0) throw new Error(`${label} declared metrics must be non-empty`);
|
|
265
|
-
if (status === 'not_separately_declared' && values.length !== 0) throw new Error(`${label} undeclared metrics must be empty`);
|
|
88
|
+
exact(metrics, `${label}.native_metrics`, ['status', 'values']);
|
|
89
|
+
if (!Array.isArray(metrics.values))
|
|
90
|
+
throw new Error(`${label}.native_metrics.values must be an array`);
|
|
91
|
+
const status = lit(metrics.status, `${label}.native_metrics.status`, ['declared', 'not_separately_declared']);
|
|
92
|
+
const values = metrics.values.map((raw, index) => { const metric = obj(raw, `${label}.metric[${index}]`); exact(metric, `${label}.metric[${index}]`, ['receipt_key', 'upstream_label', 'scope', 'source_url']); return { receipt_key: id(metric.receipt_key, `${label}.metric.receipt_key`), upstream_label: text(metric.upstream_label, `${label}.metric.upstream_label`), scope: text(metric.scope, `${label}.metric.scope`), source_url: url(metric.source_url, `${label}.metric.source_url`) }; });
|
|
93
|
+
if (status === 'declared' && values.length === 0)
|
|
94
|
+
throw new Error(`${label} declared metrics must be non-empty`);
|
|
95
|
+
if (status === 'not_separately_declared' && values.length !== 0)
|
|
96
|
+
throw new Error(`${label} undeclared metrics must be empty`);
|
|
266
97
|
const fence = obj(root.source_fence, `${label}.source_fence`);
|
|
267
|
-
exact(fence, `${label}.source_fence`, [
|
|
268
|
-
|
|
269
|
-
'solver_forbidden_field_classes'
|
|
270
|
-
]);
|
|
271
|
-
return {
|
|
272
|
-
schema_version: lit(root.schema_version, `${label}.schema_version`, [
|
|
273
|
-
'gotry_benchmark_registry_entry_v0'
|
|
274
|
-
]),
|
|
275
|
-
benchmark_id: lit(root.benchmark_id, `${label}.benchmark_id`, BENCHMARK_IDS),
|
|
276
|
-
provenance: {
|
|
277
|
-
official_entry: pin(provenance.official_entry, `${label}.official_entry`),
|
|
278
|
-
data: pin(provenance.data, `${label}.data`),
|
|
279
|
-
evaluator: pin(provenance.evaluator, `${label}.evaluator`)
|
|
280
|
-
},
|
|
281
|
-
license: {
|
|
282
|
-
upstream_rights: {
|
|
283
|
-
code: licenseDetermination(rights.code, `${label}.rights.code`),
|
|
284
|
-
data: licenseDetermination(rights.data, `${label}.rights.data`),
|
|
285
|
-
evaluator: licenseDetermination(rights.evaluator, `${label}.rights.evaluator`)
|
|
286
|
-
},
|
|
287
|
-
repo_storage_policy: lit(license.repo_storage_policy, `${label}.storage`, [
|
|
288
|
-
'metadata_only_no_upstream_payload'
|
|
289
|
-
])
|
|
290
|
-
},
|
|
291
|
-
task_scopes: strings(root.task_scopes, `${label}.task_scopes`),
|
|
292
|
-
native_metrics: status === 'declared' ? {
|
|
293
|
-
status,
|
|
294
|
-
values
|
|
295
|
-
} : {
|
|
296
|
-
status,
|
|
297
|
-
values: []
|
|
298
|
-
},
|
|
299
|
-
source_fence: {
|
|
300
|
-
solver_allowed_field_classes: strings(fence.solver_allowed_field_classes, `${label}.allowed`),
|
|
301
|
-
solver_forbidden_field_classes: strings(fence.solver_forbidden_field_classes, `${label}.forbidden`)
|
|
302
|
-
},
|
|
303
|
-
countability_default: lit(root.countability_default, `${label}.countability_default`, [
|
|
304
|
-
'countable_if_qualified',
|
|
305
|
-
'diagnostic_only'
|
|
306
|
-
])
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
export function parseBenchmarkRegistry(value) {
|
|
310
|
-
if (!Array.isArray(value) || value.length !== 7) throw new Error('registry must contain exactly seven entries');
|
|
311
|
-
const out = value.map((item, index)=>registryEntry(item, `registry[${index}]`));
|
|
312
|
-
if (out.some((item, index)=>item.benchmark_id !== BENCHMARK_IDS[index])) throw new Error('registry must use canonical unique order');
|
|
313
|
-
return out;
|
|
98
|
+
exact(fence, `${label}.source_fence`, ['solver_allowed_field_classes', 'solver_forbidden_field_classes']);
|
|
99
|
+
return { schema_version: lit(root.schema_version, `${label}.schema_version`, ['gotry_benchmark_registry_entry_v0']), benchmark_id: lit(root.benchmark_id, `${label}.benchmark_id`, BENCHMARK_IDS), provenance: { official_entry: pin(provenance.official_entry, `${label}.official_entry`), data: pin(provenance.data, `${label}.data`), evaluator: pin(provenance.evaluator, `${label}.evaluator`) }, license: { upstream_rights: { code: licenseDetermination(rights.code, `${label}.rights.code`), data: licenseDetermination(rights.data, `${label}.rights.data`), evaluator: licenseDetermination(rights.evaluator, `${label}.rights.evaluator`) }, repo_storage_policy: lit(license.repo_storage_policy, `${label}.storage`, ['metadata_only_no_upstream_payload']) }, task_scopes: strings(root.task_scopes, `${label}.task_scopes`), native_metrics: status === 'declared' ? { status, values } : { status, values: [] }, source_fence: { solver_allowed_field_classes: strings(fence.solver_allowed_field_classes, `${label}.allowed`), solver_forbidden_field_classes: strings(fence.solver_forbidden_field_classes, `${label}.forbidden`) }, countability_default: lit(root.countability_default, `${label}.countability_default`, ['countable_if_qualified', 'diagnostic_only']) };
|
|
314
100
|
}
|
|
101
|
+
export function parseBenchmarkRegistry(value) { if (!Array.isArray(value) || value.length !== 7)
|
|
102
|
+
throw new Error('registry must contain exactly seven entries'); const out = value.map((item, index) => registryEntry(item, `registry[${index}]`)); if (out.some((item, index) => item.benchmark_id !== BENCHMARK_IDS[index]))
|
|
103
|
+
throw new Error('registry must use canonical unique order'); return out; }
|
|
315
104
|
export function parseEvalCase(value) {
|
|
316
105
|
const root = obj(value, 'case');
|
|
317
|
-
exact(root, 'case', [
|
|
318
|
-
'schema_version',
|
|
319
|
-
'case_id',
|
|
320
|
-
'benchmark_id',
|
|
321
|
-
'input_ref',
|
|
322
|
-
'isolation',
|
|
323
|
-
'clock',
|
|
324
|
-
'allowed_effects',
|
|
325
|
-
'forbidden_effects',
|
|
326
|
-
'budget',
|
|
327
|
-
'scorer_revision',
|
|
328
|
-
'public_safety'
|
|
329
|
-
]);
|
|
106
|
+
exact(root, 'case', ['schema_version', 'case_id', 'benchmark_id', 'input_ref', 'isolation', 'clock', 'allowed_effects', 'forbidden_effects', 'budget', 'scorer_revision', 'public_safety']);
|
|
330
107
|
const input = obj(root.input_ref, 'case.input_ref');
|
|
331
|
-
exact(input, 'case.input_ref', [
|
|
332
|
-
'kind',
|
|
333
|
-
'revision',
|
|
334
|
-
'digest_sha256'
|
|
335
|
-
]);
|
|
108
|
+
exact(input, 'case.input_ref', ['kind', 'revision', 'digest_sha256']);
|
|
336
109
|
const isolation = obj(root.isolation, 'case.isolation');
|
|
337
|
-
exact(isolation, 'case.isolation', [
|
|
338
|
-
'state_root',
|
|
339
|
-
'network',
|
|
340
|
-
'writes'
|
|
341
|
-
]);
|
|
110
|
+
exact(isolation, 'case.isolation', ['state_root', 'network', 'writes']);
|
|
342
111
|
const clock = obj(root.clock, 'case.clock');
|
|
343
|
-
exact(clock, 'case.clock', [
|
|
344
|
-
'now',
|
|
345
|
-
'timezone'
|
|
346
|
-
]);
|
|
112
|
+
exact(clock, 'case.clock', ['now', 'timezone']);
|
|
347
113
|
const budget = obj(root.budget, 'case.budget');
|
|
348
|
-
exact(budget, 'case.budget', [
|
|
349
|
-
'max_seconds',
|
|
350
|
-
'max_cost_usd',
|
|
351
|
-
'max_tool_calls',
|
|
352
|
-
'max_turns'
|
|
353
|
-
]);
|
|
114
|
+
exact(budget, 'case.budget', ['max_seconds', 'max_cost_usd', 'max_tool_calls', 'max_turns']);
|
|
354
115
|
const safety = obj(root.public_safety, 'case.public_safety');
|
|
355
|
-
exact(safety, 'case.public_safety', [
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
]);
|
|
361
|
-
for (const [key, flag] of Object.entries(safety))if (boolean(flag, `case.public_safety.${key}`) !== false) throw new Error(`case.public_safety.${key} must be false`);
|
|
362
|
-
const out = {
|
|
363
|
-
schema_version: lit(root.schema_version, 'case.schema_version', [
|
|
364
|
-
'gotry_eval_case_v0'
|
|
365
|
-
]),
|
|
366
|
-
case_id: id(root.case_id, 'case.case_id'),
|
|
367
|
-
benchmark_id: lit(root.benchmark_id, 'case.benchmark_id', BENCHMARK_IDS),
|
|
368
|
-
input_ref: {
|
|
369
|
-
kind: lit(input.kind, 'case.input_ref.kind', [
|
|
370
|
-
'gotry_owned_synthetic',
|
|
371
|
-
'external_opaque_reference'
|
|
372
|
-
]),
|
|
373
|
-
revision: revision(input.revision, 'case.input_ref.revision'),
|
|
374
|
-
digest_sha256: digest(input.digest_sha256, 'case.input_ref.digest_sha256')
|
|
375
|
-
},
|
|
376
|
-
isolation: {
|
|
377
|
-
state_root: lit(isolation.state_root, 'case.state_root', [
|
|
378
|
-
'ephemeral'
|
|
379
|
-
]),
|
|
380
|
-
network: lit(isolation.network, 'case.network', [
|
|
381
|
-
'denied',
|
|
382
|
-
'benchmark_declared'
|
|
383
|
-
]),
|
|
384
|
-
writes: lit(isolation.writes, 'case.writes', [
|
|
385
|
-
'forbidden'
|
|
386
|
-
])
|
|
387
|
-
},
|
|
388
|
-
clock: {
|
|
389
|
-
now: instant(clock.now, 'case.clock.now'),
|
|
390
|
-
timezone: zone(clock.timezone, 'case.clock.timezone')
|
|
391
|
-
},
|
|
392
|
-
allowed_effects: strings(root.allowed_effects, 'case.allowed_effects', true),
|
|
393
|
-
forbidden_effects: strings(root.forbidden_effects, 'case.forbidden_effects'),
|
|
394
|
-
budget: {
|
|
395
|
-
max_seconds: finite(budget.max_seconds, 'case.max_seconds'),
|
|
396
|
-
max_cost_usd: finite(budget.max_cost_usd, 'case.max_cost_usd'),
|
|
397
|
-
max_tool_calls: integer(budget.max_tool_calls, 'case.max_tool_calls'),
|
|
398
|
-
max_turns: integer(budget.max_turns, 'case.max_turns')
|
|
399
|
-
},
|
|
400
|
-
scorer_revision: revision(root.scorer_revision, 'case.scorer_revision'),
|
|
401
|
-
public_safety: {
|
|
402
|
-
contains_third_party_prompt: false,
|
|
403
|
-
contains_gold: false,
|
|
404
|
-
contains_oracle: false,
|
|
405
|
-
contains_private_data: false
|
|
406
|
-
}
|
|
407
|
-
};
|
|
116
|
+
exact(safety, 'case.public_safety', ['contains_third_party_prompt', 'contains_gold', 'contains_oracle', 'contains_private_data']);
|
|
117
|
+
for (const [key, flag] of Object.entries(safety))
|
|
118
|
+
if (boolean(flag, `case.public_safety.${key}`) !== false)
|
|
119
|
+
throw new Error(`case.public_safety.${key} must be false`);
|
|
120
|
+
const out = { schema_version: lit(root.schema_version, 'case.schema_version', ['gotry_eval_case_v0']), case_id: id(root.case_id, 'case.case_id'), benchmark_id: lit(root.benchmark_id, 'case.benchmark_id', BENCHMARK_IDS), input_ref: { kind: lit(input.kind, 'case.input_ref.kind', ['gotry_owned_synthetic', 'external_opaque_reference']), revision: revision(input.revision, 'case.input_ref.revision'), digest_sha256: digest(input.digest_sha256, 'case.input_ref.digest_sha256') }, isolation: { state_root: lit(isolation.state_root, 'case.state_root', ['ephemeral']), network: lit(isolation.network, 'case.network', ['denied', 'benchmark_declared']), writes: lit(isolation.writes, 'case.writes', ['forbidden']) }, clock: { now: instant(clock.now, 'case.clock.now'), timezone: zone(clock.timezone, 'case.clock.timezone') }, allowed_effects: strings(root.allowed_effects, 'case.allowed_effects', true), forbidden_effects: strings(root.forbidden_effects, 'case.forbidden_effects'), budget: { max_seconds: finite(budget.max_seconds, 'case.max_seconds'), max_cost_usd: finite(budget.max_cost_usd, 'case.max_cost_usd'), max_tool_calls: integer(budget.max_tool_calls, 'case.max_tool_calls'), max_turns: integer(budget.max_turns, 'case.max_turns') }, scorer_revision: revision(root.scorer_revision, 'case.scorer_revision'), public_safety: { contains_third_party_prompt: false, contains_gold: false, contains_oracle: false, contains_private_data: false } };
|
|
408
121
|
assertPublicArtifactSafe(out, 'case');
|
|
409
122
|
return out;
|
|
410
123
|
}
|
|
411
|
-
function optionalDigest(value, label) {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
function numberMap(value, label, integers = false) {
|
|
415
|
-
const root = obj(value, label);
|
|
416
|
-
return Object.fromEntries(Object.entries(root).map(([key, item])=>[
|
|
417
|
-
id(key, `${label}.key`),
|
|
418
|
-
integers ? integer(item, `${label}.${key}`) : finite(item, `${label}.${key}`, Number.NEGATIVE_INFINITY)
|
|
419
|
-
]));
|
|
420
|
-
}
|
|
124
|
+
function optionalDigest(value, label) { return value === null ? null : digest(value, label); }
|
|
125
|
+
// The only schema-free object maps are native_metrics and evidence counts. Their leaves are numeric;
|
|
126
|
+
// native_metrics keys close exactly to registry receipt_key values in parseEvaluationFoundation.
|
|
127
|
+
function numberMap(value, label, integers = false) { const root = obj(value, label); return Object.fromEntries(Object.entries(root).map(([key, item]) => [id(key, `${label}.key`), integers ? integer(item, `${label}.${key}`) : finite(item, `${label}.${key}`, Number.NEGATIVE_INFINITY)])); }
|
|
421
128
|
export function parseEvalRunReceipt(value) {
|
|
422
129
|
const root = obj(value, 'run');
|
|
423
|
-
exact(root, 'run', [
|
|
424
|
-
'schema_version',
|
|
425
|
-
'run_id',
|
|
426
|
-
'benchmark_id',
|
|
427
|
-
'case_id',
|
|
428
|
-
'evidence_kind',
|
|
429
|
-
'pairing',
|
|
430
|
-
'status',
|
|
431
|
-
'started_at',
|
|
432
|
-
'finished_at',
|
|
433
|
-
'gotry_sha',
|
|
434
|
-
'model',
|
|
435
|
-
'controls',
|
|
436
|
-
'qualification',
|
|
437
|
-
'experiment',
|
|
438
|
-
'native_metrics',
|
|
439
|
-
'guardrails',
|
|
440
|
-
'evidence_summary'
|
|
441
|
-
]);
|
|
130
|
+
exact(root, 'run', ['schema_version', 'run_id', 'benchmark_id', 'case_id', 'evidence_kind', 'pairing', 'status', 'started_at', 'finished_at', 'gotry_sha', 'model', 'controls', 'qualification', 'experiment', 'native_metrics', 'guardrails', 'evidence_summary']);
|
|
442
131
|
const model = obj(root.model, 'run.model');
|
|
443
|
-
exact(model, 'run.model', [
|
|
444
|
-
'provider',
|
|
445
|
-
'model'
|
|
446
|
-
]);
|
|
132
|
+
exact(model, 'run.model', ['provider', 'model']);
|
|
447
133
|
const controls = obj(root.controls, 'run.controls');
|
|
448
|
-
const controlKeys = [
|
|
449
|
-
'case_set_sha256',
|
|
450
|
-
'protocol_control_sha256',
|
|
451
|
-
'model_parameters_sha256',
|
|
452
|
-
'scorer_sha256',
|
|
453
|
-
'tool_snapshot_sha256',
|
|
454
|
-
'source_fence_sha256',
|
|
455
|
-
'integrity_sha256',
|
|
456
|
-
'official_evaluator_sha256'
|
|
457
|
-
];
|
|
134
|
+
const controlKeys = ['case_set_sha256', 'protocol_control_sha256', 'model_parameters_sha256', 'scorer_sha256', 'tool_snapshot_sha256', 'source_fence_sha256', 'integrity_sha256', 'official_evaluator_sha256'];
|
|
458
135
|
exact(controls, 'run.controls', controlKeys);
|
|
459
136
|
const qualification = obj(root.qualification, 'run.qualification');
|
|
460
|
-
exact(qualification, 'run.qualification', [
|
|
461
|
-
'official_result',
|
|
462
|
-
'source_fence_passed',
|
|
463
|
-
'integrity_passed',
|
|
464
|
-
'evidence_receipts'
|
|
465
|
-
]);
|
|
137
|
+
exact(qualification, 'run.qualification', ['official_result', 'source_fence_passed', 'integrity_passed', 'evidence_receipts']);
|
|
466
138
|
const receipts = obj(qualification.evidence_receipts, 'run.qualification.evidence_receipts');
|
|
467
|
-
exact(receipts, 'run.qualification.evidence_receipts', [
|
|
468
|
-
'official_evaluator_output_sha256',
|
|
469
|
-
'source_fence_audit_sha256',
|
|
470
|
-
'integrity_audit_sha256'
|
|
471
|
-
]);
|
|
139
|
+
exact(receipts, 'run.qualification.evidence_receipts', ['official_evaluator_output_sha256', 'source_fence_audit_sha256', 'integrity_audit_sha256']);
|
|
472
140
|
const experiment = obj(root.experiment, 'run.experiment');
|
|
473
|
-
exact(experiment, 'run.experiment', [
|
|
474
|
-
'changed_variables',
|
|
475
|
-
'candidate_sha256'
|
|
476
|
-
]);
|
|
141
|
+
exact(experiment, 'run.experiment', ['changed_variables', 'candidate_sha256']);
|
|
477
142
|
const guard = obj(root.guardrails, 'run.guardrails');
|
|
478
|
-
exact(guard, 'run.guardrails', [
|
|
479
|
-
'hard_violation_count',
|
|
480
|
-
'forbidden_leakage_hits',
|
|
481
|
-
'latency_ms',
|
|
482
|
-
'cost_usd',
|
|
483
|
-
'tool_calls',
|
|
484
|
-
'turns'
|
|
485
|
-
]);
|
|
143
|
+
exact(guard, 'run.guardrails', ['hard_violation_count', 'forbidden_leakage_hits', 'latency_ms', 'cost_usd', 'tool_calls', 'turns']);
|
|
486
144
|
const summary = obj(root.evidence_summary, 'run.evidence_summary');
|
|
487
|
-
exact(summary, 'run.evidence_summary', [
|
|
488
|
-
'artifact_classification',
|
|
489
|
-
'sha256',
|
|
490
|
-
'counts',
|
|
491
|
-
'reason_codes',
|
|
492
|
-
'fixture_only',
|
|
493
|
-
'statement'
|
|
494
|
-
]);
|
|
145
|
+
exact(summary, 'run.evidence_summary', ['artifact_classification', 'sha256', 'counts', 'reason_codes', 'fixture_only', 'statement']);
|
|
495
146
|
let pairing = null;
|
|
496
147
|
if (root.pairing !== null) {
|
|
497
148
|
const item = obj(root.pairing, 'run.pairing');
|
|
498
|
-
exact(item, 'run.pairing', [
|
|
499
|
-
|
|
500
|
-
'role',
|
|
501
|
-
'counterpart_run_id'
|
|
502
|
-
]);
|
|
503
|
-
pairing = {
|
|
504
|
-
pair_id: id(item.pair_id, 'run.pair_id'),
|
|
505
|
-
role: lit(item.role, 'run.role', [
|
|
506
|
-
'baseline',
|
|
507
|
-
'treatment'
|
|
508
|
-
]),
|
|
509
|
-
counterpart_run_id: id(item.counterpart_run_id, 'run.counterpart')
|
|
510
|
-
};
|
|
149
|
+
exact(item, 'run.pairing', ['pair_id', 'role', 'counterpart_run_id']);
|
|
150
|
+
pairing = { pair_id: id(item.pair_id, 'run.pair_id'), role: lit(item.role, 'run.role', ['baseline', 'treatment']), counterpart_run_id: id(item.counterpart_run_id, 'run.counterpart') };
|
|
511
151
|
}
|
|
512
|
-
const evidence_kind = lit(root.evidence_kind, 'run.evidence_kind', [
|
|
513
|
-
|
|
514
|
-
'observed_external'
|
|
515
|
-
]);
|
|
516
|
-
const status = lit(root.status, 'run.status', [
|
|
517
|
-
'running',
|
|
518
|
-
'succeeded',
|
|
519
|
-
'failed',
|
|
520
|
-
'blocked'
|
|
521
|
-
]);
|
|
152
|
+
const evidence_kind = lit(root.evidence_kind, 'run.evidence_kind', ['synthetic_fixture', 'observed_external']);
|
|
153
|
+
const status = lit(root.status, 'run.status', ['running', 'succeeded', 'failed', 'blocked']);
|
|
522
154
|
const finished_at = root.finished_at === null ? null : instant(root.finished_at, 'run.finished_at');
|
|
523
|
-
if (status === 'running' !== (finished_at === null))
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
pairing,
|
|
533
|
-
status,
|
|
534
|
-
started_at: instant(root.started_at, 'run.started_at'),
|
|
535
|
-
finished_at,
|
|
536
|
-
gotry_sha: commit(root.gotry_sha, 'run.gotry_sha'),
|
|
537
|
-
model: {
|
|
538
|
-
provider: text(model.provider, 'run.provider'),
|
|
539
|
-
model: text(model.model, 'run.model')
|
|
540
|
-
},
|
|
541
|
-
controls: Object.fromEntries(controlKeys.map((key)=>[
|
|
542
|
-
key,
|
|
543
|
-
digest(controls[key], `run.controls.${key}`)
|
|
544
|
-
])),
|
|
545
|
-
qualification: {
|
|
546
|
-
official_result: boolean(qualification.official_result, 'run.official_result'),
|
|
547
|
-
source_fence_passed: boolean(qualification.source_fence_passed, 'run.source_fence_passed'),
|
|
548
|
-
integrity_passed: boolean(qualification.integrity_passed, 'run.integrity_passed'),
|
|
549
|
-
evidence_receipts: {
|
|
550
|
-
official_evaluator_output_sha256: optionalDigest(receipts.official_evaluator_output_sha256, 'run.evaluator_receipt'),
|
|
551
|
-
source_fence_audit_sha256: optionalDigest(receipts.source_fence_audit_sha256, 'run.fence_receipt'),
|
|
552
|
-
integrity_audit_sha256: optionalDigest(receipts.integrity_audit_sha256, 'run.integrity_receipt')
|
|
553
|
-
}
|
|
554
|
-
},
|
|
555
|
-
experiment: {
|
|
556
|
-
changed_variables: strings(experiment.changed_variables, 'run.changed_variables', true),
|
|
557
|
-
candidate_sha256: digest(experiment.candidate_sha256, 'run.candidate')
|
|
558
|
-
},
|
|
559
|
-
native_metrics: numberMap(root.native_metrics, 'run.native_metrics'),
|
|
560
|
-
guardrails: {
|
|
561
|
-
hard_violation_count: integer(guard.hard_violation_count, 'run.hard'),
|
|
562
|
-
forbidden_leakage_hits: integer(guard.forbidden_leakage_hits, 'run.leakage'),
|
|
563
|
-
latency_ms: finite(guard.latency_ms, 'run.latency'),
|
|
564
|
-
cost_usd: finite(guard.cost_usd, 'run.cost'),
|
|
565
|
-
tool_calls: integer(guard.tool_calls, 'run.tools'),
|
|
566
|
-
turns: integer(guard.turns, 'run.turns')
|
|
567
|
-
},
|
|
568
|
-
evidence_summary: {
|
|
569
|
-
artifact_classification: lit(summary.artifact_classification, 'run.classification', [
|
|
570
|
-
'public_safe'
|
|
571
|
-
]),
|
|
572
|
-
sha256: strings(summary.sha256, 'run.sha256', true).map((item, index)=>digest(item, `run.sha256[${index}]`)),
|
|
573
|
-
counts: numberMap(summary.counts, 'run.counts', true),
|
|
574
|
-
reason_codes: strings(summary.reason_codes, 'run.reason_codes', true),
|
|
575
|
-
fixture_only: boolean(summary.fixture_only, 'run.fixture_only'),
|
|
576
|
-
statement: text(summary.statement, 'run.statement')
|
|
577
|
-
}
|
|
578
|
-
};
|
|
579
|
-
if (out.finished_at !== null && new Date(out.finished_at).valueOf() < new Date(out.started_at).valueOf()) throw new Error('run.finished_at must not precede started_at');
|
|
580
|
-
if (evidence_kind === 'synthetic_fixture' && (pairing !== null || out.qualification.official_result || out.qualification.source_fence_passed || out.qualification.integrity_passed || Object.values(out.qualification.evidence_receipts).some(Boolean) || !out.evidence_summary.fixture_only)) throw new Error('synthetic fixture must be unmatched, unqualified, receipt-free, and fixture-only');
|
|
581
|
-
if (evidence_kind === 'observed_external' && out.evidence_summary.fixture_only) throw new Error('observed_external receipt must set fixture_only=false');
|
|
155
|
+
if ((status === 'running') !== (finished_at === null))
|
|
156
|
+
throw new Error('run.finished_at must be null only while running');
|
|
157
|
+
const out = { schema_version: lit(root.schema_version, 'run.schema_version', ['gotry_eval_run_receipt_v0']), run_id: id(root.run_id, 'run.run_id'), benchmark_id: lit(root.benchmark_id, 'run.benchmark_id', BENCHMARK_IDS), case_id: id(root.case_id, 'run.case_id'), evidence_kind, pairing, status, started_at: instant(root.started_at, 'run.started_at'), finished_at, gotry_sha: commit(root.gotry_sha, 'run.gotry_sha'), model: { provider: text(model.provider, 'run.provider'), model: text(model.model, 'run.model') }, controls: Object.fromEntries(controlKeys.map(key => [key, digest(controls[key], `run.controls.${key}`)])), qualification: { official_result: boolean(qualification.official_result, 'run.official_result'), source_fence_passed: boolean(qualification.source_fence_passed, 'run.source_fence_passed'), integrity_passed: boolean(qualification.integrity_passed, 'run.integrity_passed'), evidence_receipts: { official_evaluator_output_sha256: optionalDigest(receipts.official_evaluator_output_sha256, 'run.evaluator_receipt'), source_fence_audit_sha256: optionalDigest(receipts.source_fence_audit_sha256, 'run.fence_receipt'), integrity_audit_sha256: optionalDigest(receipts.integrity_audit_sha256, 'run.integrity_receipt') } }, experiment: { changed_variables: strings(experiment.changed_variables, 'run.changed_variables', true), candidate_sha256: digest(experiment.candidate_sha256, 'run.candidate') }, native_metrics: numberMap(root.native_metrics, 'run.native_metrics'), guardrails: { hard_violation_count: integer(guard.hard_violation_count, 'run.hard'), forbidden_leakage_hits: integer(guard.forbidden_leakage_hits, 'run.leakage'), latency_ms: finite(guard.latency_ms, 'run.latency'), cost_usd: finite(guard.cost_usd, 'run.cost'), tool_calls: integer(guard.tool_calls, 'run.tools'), turns: integer(guard.turns, 'run.turns') }, evidence_summary: { artifact_classification: lit(summary.artifact_classification, 'run.classification', ['public_safe']), sha256: strings(summary.sha256, 'run.sha256', true).map((item, index) => digest(item, `run.sha256[${index}]`)), counts: numberMap(summary.counts, 'run.counts', true), reason_codes: strings(summary.reason_codes, 'run.reason_codes', true), fixture_only: boolean(summary.fixture_only, 'run.fixture_only'), statement: text(summary.statement, 'run.statement') } };
|
|
158
|
+
if (out.finished_at !== null && new Date(out.finished_at).valueOf() < new Date(out.started_at).valueOf())
|
|
159
|
+
throw new Error('run.finished_at must not precede started_at');
|
|
160
|
+
if (evidence_kind === 'synthetic_fixture' && (pairing !== null || out.qualification.official_result || out.qualification.source_fence_passed || out.qualification.integrity_passed || Object.values(out.qualification.evidence_receipts).some(Boolean) || !out.evidence_summary.fixture_only))
|
|
161
|
+
throw new Error('synthetic fixture must be unmatched, unqualified, receipt-free, and fixture-only');
|
|
162
|
+
if (evidence_kind === 'observed_external' && out.evidence_summary.fixture_only)
|
|
163
|
+
throw new Error('observed_external receipt must set fixture_only=false');
|
|
582
164
|
assertPublicArtifactSafe(out, 'run');
|
|
583
165
|
return out;
|
|
584
166
|
}
|
|
585
|
-
export function parseEvalFailureCluster(value) {
|
|
586
|
-
|
|
587
|
-
exact(root, 'failure', [
|
|
588
|
-
'schema_version',
|
|
589
|
-
'cluster_id',
|
|
590
|
-
'category',
|
|
591
|
-
'severity',
|
|
592
|
-
'benchmark_ids',
|
|
593
|
-
'case_ids',
|
|
594
|
-
'run_ids',
|
|
595
|
-
'reproduction',
|
|
596
|
-
'falsifiable_hypothesis',
|
|
597
|
-
'gotry_regression_id',
|
|
598
|
-
'suggested_surface',
|
|
599
|
-
'state'
|
|
600
|
-
]);
|
|
601
|
-
const reproduction = obj(root.reproduction, 'failure.reproduction');
|
|
602
|
-
exact(reproduction, 'failure.reproduction', [
|
|
603
|
-
'condition',
|
|
604
|
-
'minimum_repetitions',
|
|
605
|
-
'observed_repetitions'
|
|
606
|
-
]);
|
|
607
|
-
const out = {
|
|
608
|
-
schema_version: lit(root.schema_version, 'failure.schema_version', [
|
|
609
|
-
'gotry_eval_failure_cluster_v0'
|
|
610
|
-
]),
|
|
611
|
-
cluster_id: id(root.cluster_id, 'failure.cluster_id'),
|
|
612
|
-
category: lit(root.category, 'failure.category', FAILURE_CATEGORIES),
|
|
613
|
-
severity: lit(root.severity, 'failure.severity', [
|
|
614
|
-
'P0',
|
|
615
|
-
'P1',
|
|
616
|
-
'P2',
|
|
617
|
-
'P3'
|
|
618
|
-
]),
|
|
619
|
-
benchmark_ids: strings(root.benchmark_ids, 'failure.benchmark_ids').map((item)=>lit(item, 'failure.benchmark', BENCHMARK_IDS)),
|
|
620
|
-
case_ids: strings(root.case_ids, 'failure.case_ids'),
|
|
621
|
-
run_ids: strings(root.run_ids, 'failure.run_ids'),
|
|
622
|
-
reproduction: {
|
|
623
|
-
condition: text(reproduction.condition, 'failure.condition'),
|
|
624
|
-
minimum_repetitions: integer(reproduction.minimum_repetitions, 'failure.minimum', 1),
|
|
625
|
-
observed_repetitions: integer(reproduction.observed_repetitions, 'failure.observed')
|
|
626
|
-
},
|
|
627
|
-
falsifiable_hypothesis: text(root.falsifiable_hypothesis, 'failure.hypothesis'),
|
|
628
|
-
gotry_regression_id: id(root.gotry_regression_id, 'failure.regression'),
|
|
629
|
-
suggested_surface: lit(root.suggested_surface, 'failure.surface', [
|
|
630
|
-
'agent',
|
|
631
|
-
'tool_policy',
|
|
632
|
-
'deterministic',
|
|
633
|
-
'evaluation'
|
|
634
|
-
]),
|
|
635
|
-
state: lit(root.state, 'failure.state', [
|
|
636
|
-
'observed',
|
|
637
|
-
'confirmed',
|
|
638
|
-
'resolved',
|
|
639
|
-
'rejected'
|
|
640
|
-
])
|
|
641
|
-
};
|
|
642
|
-
if (out.state === 'confirmed' && out.reproduction.observed_repetitions < out.reproduction.minimum_repetitions) throw new Error('confirmed failure lacks repetitions');
|
|
643
|
-
assertPublicArtifactSafe(out, 'failure');
|
|
644
|
-
return out;
|
|
645
|
-
}
|
|
167
|
+
export function parseEvalFailureCluster(value) { const root = obj(value, 'failure'); exact(root, 'failure', ['schema_version', 'cluster_id', 'category', 'severity', 'benchmark_ids', 'case_ids', 'run_ids', 'reproduction', 'falsifiable_hypothesis', 'gotry_regression_id', 'suggested_surface', 'state']); const reproduction = obj(root.reproduction, 'failure.reproduction'); exact(reproduction, 'failure.reproduction', ['condition', 'minimum_repetitions', 'observed_repetitions']); const out = { schema_version: lit(root.schema_version, 'failure.schema_version', ['gotry_eval_failure_cluster_v0']), cluster_id: id(root.cluster_id, 'failure.cluster_id'), category: lit(root.category, 'failure.category', FAILURE_CATEGORIES), severity: lit(root.severity, 'failure.severity', ['P0', 'P1', 'P2', 'P3']), benchmark_ids: strings(root.benchmark_ids, 'failure.benchmark_ids').map(item => lit(item, 'failure.benchmark', BENCHMARK_IDS)), case_ids: strings(root.case_ids, 'failure.case_ids'), run_ids: strings(root.run_ids, 'failure.run_ids'), reproduction: { condition: text(reproduction.condition, 'failure.condition'), minimum_repetitions: integer(reproduction.minimum_repetitions, 'failure.minimum', 1), observed_repetitions: integer(reproduction.observed_repetitions, 'failure.observed') }, falsifiable_hypothesis: text(root.falsifiable_hypothesis, 'failure.hypothesis'), gotry_regression_id: id(root.gotry_regression_id, 'failure.regression'), suggested_surface: lit(root.suggested_surface, 'failure.surface', ['agent', 'tool_policy', 'deterministic', 'evaluation']), state: lit(root.state, 'failure.state', ['observed', 'confirmed', 'resolved', 'rejected']) }; if (out.state === 'confirmed' && out.reproduction.observed_repetitions < out.reproduction.minimum_repetitions)
|
|
168
|
+
throw new Error('confirmed failure lacks repetitions'); assertPublicArtifactSafe(out, 'failure'); return out; }
|
|
646
169
|
export function parseEvaluationFoundation(value) {
|
|
647
170
|
const root = obj(value, 'foundation');
|
|
648
|
-
exact(root, 'foundation', [
|
|
649
|
-
'registry',
|
|
650
|
-
'cases',
|
|
651
|
-
'run_receipts',
|
|
652
|
-
'failure_clusters'
|
|
653
|
-
]);
|
|
171
|
+
exact(root, 'foundation', ['registry', 'cases', 'run_receipts', 'failure_clusters']);
|
|
654
172
|
const registry = parseBenchmarkRegistry(root.registry);
|
|
655
|
-
if (!Array.isArray(root.cases) || !Array.isArray(root.run_receipts) || !Array.isArray(root.failure_clusters))
|
|
173
|
+
if (!Array.isArray(root.cases) || !Array.isArray(root.run_receipts) || !Array.isArray(root.failure_clusters))
|
|
174
|
+
throw new Error('foundation collections must be arrays');
|
|
656
175
|
const cases = root.cases.map(parseEvalCase), runs = root.run_receipts.map(parseEvalRunReceipt), failures = root.failure_clusters.map(parseEvalFailureCluster);
|
|
657
|
-
const unique = (values, label)=>{
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
unique(
|
|
661
|
-
unique(
|
|
662
|
-
|
|
663
|
-
const
|
|
664
|
-
item.case_id,
|
|
665
|
-
item
|
|
666
|
-
])), runById = new Map(runs.map((item)=>[
|
|
667
|
-
item.run_id,
|
|
668
|
-
item
|
|
669
|
-
])), registryById = new Map(registry.map((item)=>[
|
|
670
|
-
item.benchmark_id,
|
|
671
|
-
item
|
|
672
|
-
]));
|
|
673
|
-
for (const run of runs){
|
|
176
|
+
const unique = (values, label) => { if (new Set(values).size !== values.length)
|
|
177
|
+
throw new Error(`${label} ids must be unique`); };
|
|
178
|
+
unique(cases.map(item => item.case_id), 'case');
|
|
179
|
+
unique(runs.map(item => item.run_id), 'run');
|
|
180
|
+
unique(failures.map(item => item.cluster_id), 'failure');
|
|
181
|
+
const caseById = new Map(cases.map(item => [item.case_id, item])), runById = new Map(runs.map(item => [item.run_id, item])), registryById = new Map(registry.map(item => [item.benchmark_id, item]));
|
|
182
|
+
for (const run of runs) {
|
|
674
183
|
const linked = caseById.get(run.case_id), entry = registryById.get(run.benchmark_id);
|
|
675
|
-
if (!linked || linked.benchmark_id !== run.benchmark_id || !entry)
|
|
676
|
-
|
|
677
|
-
|
|
184
|
+
if (!linked || linked.benchmark_id !== run.benchmark_id || !entry)
|
|
185
|
+
throw new Error(`run ${run.run_id} must close to case and registry`);
|
|
186
|
+
const metricKeys = Object.keys(run.native_metrics).sort(), declared = entry.native_metrics.values.map(metric => metric.receipt_key).sort();
|
|
187
|
+
if (JSON.stringify(metricKeys) !== JSON.stringify(declared))
|
|
188
|
+
throw new Error(`run ${run.run_id} metric keys must equal registry receipt keys`);
|
|
678
189
|
}
|
|
679
|
-
for (const failure of failures){
|
|
680
|
-
const linkedCases = failure.case_ids.map(
|
|
681
|
-
if (linkedCases.some(
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
if (actual.size !== failure.benchmark_ids.length || failure.benchmark_ids.some((key)=>!actual.has(key))) throw new Error(`failure ${failure.cluster_id} benchmark closure must equal linked case and run benchmarks`);
|
|
190
|
+
for (const failure of failures) {
|
|
191
|
+
const linkedCases = failure.case_ids.map(key => caseById.get(key)), linkedRuns = failure.run_ids.map(key => runById.get(key));
|
|
192
|
+
if (linkedCases.some(item => !item) || linkedRuns.some(item => !item))
|
|
193
|
+
throw new Error(`failure ${failure.cluster_id} has unclosed links`);
|
|
194
|
+
const actual = new Set([...linkedCases, ...linkedRuns].map(item => item.benchmark_id));
|
|
195
|
+
if (actual.size !== failure.benchmark_ids.length || failure.benchmark_ids.some(key => !actual.has(key)))
|
|
196
|
+
throw new Error(`failure ${failure.cluster_id} benchmark closure must equal linked case and run benchmarks`);
|
|
687
197
|
}
|
|
688
|
-
return {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
const expected = kind === 'official_evaluator' ? run.qualification.evidence_receipts.official_evaluator_output_sha256 : kind === 'source_fence_audit' ? run.qualification.evidence_receipts.source_fence_audit_sha256 : run.qualification.evidence_receipts.integrity_audit_sha256;
|
|
704
|
-
if (!expected) throw new Error(`pair ${run.run_id} lacks qualification evidence receipts`);
|
|
705
|
-
const value = obj(resolver.resolve(expected), `${kind} artifact`);
|
|
706
|
-
const common = [
|
|
707
|
-
'schema_version',
|
|
708
|
-
'artifact_kind',
|
|
709
|
-
'run_id',
|
|
710
|
-
'benchmark_id',
|
|
711
|
-
'case_id',
|
|
712
|
-
'run_binding_sha256'
|
|
713
|
-
];
|
|
714
|
-
const specific = kind === 'official_evaluator' ? [
|
|
715
|
-
'evaluator_sha256',
|
|
716
|
-
'native_metrics_sha256',
|
|
717
|
-
'native_metrics',
|
|
718
|
-
'official_result'
|
|
719
|
-
] : kind === 'source_fence_audit' ? [
|
|
720
|
-
'source_fence_sha256',
|
|
721
|
-
'input_digest_sha256',
|
|
722
|
-
'source_fence_passed',
|
|
723
|
-
'forbidden_field_hits'
|
|
724
|
-
] : [
|
|
725
|
-
'integrity_sha256',
|
|
726
|
-
'candidate_sha256',
|
|
727
|
-
'integrity_passed'
|
|
728
|
-
];
|
|
729
|
-
exact(value, `${kind} artifact`, [
|
|
730
|
-
...common,
|
|
731
|
-
...specific
|
|
732
|
-
]);
|
|
733
|
-
lit(value.schema_version, `${kind}.schema_version`, [
|
|
734
|
-
'gotry_eval_evidence_artifact_v0'
|
|
735
|
-
]);
|
|
736
|
-
if (value.artifact_kind !== kind) throw new Error(`${kind} artifact kind mismatch`);
|
|
737
|
-
if (typeof value.run_id !== 'string' || typeof value.benchmark_id !== 'string' || typeof value.case_id !== 'string' || typeof value.run_binding_sha256 !== 'string') throw new Error(`${kind} artifact schema types mismatch`);
|
|
738
|
-
assertPublicArtifactSafe(value, ` artifact`);
|
|
739
|
-
if (kind === 'official_evaluator' && (typeof value.official_result !== 'boolean' || typeof value.native_metrics_sha256 !== 'string')) throw new Error(`${kind} artifact schema types mismatch`);
|
|
740
|
-
if (kind === 'source_fence_audit' && (typeof value.source_fence_passed !== 'boolean' || typeof value.forbidden_field_hits !== 'number' || !Number.isInteger(value.forbidden_field_hits))) throw new Error(`${kind} artifact schema types mismatch`);
|
|
741
|
-
if (kind === 'integrity_audit' && typeof value.integrity_passed !== 'boolean') throw new Error(`${kind} artifact schema types mismatch`);
|
|
742
|
-
if (evaluationFingerprint(value) !== expected) throw new Error(`pair ${run.run_id} ${kind} artifact fingerprint mismatch`);
|
|
743
|
-
if (value.run_id !== run.run_id || value.benchmark_id !== run.benchmark_id || value.case_id !== run.case_id || value.run_binding_sha256 !== runBinding(run)) throw new Error(`pair ${run.run_id} ${kind} artifact binding mismatch`);
|
|
744
|
-
if (kind === 'official_evaluator' && (value.evaluator_sha256 !== run.controls.official_evaluator_sha256 || value.native_metrics_sha256 !== evaluationFingerprint(run.native_metrics) || value.official_result !== true || JSON.stringify(value.native_metrics) !== JSON.stringify(run.native_metrics))) throw new Error(`pair ${run.run_id} evaluator artifact mismatch`);
|
|
745
|
-
if (kind === 'source_fence_audit' && (value.source_fence_sha256 !== run.controls.source_fence_sha256 || value.source_fence_passed !== true || value.forbidden_field_hits !== 0 || evalCase !== undefined && value.input_digest_sha256 !== evalCase.input_ref.digest_sha256)) throw new Error(`pair ${run.run_id} source-fence artifact mismatch`);
|
|
746
|
-
if (kind === 'integrity_audit' && (value.integrity_sha256 !== run.controls.integrity_sha256 || value.candidate_sha256 !== run.experiment.candidate_sha256 || value.integrity_passed !== true)) throw new Error(`pair ${run.run_id} integrity artifact mismatch`);
|
|
747
|
-
return value;
|
|
748
|
-
}
|
|
198
|
+
return { registry, cases, run_receipts: runs, failure_clusters: failures };
|
|
199
|
+
}
|
|
200
|
+
function runBinding(run) { const { evidence_receipts: _receipts, ...qualification } = run.qualification; return evaluationFingerprint({ ...run, qualification }); }
|
|
201
|
+
function resolveArtifact(resolver, run, kind, evalCase) { const expected = kind === 'official_evaluator' ? run.qualification.evidence_receipts.official_evaluator_output_sha256 : kind === 'source_fence_audit' ? run.qualification.evidence_receipts.source_fence_audit_sha256 : run.qualification.evidence_receipts.integrity_audit_sha256; if (!expected)
|
|
202
|
+
throw new Error(`pair ${run.run_id} lacks qualification evidence receipts`); const value = obj(resolver.resolve(expected), `${kind} artifact`); const common = ['schema_version', 'artifact_kind', 'run_id', 'benchmark_id', 'case_id', 'run_binding_sha256']; const specific = kind === 'official_evaluator' ? ['evaluator_sha256', 'native_metrics_sha256', 'native_metrics', 'official_result'] : kind === 'source_fence_audit' ? ['source_fence_sha256', 'input_digest_sha256', 'source_fence_passed', 'forbidden_field_hits'] : ['integrity_sha256', 'candidate_sha256', 'integrity_passed']; exact(value, `${kind} artifact`, [...common, ...specific]); lit(value.schema_version, `${kind}.schema_version`, ['gotry_eval_evidence_artifact_v0']); if (value.artifact_kind !== kind)
|
|
203
|
+
throw new Error(`${kind} artifact kind mismatch`); if (typeof value.run_id !== 'string' || typeof value.benchmark_id !== 'string' || typeof value.case_id !== 'string' || typeof value.run_binding_sha256 !== 'string')
|
|
204
|
+
throw new Error(`${kind} artifact schema types mismatch`); assertPublicArtifactSafe(value, ` artifact`); if (kind === 'official_evaluator' && (typeof value.official_result !== 'boolean' || typeof value.native_metrics_sha256 !== 'string'))
|
|
205
|
+
throw new Error(`${kind} artifact schema types mismatch`); if (kind === 'source_fence_audit' && (typeof value.source_fence_passed !== 'boolean' || typeof value.forbidden_field_hits !== 'number' || !Number.isInteger(value.forbidden_field_hits)))
|
|
206
|
+
throw new Error(`${kind} artifact schema types mismatch`); if (kind === 'integrity_audit' && typeof value.integrity_passed !== 'boolean')
|
|
207
|
+
throw new Error(`${kind} artifact schema types mismatch`); if (evaluationFingerprint(value) !== expected)
|
|
208
|
+
throw new Error(`pair ${run.run_id} ${kind} artifact fingerprint mismatch`); if (value.run_id !== run.run_id || value.benchmark_id !== run.benchmark_id || value.case_id !== run.case_id || value.run_binding_sha256 !== runBinding(run))
|
|
209
|
+
throw new Error(`pair ${run.run_id} ${kind} artifact binding mismatch`); if (kind === 'official_evaluator' && (value.evaluator_sha256 !== run.controls.official_evaluator_sha256 || value.native_metrics_sha256 !== evaluationFingerprint(run.native_metrics) || value.official_result !== true || JSON.stringify(value.native_metrics) !== JSON.stringify(run.native_metrics)))
|
|
210
|
+
throw new Error(`pair ${run.run_id} evaluator artifact mismatch`); if (kind === 'source_fence_audit' && (value.source_fence_sha256 !== run.controls.source_fence_sha256 || value.source_fence_passed !== true || value.forbidden_field_hits !== 0 || (evalCase !== undefined && value.input_digest_sha256 !== evalCase.input_ref.digest_sha256)))
|
|
211
|
+
throw new Error(`pair ${run.run_id} source-fence artifact mismatch`); if (kind === 'integrity_audit' && (value.integrity_sha256 !== run.controls.integrity_sha256 || value.candidate_sha256 !== run.experiment.candidate_sha256 || value.integrity_passed !== true))
|
|
212
|
+
throw new Error(`pair ${run.run_id} integrity artifact mismatch`); return value; }
|
|
749
213
|
export function deriveMatchedPairs(foundation, resolver) {
|
|
750
214
|
const groups = new Map();
|
|
751
|
-
for (const run of foundation.run_receipts)
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
]);
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
if (members.length !== 2) throw new Error(`pair ${pairId} must contain exactly two receipts`);
|
|
766
|
-
const baseline = members.find((item)=>item.pairing?.role === 'baseline'), treatment = members.find((item)=>item.pairing?.role === 'treatment');
|
|
767
|
-
if (!baseline || !treatment) throw new Error(`pair ${pairId} must contain opposite baseline and treatment roles`);
|
|
768
|
-
if (baseline.pairing.counterpart_run_id !== treatment.run_id || treatment.pairing.counterpart_run_id !== baseline.run_id) throw new Error(`pair ${pairId} counterpart references must be reciprocal`);
|
|
769
|
-
if (baseline.benchmark_id !== treatment.benchmark_id || baseline.case_id !== treatment.case_id) throw new Error(`pair ${pairId} must share benchmark and case`);
|
|
215
|
+
for (const run of foundation.run_receipts)
|
|
216
|
+
if (run.pairing)
|
|
217
|
+
groups.set(run.pairing.pair_id, [...(groups.get(run.pairing.pair_id) ?? []), run]);
|
|
218
|
+
const registry = new Map(foundation.registry.map(item => [item.benchmark_id, item])), cases = new Map(foundation.cases.map(item => [item.case_id, item]));
|
|
219
|
+
return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([pairId, members]) => {
|
|
220
|
+
if (members.length !== 2)
|
|
221
|
+
throw new Error(`pair ${pairId} must contain exactly two receipts`);
|
|
222
|
+
const baseline = members.find(item => item.pairing?.role === 'baseline'), treatment = members.find(item => item.pairing?.role === 'treatment');
|
|
223
|
+
if (!baseline || !treatment)
|
|
224
|
+
throw new Error(`pair ${pairId} must contain opposite baseline and treatment roles`);
|
|
225
|
+
if (baseline.pairing.counterpart_run_id !== treatment.run_id || treatment.pairing.counterpart_run_id !== baseline.run_id)
|
|
226
|
+
throw new Error(`pair ${pairId} counterpart references must be reciprocal`);
|
|
227
|
+
if (baseline.benchmark_id !== treatment.benchmark_id || baseline.case_id !== treatment.case_id)
|
|
228
|
+
throw new Error(`pair ${pairId} must share benchmark and case`);
|
|
770
229
|
const entry = registry.get(baseline.benchmark_id), evalCase = cases.get(baseline.case_id);
|
|
771
|
-
if (entry.countability_default !== 'countable_if_qualified')
|
|
772
|
-
|
|
773
|
-
if (
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (baseline.status !== 'succeeded' || treatment.status !== 'succeeded')
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
if (baseline.controls.
|
|
791
|
-
|
|
792
|
-
if (baseline.
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
if (baseline.
|
|
797
|
-
|
|
798
|
-
for (const run of [
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
treatment_variable: 'gotry_sha',
|
|
803
|
-
gotry_sha: run.gotry_sha
|
|
804
|
-
})) throw new Error(`pair ${pairId} candidate fingerprint does not close to gotry_sha`);
|
|
805
|
-
for (const run of [
|
|
806
|
-
baseline,
|
|
807
|
-
treatment
|
|
808
|
-
]){
|
|
230
|
+
if (entry.countability_default !== 'countable_if_qualified')
|
|
231
|
+
throw new Error(`pair ${pairId} benchmark is diagnostic only`);
|
|
232
|
+
if (evalCase.input_ref.kind !== 'external_opaque_reference')
|
|
233
|
+
throw new Error(`pair ${pairId} requires an external opaque case reference`);
|
|
234
|
+
if ([baseline, treatment].some(run => run.evidence_kind !== 'observed_external'))
|
|
235
|
+
throw new Error(`pair ${pairId} requires observed_external evidence`);
|
|
236
|
+
if (baseline.status !== 'succeeded' || treatment.status !== 'succeeded')
|
|
237
|
+
throw new Error(`pair ${pairId} requires succeeded terminal receipts`);
|
|
238
|
+
if (baseline.model.provider !== treatment.model.provider || baseline.model.model !== treatment.model.model)
|
|
239
|
+
throw new Error(`pair ${pairId} model identity must match`);
|
|
240
|
+
for (const key of ['case_set_sha256', 'protocol_control_sha256', 'model_parameters_sha256', 'scorer_sha256', 'tool_snapshot_sha256', 'source_fence_sha256', 'integrity_sha256', 'official_evaluator_sha256'])
|
|
241
|
+
if (baseline.controls[key] !== treatment.controls[key])
|
|
242
|
+
throw new Error(`pair ${pairId} control mismatch: ${key}`);
|
|
243
|
+
if (baseline.controls.scorer_sha256 !== evaluationFingerprint(evalCase.scorer_revision))
|
|
244
|
+
throw new Error(`pair ${pairId} scorer fingerprint does not close`);
|
|
245
|
+
if (baseline.controls.case_set_sha256 !== evaluationFingerprint(foundation.cases.filter(item => item.benchmark_id === baseline.benchmark_id)))
|
|
246
|
+
throw new Error(`pair ${pairId} case-set fingerprint does not close`);
|
|
247
|
+
if (baseline.controls.source_fence_sha256 !== evaluationFingerprint(entry.source_fence))
|
|
248
|
+
throw new Error(`pair ${pairId} source-fence fingerprint does not close`);
|
|
249
|
+
if (baseline.controls.official_evaluator_sha256 !== evaluationFingerprint(entry.provenance.evaluator))
|
|
250
|
+
throw new Error(`pair ${pairId} evaluator fingerprint does not close`);
|
|
251
|
+
if (baseline.experiment.changed_variables.length !== 0 || JSON.stringify(treatment.experiment.changed_variables) !== JSON.stringify(['gotry_sha']))
|
|
252
|
+
throw new Error(`pair ${pairId} treatment variable must be exactly gotry_sha`);
|
|
253
|
+
if (baseline.gotry_sha === treatment.gotry_sha)
|
|
254
|
+
throw new Error(`pair ${pairId} baseline and treatment gotry_sha must differ`);
|
|
255
|
+
if (baseline.guardrails.hard_violation_count !== 0 || treatment.guardrails.hard_violation_count !== 0 || baseline.guardrails.forbidden_leakage_hits !== 0 || treatment.guardrails.forbidden_leakage_hits !== 0)
|
|
256
|
+
throw new Error(`pair ${pairId} hard violation or leakage`);
|
|
257
|
+
for (const run of [baseline, treatment])
|
|
258
|
+
if (run.experiment.candidate_sha256 !== evaluationFingerprint({ treatment_variable: 'gotry_sha', gotry_sha: run.gotry_sha }))
|
|
259
|
+
throw new Error(`pair ${pairId} candidate fingerprint does not close to gotry_sha`);
|
|
260
|
+
for (const run of [baseline, treatment]) {
|
|
809
261
|
const receipts = Object.values(run.qualification.evidence_receipts);
|
|
810
|
-
if (!run.qualification.official_result || !run.qualification.source_fence_passed || !run.qualification.integrity_passed || receipts.some(
|
|
262
|
+
if (!run.qualification.official_result || !run.qualification.source_fence_passed || !run.qualification.integrity_passed || receipts.some(item => item === null))
|
|
263
|
+
throw new Error(`pair ${pairId} lacks qualification evidence receipts`);
|
|
811
264
|
const evaluator = resolveArtifact(resolver, run, 'official_evaluator'), fence = resolveArtifact(resolver, run, 'source_fence_audit', evalCase), integrity = resolveArtifact(resolver, run, 'integrity_audit');
|
|
812
265
|
}
|
|
813
|
-
return {
|
|
814
|
-
schema_version: 'gotry_eval_matched_pair_derived_v0',
|
|
815
|
-
pair_id: pairId,
|
|
816
|
-
benchmark_id: baseline.benchmark_id,
|
|
817
|
-
case_id: baseline.case_id,
|
|
818
|
-
baseline_run_id: baseline.run_id,
|
|
819
|
-
treatment_run_id: treatment.run_id,
|
|
820
|
-
treatment_variable: 'gotry_sha',
|
|
821
|
-
matched_pair_countable: true
|
|
822
|
-
};
|
|
266
|
+
return { schema_version: 'gotry_eval_matched_pair_derived_v0', pair_id: pairId, benchmark_id: baseline.benchmark_id, case_id: baseline.case_id, baseline_run_id: baseline.run_id, treatment_run_id: treatment.run_id, treatment_variable: 'gotry_sha', matched_pair_countable: true };
|
|
823
267
|
});
|
|
824
268
|
}
|
|
825
|
-
export function parseMutationVectors(value) {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
'value',
|
|
841
|
-
'expected_error'
|
|
842
|
-
] : [
|
|
843
|
-
'id',
|
|
844
|
-
'foundation_kind',
|
|
845
|
-
'target',
|
|
846
|
-
'operation',
|
|
847
|
-
'path',
|
|
848
|
-
'value_kind',
|
|
849
|
-
'expected_error'
|
|
850
|
-
];
|
|
851
|
-
exact(root, `mutation[${index}]`, keys);
|
|
852
|
-
return {
|
|
853
|
-
id: id(root.id, 'mutation.id'),
|
|
854
|
-
foundation_kind: lit(root.foundation_kind, 'mutation.foundation_kind', [
|
|
855
|
-
'diagnostic_repository',
|
|
856
|
-
'countable_test_only'
|
|
857
|
-
]),
|
|
858
|
-
target: lit(root.target, 'mutation.target', [
|
|
859
|
-
'registry',
|
|
860
|
-
'case',
|
|
861
|
-
'run',
|
|
862
|
-
'failure',
|
|
863
|
-
'foundation'
|
|
864
|
-
]),
|
|
865
|
-
operation: lit(root.operation, 'mutation.operation', [
|
|
866
|
-
'replace',
|
|
867
|
-
'remove',
|
|
868
|
-
'append'
|
|
869
|
-
]),
|
|
870
|
-
path: text(root.path, 'mutation.path'),
|
|
871
|
-
value_kind: kind,
|
|
872
|
-
...kind === 'literal' ? {
|
|
873
|
-
value: root.value
|
|
874
|
-
} : {},
|
|
875
|
-
expected_error: text(root.expected_error, 'mutation.expected_error')
|
|
876
|
-
};
|
|
877
|
-
});
|
|
878
|
-
}
|
|
879
|
-
export function applyMutationVector(foundation, vector) {
|
|
880
|
-
const cloned = JSON.parse(JSON.stringify(foundation));
|
|
881
|
-
const selected = vector.target === 'foundation' ? cloned : vector.target === 'registry' ? cloned.registry : vector.target === 'case' ? cloned.cases[0] : vector.target === 'run' ? cloned.run_receipts[0] : cloned.failure_clusters[0];
|
|
882
|
-
let value = vector.value;
|
|
883
|
-
if (vector.value_kind === 'synthetic_absolute_path') value = [
|
|
884
|
-
'',
|
|
885
|
-
'Users',
|
|
886
|
-
'fixture',
|
|
887
|
-
'private.json'
|
|
888
|
-
].join('/');
|
|
889
|
-
if (vector.value_kind === 'nan') value = Number.NaN;
|
|
890
|
-
if (vector.value_kind === 'duplicate_treatment_receipt') {
|
|
891
|
-
const run = structuredClone(cloned.run_receipts[1]);
|
|
892
|
-
run.run_id = 'run:trek:treatment-duplicate';
|
|
893
|
-
value = run;
|
|
894
|
-
}
|
|
895
|
-
const parts = vector.path.split('.').filter(Boolean);
|
|
896
|
-
let parent = selected;
|
|
897
|
-
for (const part of parts.slice(0, -1))parent = parent[part];
|
|
898
|
-
const leaf = parts.at(-1);
|
|
899
|
-
if (vector.operation === 'remove') Array.isArray(parent) ? parent.splice(Number(leaf), 1) : delete parent[leaf];
|
|
900
|
-
else if (vector.operation === 'append') parent[leaf].push(value);
|
|
901
|
-
else parent[leaf] = value;
|
|
902
|
-
return selected;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
//# sourceURL=ts/src/evaluation-contracts.ts
|
|
269
|
+
export function parseMutationVectors(value) { if (!Array.isArray(value))
|
|
270
|
+
throw new Error('mutation vectors must be an array'); return value.map((raw, index) => { const root = obj(raw, `mutation[${index}]`), kind = lit(root.value_kind, 'mutation.value_kind', ['literal', 'synthetic_absolute_path', 'nan', 'duplicate_treatment_receipt']), keys = kind === 'literal' ? ['id', 'foundation_kind', 'target', 'operation', 'path', 'value_kind', 'value', 'expected_error'] : ['id', 'foundation_kind', 'target', 'operation', 'path', 'value_kind', 'expected_error']; exact(root, `mutation[${index}]`, keys); return { id: id(root.id, 'mutation.id'), foundation_kind: lit(root.foundation_kind, 'mutation.foundation_kind', ['diagnostic_repository', 'countable_test_only']), target: lit(root.target, 'mutation.target', ['registry', 'case', 'run', 'failure', 'foundation']), operation: lit(root.operation, 'mutation.operation', ['replace', 'remove', 'append']), path: text(root.path, 'mutation.path'), value_kind: kind, ...(kind === 'literal' ? { value: root.value } : {}), expected_error: text(root.expected_error, 'mutation.expected_error') }; }); }
|
|
271
|
+
export function applyMutationVector(foundation, vector) { const cloned = JSON.parse(JSON.stringify(foundation)); const selected = vector.target === 'foundation' ? cloned : vector.target === 'registry' ? cloned.registry : vector.target === 'case' ? cloned.cases[0] : vector.target === 'run' ? cloned.run_receipts[0] : cloned.failure_clusters[0]; let value = vector.value; if (vector.value_kind === 'synthetic_absolute_path')
|
|
272
|
+
value = ['', 'Users', 'fixture', 'private.json'].join('/'); if (vector.value_kind === 'nan')
|
|
273
|
+
value = Number.NaN; if (vector.value_kind === 'duplicate_treatment_receipt') {
|
|
274
|
+
const run = structuredClone(cloned.run_receipts[1]);
|
|
275
|
+
run.run_id = 'run:trek:treatment-duplicate';
|
|
276
|
+
value = run;
|
|
277
|
+
} const parts = vector.path.split('.').filter(Boolean); let parent = selected; for (const part of parts.slice(0, -1))
|
|
278
|
+
parent = parent[part]; const leaf = parts.at(-1); if (vector.operation === 'remove')
|
|
279
|
+
Array.isArray(parent) ? parent.splice(Number(leaf), 1) : delete parent[leaf];
|
|
280
|
+
else if (vector.operation === 'append')
|
|
281
|
+
parent[leaf].push(value);
|
|
282
|
+
else
|
|
283
|
+
parent[leaf] = value; return selected; }
|