engineering-behavior-observatory 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +151 -0
- package/contracts/codex-app-server-0.150.1/manifest.json +25 -0
- package/contracts/codex-app-server-0.150.1/schema/InitializeParams.json +84 -0
- package/contracts/codex-app-server-0.150.1/schema/JSONRPCMessage.json +137 -0
- package/contracts/codex-app-server-0.150.1/schema/v2/ThreadReadParams.json +17 -0
- package/contracts/codex-app-server-0.150.1/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
- package/contracts/codex-app-server-0.150.1/schema/v2/TurnInterruptParams.json +17 -0
- package/contracts/codex-app-server-0.150.1/types/AskForApproval.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/ClientNotification.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/SandboxMode.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/ThreadReadParams.ts +9 -0
- package/contracts/codex-app-server-0.150.1/types/TokenUsageBreakdown.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/TurnInterruptParams.ts +5 -0
- package/contracts/codex-app-server-0.153.4/manifest.json +27 -0
- package/contracts/codex-app-server-0.153.4/schema/JSONRPCMessage.json +137 -0
- package/contracts/codex-app-server-0.153.4/schema/v1/InitializeParams.json +84 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/ThreadReadParams.json +17 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/ThreadStartParams.json +515 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/TurnInterruptParams.json +17 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/TurnStartParams.json +921 -0
- package/contracts/codex-app-server-0.153.4/types/AskForApproval.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/ClientNotification.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/SandboxMode.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/ThreadReadParams.ts +12 -0
- package/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.ts +5 -0
- package/contracts/openhands-agent-server-v1.44.1.json +65 -0
- package/contracts/openhands-agent-server-v1.46.0.json +46 -0
- package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.d.ts +9 -0
- package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.d.ts +3 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.d.ts +1 -0
- package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.d.ts +10 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.d.ts +8 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.d.ts +4 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.js +2 -0
- package/dist/src/agent-sdk-normalizer.d.ts +22 -0
- package/dist/src/agent-sdk-normalizer.js +995 -0
- package/dist/src/agent-sdk-run.d.ts +31 -0
- package/dist/src/agent-sdk-run.js +242 -0
- package/dist/src/agent-sdk-runner.d.ts +96 -0
- package/dist/src/agent-sdk-runner.js +407 -0
- package/dist/src/agent-sdk.d.ts +294 -0
- package/dist/src/agent-sdk.js +644 -0
- package/dist/src/aggregation.d.ts +183 -0
- package/dist/src/aggregation.js +513 -0
- package/dist/src/artifacts.d.ts +36 -0
- package/dist/src/artifacts.js +1372 -0
- package/dist/src/atlas-grafana.d.ts +377 -0
- package/dist/src/atlas-grafana.js +47 -0
- package/dist/src/atlas-html.d.ts +2 -0
- package/dist/src/atlas-html.js +34 -0
- package/dist/src/atlas.d.ts +130 -0
- package/dist/src/atlas.js +235 -0
- package/dist/src/behavior-assertions.d.ts +95 -0
- package/dist/src/behavior-assertions.js +154 -0
- package/dist/src/cli.d.ts +5 -0
- package/dist/src/cli.js +957 -0
- package/dist/src/codex-judge.d.ts +44 -0
- package/dist/src/codex-judge.js +214 -0
- package/dist/src/codex-run.d.ts +114 -0
- package/dist/src/codex-run.js +519 -0
- package/dist/src/codex.d.ts +187 -0
- package/dist/src/codex.js +1028 -0
- package/dist/src/contracts.d.ts +155 -0
- package/dist/src/contracts.js +866 -0
- package/dist/src/corpus.d.ts +67 -0
- package/dist/src/corpus.js +540 -0
- package/dist/src/cursor-sdk-runner.d.ts +81 -0
- package/dist/src/cursor-sdk-runner.js +362 -0
- package/dist/src/cursor-sdk.d.ts +81 -0
- package/dist/src/cursor-sdk.js +1078 -0
- package/dist/src/deepseek-adapter.d.ts +152 -0
- package/dist/src/deepseek-adapter.js +777 -0
- package/dist/src/exports.d.ts +69 -0
- package/dist/src/exports.js +966 -0
- package/dist/src/human-calibration.d.ts +167 -0
- package/dist/src/human-calibration.js +618 -0
- package/dist/src/index.d.ts +71 -0
- package/dist/src/index.js +38 -0
- package/dist/src/lifecycle.d.ts +196 -0
- package/dist/src/lifecycle.js +1889 -0
- package/dist/src/normalization-integrity.d.ts +128 -0
- package/dist/src/normalization-integrity.js +429 -0
- package/dist/src/openhands-run.d.ts +36 -0
- package/dist/src/openhands-run.js +415 -0
- package/dist/src/openhands.d.ts +130 -0
- package/dist/src/openhands.js +859 -0
- package/dist/src/pi.d.ts +203 -0
- package/dist/src/pi.js +1345 -0
- package/dist/src/process-protocol.d.ts +245 -0
- package/dist/src/process-protocol.js +1261 -0
- package/dist/src/retained-evidence.d.ts +12 -0
- package/dist/src/retained-evidence.js +201 -0
- package/dist/src/run-bundles.d.ts +193 -0
- package/dist/src/run-bundles.js +993 -0
- package/dist/src/scheduler.d.ts +109 -0
- package/dist/src/scheduler.js +1080 -0
- package/dist/src/semantic-judge.d.ts +194 -0
- package/dist/src/semantic-judge.js +875 -0
- package/dist/src/structural-observations.d.ts +124 -0
- package/dist/src/structural-observations.js +671 -0
- package/dist/src/task-packets.d.ts +115 -0
- package/dist/src/task-packets.js +683 -0
- package/dist/src/uniform-events.d.ts +120 -0
- package/dist/src/uniform-events.js +158 -0
- package/dist/src/verifiers.d.ts +116 -0
- package/dist/src/verifiers.js +819 -0
- package/dist/src/workspaces.d.ts +56 -0
- package/dist/src/workspaces.js +1314 -0
- package/docs/README.md +60 -0
- package/docs/development/README.md +71 -0
- package/docs/development/documentation-sync.md +58 -0
- package/docs/development/extension-contracts.md +307 -0
- package/docs/evaluation/README.md +48 -0
- package/docs/evaluation/aggregation.md +112 -0
- package/docs/evaluation/behavior-assertions.md +45 -0
- package/docs/evaluation/human-calibration.md +170 -0
- package/docs/evaluation/normalization-integrity.md +80 -0
- package/docs/evaluation/semantic-judge.md +188 -0
- package/docs/evaluation/structural-observations.md +84 -0
- package/docs/evaluation/uniform-events.md +131 -0
- package/docs/guides/atlas.md +144 -0
- package/docs/guides/evidence-and-sharing.md +100 -0
- package/docs/guides/operator-guide.md +424 -0
- package/docs/guides/quickstart.md +152 -0
- package/docs/guides/telemetry.md +66 -0
- package/docs/harnesses/README.md +31 -0
- package/docs/harnesses/claude-agent-sdk.md +61 -0
- package/docs/harnesses/codex-harness.md +174 -0
- package/docs/harnesses/cursor-sdk.md +149 -0
- package/docs/harnesses/deepseek-harness.md +134 -0
- package/docs/harnesses/openhands-agent-server.md +153 -0
- package/docs/harnesses/pi-sdk.md +66 -0
- package/docs/reference/README.md +19 -0
- package/docs/reference/agent-sdk-operational-runner.md +118 -0
- package/docs/reference/cli.md +114 -0
- package/docs/reference/contracts.md +222 -0
- package/docs/reference/run-bundle-contract.md +354 -0
- package/docs/reference/run-lifecycle.md +54 -0
- package/examples/cursor-sdk/README.md +11 -0
- package/examples/cursor-sdk/capture-profile.json +1 -0
- package/examples/cursor-sdk/harness.json +1 -0
- package/examples/cursor-sdk/model.json +1 -0
- package/examples/cursor-sdk/native-limits.json +1 -0
- package/examples/cursor-sdk/native-tool-policy.json +1 -0
- package/ontology/behavior-categories.v1.json +46 -0
- package/package.json +66 -0
- package/release/0.1.0/KNOWN_LIMITATIONS.md +35 -0
- package/release/0.1.0/README.md +46 -0
- package/release/0.1.0/reproducibility.json +61 -0
- package/release/0.2.0/KNOWN_LIMITATIONS.md +40 -0
- package/release/0.2.0/README.md +42 -0
- package/release/0.2.0/reproducibility.json +73 -0
- package/release/0.2.1/KNOWN_LIMITATIONS.md +18 -0
- package/release/0.2.1/README.md +63 -0
- package/release/0.2.1/reproducibility.json +73 -0
- package/release/README.md +13 -0
- package/schemas/aggregation.v1.json +293 -0
- package/schemas/behavior-assertions.v1.json +220 -0
- package/schemas/deepseek-runtime-composition.v1.schema.json +122 -0
- package/schemas/experiment.v1.schema.json +188 -0
- package/schemas/human-calibration.v1.json +336 -0
- package/schemas/normalization-integrity.v1.json +302 -0
- package/schemas/run-bundles/v1.json +1040 -0
- package/schemas/run-queue.v1.schema.json +206 -0
- package/schemas/semantic-judge.v1.json +380 -0
- package/schemas/structural-observations.v1.json +131 -0
- package/schemas/task-packet-freeze.v1.schema.json +106 -0
- package/schemas/task-packet.v1.schema.json +234 -0
- package/schemas/uniform-events/v1.json +289 -0
- package/scripts/atlas-grafana.sh +18 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { join, resolve } from "node:path";
|
|
2
|
+
import { canonicalizeMetadata, digestBytes, digestMetadata, validateArtifact } from "./artifacts.js";
|
|
3
|
+
import { validateRetainedBehaviorAssertion } from "./behavior-assertions.js";
|
|
4
|
+
import { effectiveReviewOutcome, isDisputedReviewOutcome, revalidateReviewSample, validateReviewHistory, } from "./human-calibration.js";
|
|
5
|
+
import { assessComparisonEligibility } from "./normalization-integrity.js";
|
|
6
|
+
import { readBoundedFile } from "./scheduler.js";
|
|
7
|
+
import { createRetainedStructuralObservationSet } from "./structural-observations.js";
|
|
8
|
+
export function aggregateEvaluation(input, policy) {
|
|
9
|
+
return aggregateValidatedEvaluation(input, policy);
|
|
10
|
+
}
|
|
11
|
+
export function selectAggregationAttempts(entries, policy) {
|
|
12
|
+
if (!["all-attempts", "latest-attempt-per-run"].includes(policy))
|
|
13
|
+
throw new Error("Invalid attempt selection policy.");
|
|
14
|
+
return selectAttempts(uniqueAttempts(entries).attempts, policy);
|
|
15
|
+
}
|
|
16
|
+
async function aggregateValidatedEvaluation(input, policy) {
|
|
17
|
+
assertPolicy(policy);
|
|
18
|
+
const { attempts, duplicates } = uniqueAttempts(input.corpusEntries);
|
|
19
|
+
const selected = selectAttempts(attempts, policy.selectedAttemptPolicy);
|
|
20
|
+
const observationSources = uniqueBy(input.observationSets, ({ document }) => `${document.runId}\0${document.attemptId}`, "observation set");
|
|
21
|
+
const observations = new Map();
|
|
22
|
+
for (const { bundleRoot, document } of observationSources.values()) {
|
|
23
|
+
assertIndexedBundle(bundleRoot, document.runId, document.attemptId, attempts);
|
|
24
|
+
const rebuilt = await createRetainedStructuralObservationSet(bundleRoot);
|
|
25
|
+
const { capabilityProfile: _capabilityProfile, ...legacyNormalization } = rebuilt.normalization;
|
|
26
|
+
const comparable = document.normalization.capabilityProfile === undefined
|
|
27
|
+
? { ...rebuilt, normalization: legacyNormalization }
|
|
28
|
+
: rebuilt;
|
|
29
|
+
if (canonicalizeMetadata(comparable) !== canonicalizeMetadata(document))
|
|
30
|
+
throw new Error(`Structural observation set for attempt "${document.attemptId}" is stale.`);
|
|
31
|
+
observations.set(`${document.runId}\0${document.attemptId}`, rebuilt);
|
|
32
|
+
}
|
|
33
|
+
const assertionSources = uniqueBy(input.assertions, ({ document }) => assertionKey(document), "behavior assertion");
|
|
34
|
+
const assertions = new Map();
|
|
35
|
+
for (const { bundleRoot, document } of assertionSources.values()) {
|
|
36
|
+
assertIndexedBundle(bundleRoot, document.runId, document.attemptId, attempts);
|
|
37
|
+
await validateRetainedBehaviorAssertion(bundleRoot, document);
|
|
38
|
+
assertions.set(assertionKey(document), document);
|
|
39
|
+
}
|
|
40
|
+
for (const { selection } of input.calibrations)
|
|
41
|
+
await revalidateReviewSample(selection);
|
|
42
|
+
const reviewOutcomes = reviewOutcomeIndex(input.calibrations);
|
|
43
|
+
const selectedIds = new Set(selected.map(attemptKey));
|
|
44
|
+
const groups = groupAttempts(attempts, policy.groupBy).map(([dimensions, allMembers]) => {
|
|
45
|
+
const members = allMembers.filter((attempt) => selectedIds.has(attemptKey(attempt)));
|
|
46
|
+
return {
|
|
47
|
+
dimensions,
|
|
48
|
+
metrics: groupMetrics(members, attempts, observations, assertions, reviewOutcomes, dimensions),
|
|
49
|
+
behaviors: behaviorAggregates(members, assertions, reviewOutcomes),
|
|
50
|
+
variations: [
|
|
51
|
+
variation("terminal-state", members, policy.recurrence.minimumOccurrences),
|
|
52
|
+
variation("verifier-status", members, policy.recurrence.minimumOccurrences),
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
const report = {
|
|
57
|
+
schemaVersion: "ebo.aggregation-report/v1",
|
|
58
|
+
policy: {
|
|
59
|
+
groupBy: [...policy.groupBy],
|
|
60
|
+
selectedAttemptPolicy: policy.selectedAttemptPolicy,
|
|
61
|
+
recurrence: {
|
|
62
|
+
minimumOccurrences: policy.recurrence.minimumOccurrences,
|
|
63
|
+
establishesCausality: false,
|
|
64
|
+
establishesStatisticalSignificance: false,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
sourcePopulation: {
|
|
68
|
+
uniqueRuns: new Set(attempts.map(({ runId }) => runId)).size,
|
|
69
|
+
uniqueAttempts: attempts.length,
|
|
70
|
+
selectedAttempts: selected.length,
|
|
71
|
+
duplicateInputsIgnored: duplicates,
|
|
72
|
+
},
|
|
73
|
+
sourceLineage: {
|
|
74
|
+
requestDigest: input.lineage?.requestDigest ?? metadataDigest({ policy, comparisons: input.comparisons.map(({ eligibility: _eligibility, ...comparison }) => comparison) }),
|
|
75
|
+
corpusIndexDigest: input.lineage?.corpusIndexDigest ?? metadataDigest(input.corpusEntries),
|
|
76
|
+
manifests: attempts.map(({ runId, attemptId, manifestDigest: digest }) => ({ runId, attemptId, digest })),
|
|
77
|
+
observationSets: [...observationSources.values()].map(({ document }) => ({ runId: document.runId, attemptId: document.attemptId, digest: metadataDigest(document) })),
|
|
78
|
+
assertions: [...assertionSources.values()].map(({ document }) => ({ runId: document.runId, attemptId: document.attemptId, id: document.id, digest: metadataDigest(document) })),
|
|
79
|
+
calibrations: input.calibrations.map(({ selection, history }) => ({ selectionDigest: metadataDigest(selection), historyDigest: metadataDigest(history) })),
|
|
80
|
+
comparisonGates: input.comparisons.flatMap(({ eligibility }) => eligibility.map(({ request, report }) => ({
|
|
81
|
+
requestDigest: metadataDigest(request),
|
|
82
|
+
reportDigest: metadataDigest(report),
|
|
83
|
+
}))),
|
|
84
|
+
},
|
|
85
|
+
groups,
|
|
86
|
+
comparisons: input.comparisons.map((comparison) => compare(comparison, selected, observations, policy.recurrence.minimumOccurrences)),
|
|
87
|
+
limitations: [
|
|
88
|
+
"All aggregates are descriptive; no causal attribution or statistical significance is claimed.",
|
|
89
|
+
"Observational completion is a terminal-state measure, not task success.",
|
|
90
|
+
"Verifier rates include only verified attempts with an available terminal verifier result.",
|
|
91
|
+
"Human-reviewed assertion rates include only digest-bound calibration selections and validated review lineage.",
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
assertArtifact("aggregation report", report);
|
|
95
|
+
return report;
|
|
96
|
+
}
|
|
97
|
+
function behaviorAggregates(selected, assertions, reviews) {
|
|
98
|
+
const selectedIds = new Set(selected.map(attemptKey));
|
|
99
|
+
const partitions = new Map();
|
|
100
|
+
for (const assertion of assertions.values()) {
|
|
101
|
+
if (!selectedIds.has(attemptKey(assertion)))
|
|
102
|
+
continue;
|
|
103
|
+
const key = canonicalizeMetadata({ behavior: assertion.behavior, rubric: assertion.rubric, evaluator: assertion.evaluator });
|
|
104
|
+
const values = partitions.get(key) ?? [];
|
|
105
|
+
values.push(assertion);
|
|
106
|
+
partitions.set(key, values);
|
|
107
|
+
}
|
|
108
|
+
return [...partitions.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, values]) => {
|
|
109
|
+
const first = values[0];
|
|
110
|
+
const included = new Set();
|
|
111
|
+
const assessments = [];
|
|
112
|
+
const exclusions = [];
|
|
113
|
+
const outcome = (assertion) => reviews.get(assertionBindingKey(assertion)) ?? {
|
|
114
|
+
outcome: assertion.judgment.disposition === "abstained" ? "judge-abstained" : "unreviewed", disputed: false,
|
|
115
|
+
};
|
|
116
|
+
for (const attempt of selected) {
|
|
117
|
+
const candidates = values.filter((assertion) => attemptKey(assertion) === attemptKey(attempt));
|
|
118
|
+
const confirmed = candidates.filter((assertion) => assertion.judgment.disposition === "assessed"
|
|
119
|
+
&& outcome(assertion).outcome === "confirmed" && !outcome(assertion).disputed);
|
|
120
|
+
const judgments = new Set(confirmed.flatMap(({ judgment }) => judgment.disposition === "assessed" ? [judgment.assessment] : []));
|
|
121
|
+
if (judgments.size === 1) {
|
|
122
|
+
assessments.push([...judgments][0]);
|
|
123
|
+
for (const assertion of confirmed)
|
|
124
|
+
included.add(assertionKey(assertion));
|
|
125
|
+
}
|
|
126
|
+
else
|
|
127
|
+
exclusions.push(judgments.size > 1 ? "conflicting-confirmed-reruns" : candidates.length === 0 ? "dimension-assertion-missing" : "no-confirmed-assessment");
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
behavior: structuredClone(first.behavior), rubric: structuredClone(first.rubric), evaluator: structuredClone(first.evaluator),
|
|
131
|
+
population: "confirmed-attempt-dimension",
|
|
132
|
+
assessments: ["constructive", "adverse", "mixed", "context-dependent"].map((assessment) => ({
|
|
133
|
+
assessment,
|
|
134
|
+
measurement: rateMetric(assessment, "attempt", assessments.filter((value) => value === assessment).length, assessments.length, "confirmed-attempt-dimension", exclusionCounts(exclusions, "attempt-dimension")).measurement,
|
|
135
|
+
})),
|
|
136
|
+
assertions: values.map((assertion) => ({ runId: assertion.runId, attemptId: assertion.attemptId, id: assertion.id,
|
|
137
|
+
digest: metadataDigest(assertion), reviewOutcome: outcome(assertion).outcome, disputed: outcome(assertion).disputed,
|
|
138
|
+
included: included.has(assertionKey(assertion)) })),
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function groupMetrics(selected, all, observations, assertions, reviews, dimensions) {
|
|
143
|
+
const selectedIds = new Set(selected.map(attemptKey));
|
|
144
|
+
const relatedAll = all.filter((attempt) => matches(attempt, dimensions));
|
|
145
|
+
const selectionExclusions = exclusionCounts(relatedAll.filter((attempt) => !selectedIds.has(attemptKey(attempt))).map(() => "not-selected-by-attempt-policy"), "attempt");
|
|
146
|
+
const metrics = [
|
|
147
|
+
metric("attempt-count", "attempt", selected.length, relatedAll.length, "attempt", "attempt", selectionExclusions),
|
|
148
|
+
rateMetric("infrastructure-failure-rate", "attempt", selected.filter(({ failureClass }) => failureClass === "infrastructure").length, selected.length, "attempt", []),
|
|
149
|
+
rateMetric("capture-qualified-rate", "attempt", selected.filter(({ captureQualification }) => captureQualification === "qualified" || captureQualification === "qualified-with-gaps").length, selected.filter(({ captureQualification }) => captureQualification !== undefined && captureQualification !== "unavailable").length, "attempt", exclusionCounts(selected.flatMap(({ captureQualification }) => captureQualification === undefined || captureQualification === "unavailable" ? ["capture-qualification-unavailable"] : []), "attempt")),
|
|
150
|
+
rateMetric("terminal-completed-rate", "attempt", selected.filter(({ terminalState }) => terminalState === "completed").length, selected.length, "attempt", []),
|
|
151
|
+
];
|
|
152
|
+
const verified = selected.filter(({ assessmentMode, verifierStatuses }) => assessmentMode === "verified"
|
|
153
|
+
&& verifierStatuses.length === 1 && ["passed", "failed"].includes(verifierStatuses[0]));
|
|
154
|
+
metrics.push(rateMetric("verifier-pass-rate", "attempt", verified.filter(({ verifierStatuses }) => verifierStatuses[0] === "passed").length, verified.length, "verified-attempt", exclusionCounts(selected.flatMap((attempt) => attempt.assessmentMode !== "verified"
|
|
155
|
+
? ["observational-or-unknown-assessment-mode"]
|
|
156
|
+
: attempt.verifierStatuses.length !== 1 || !["passed", "failed"].includes(attempt.verifierStatuses[0])
|
|
157
|
+
? ["verifier-outcome-unavailable-or-infrastructure"] : []), "attempt")));
|
|
158
|
+
const selectedObservationSets = selected.flatMap((attempt) => observations.get(attemptKey(attempt)) ?? []);
|
|
159
|
+
metrics.push(rateMetric("structural-observation-set-availability-rate", "attempt", selectedObservationSets.length, selected.length, "attempt", exclusionCounts(selected.flatMap((attempt) => observations.has(attemptKey(attempt)) ? [] : ["observation-set-missing"]), "attempt")));
|
|
160
|
+
const extractorIds = [...new Set(selectedObservationSets.flatMap(({ observations: values }) => values.map(({ extractor }) => extractor.id)))].sort();
|
|
161
|
+
for (const extractorId of extractorIds) {
|
|
162
|
+
const values = selected.map((attempt) => observations.get(attemptKey(attempt))?.observations.find(({ extractor }) => extractor.id === extractorId)?.value);
|
|
163
|
+
const known = values.flatMap((value) => value?.status === "known" ? [value] : []);
|
|
164
|
+
const units = new Set(known.map(({ unit }) => unit));
|
|
165
|
+
const types = new Set(known.map(({ value }) => typeof value));
|
|
166
|
+
const exclusions = exclusionCounts(values.flatMap((value) => value?.status === "unavailable" ? [value.reason] : value === undefined ? ["observation-set-missing"] : []), "attempt");
|
|
167
|
+
if (units.size > 1 || types.size > 1) {
|
|
168
|
+
metrics.push(unavailableMetric(`structural:${extractorId}`, "attempt", "Structural observation types or units differ within the group.", selected.length, "attempt"));
|
|
169
|
+
}
|
|
170
|
+
else if (types.has("number")) {
|
|
171
|
+
metrics.push(metric(`structural:${extractorId}`, "attempt", known.reduce((sum, { value }) => sum + Number(value), 0), known.length, known[0]?.unit ?? "observation-value", "attempt-with-known-observation", exclusions));
|
|
172
|
+
}
|
|
173
|
+
else if (types.has("boolean")) {
|
|
174
|
+
metrics.push(metric(`structural:${extractorId}`, "attempt", known.filter(({ value }) => value === true).length, known.length, "attempt-with-true-observation", "attempt-with-known-observation", exclusions));
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
for (const value of [...new Set(known.map(({ value }) => String(value)))].sort()) {
|
|
178
|
+
metrics.push(metric(`structural:${extractorId}:${value}`, "attempt", known.filter((knownValue) => knownValue.value === value).length, known.length, "attempt", "attempt-with-known-observation", exclusions));
|
|
179
|
+
}
|
|
180
|
+
if (known.length === 0)
|
|
181
|
+
metrics.push(unavailableMetric(`structural:${extractorId}`, "attempt", "The eligible denominator is empty.", 0, "attempt", exclusions));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const selectedAssertions = [...assertions.values()].filter(({ runId, attemptId }) => selectedIds.has(`${runId}\0${attemptId}`));
|
|
185
|
+
metrics.push(rateMetric("assertion-abstention-rate", "assertion", selectedAssertions.filter(({ judgment }) => judgment.disposition === "abstained").length, selectedAssertions.length, "assertion", []));
|
|
186
|
+
const reviewResults = selectedAssertions.map((assertion) => reviews.get(assertionBindingKey(assertion)) ?? {
|
|
187
|
+
outcome: assertion.judgment.disposition === "abstained" ? "judge-abstained" : "unreviewed",
|
|
188
|
+
disputed: false,
|
|
189
|
+
});
|
|
190
|
+
const outcomes = reviewResults.map(({ outcome }) => outcome);
|
|
191
|
+
const reviewed = outcomes.filter((outcome) => outcome === "confirmed" || outcome === "rejected" || outcome === "unresolved");
|
|
192
|
+
metrics.push(rateMetric("reviewed-assertion-confirmed-rate", "reviewed-assertion", reviewed.filter((outcome) => outcome === "confirmed").length, reviewed.length, "reviewed-assertion", exclusionCounts(outcomes.flatMap((outcome) => outcome === "unreviewed" || outcome === "judge-abstained" ? [outcome] : []), "assertion")));
|
|
193
|
+
metrics.push(rateMetric("review-unresolved-rate", "assertion", outcomes.filter((outcome) => outcome === "unresolved" || outcome === "unreviewed" || outcome === "judge-abstained").length, outcomes.length, "assertion", []));
|
|
194
|
+
metrics.push(rateMetric("review-disputed-rate", "assertion", reviewResults.filter(({ disputed }) => disputed).length, reviewResults.length, "assertion", []));
|
|
195
|
+
return metrics;
|
|
196
|
+
}
|
|
197
|
+
function variation(measure, attempts, recurrenceMinimum) {
|
|
198
|
+
const values = attempts.flatMap((attempt) => measure === "terminal-state"
|
|
199
|
+
? attempt.terminalState === undefined ? [] : [attempt.terminalState]
|
|
200
|
+
: attempt.assessmentMode === "verified" && attempt.verifierStatuses.length === 1 ? [attempt.verifierStatuses[0]] : []);
|
|
201
|
+
const excluded = attempts.length - values.length;
|
|
202
|
+
const counts = new Map();
|
|
203
|
+
for (const value of values)
|
|
204
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
205
|
+
const distribution = [...counts].sort(([left], [right]) => left.localeCompare(right)).map(([value, count]) => rateMetric(`${measure}:${value}`, "attempt", count, values.length, "attempt", excluded === 0 ? [] : [{ reason: `${measure}-unavailable`, count: excluded, unit: "attempt" }]));
|
|
206
|
+
const secondMostFrequent = [...counts.values()].sort((left, right) => right - left)[1] ?? 0;
|
|
207
|
+
return {
|
|
208
|
+
measure,
|
|
209
|
+
distribution,
|
|
210
|
+
claimStatus: values.length === 0 ? "unavailable" : secondMostFrequent === 0 ? "no-variation"
|
|
211
|
+
: secondMostFrequent >= recurrenceMinimum ? "recurring-description" : "case-study",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function compare(comparison, attempts, observations, recurrenceMinimum) {
|
|
215
|
+
const gates = new Map();
|
|
216
|
+
for (const { request, report } of comparison.eligibility) {
|
|
217
|
+
const rebuilt = assessComparisonEligibility(request);
|
|
218
|
+
if (canonicalizeMetadata(rebuilt) !== canonicalizeMetadata(report)) {
|
|
219
|
+
throw new Error(`Comparison "${comparison.id}" has a stale eligibility report.`);
|
|
220
|
+
}
|
|
221
|
+
assertArtifact("comparison report", report);
|
|
222
|
+
const key = candidatePairKey(request.left, request.right);
|
|
223
|
+
const current = gates.get(key);
|
|
224
|
+
if (current !== undefined && canonicalizeMetadata(current) !== canonicalizeMetadata({ request, report })) {
|
|
225
|
+
throw new Error(`Comparison "${comparison.id}" has conflicting eligibility reports for one candidate pair.`);
|
|
226
|
+
}
|
|
227
|
+
gates.set(key, { request, report });
|
|
228
|
+
}
|
|
229
|
+
const left = attempts.filter((attempt) => matches(attempt, comparison.left));
|
|
230
|
+
const right = attempts.filter((attempt) => matches(attempt, comparison.right));
|
|
231
|
+
const exclusions = [
|
|
232
|
+
...left.filter((attempt) => hasMissingDimension(attempt, comparison.matchBy)).map(() => "match-dimension-unavailable"),
|
|
233
|
+
...right.filter((attempt) => hasMissingDimension(attempt, comparison.matchBy)).map(() => "match-dimension-unavailable"),
|
|
234
|
+
];
|
|
235
|
+
const leftByMatch = indexedMatches(left.filter((attempt) => !hasMissingDimension(attempt, comparison.matchBy)), comparison.matchBy);
|
|
236
|
+
const rightByMatch = indexedMatches(right.filter((attempt) => !hasMissingDimension(attempt, comparison.matchBy)), comparison.matchBy);
|
|
237
|
+
const keys = [...new Set([...leftByMatch.keys(), ...rightByMatch.keys()])].sort();
|
|
238
|
+
const differences = [];
|
|
239
|
+
let differenceUnit;
|
|
240
|
+
for (const key of keys) {
|
|
241
|
+
const leftMatches = leftByMatch.get(key) ?? [];
|
|
242
|
+
const rightMatches = rightByMatch.get(key) ?? [];
|
|
243
|
+
if (leftMatches.length !== 1 || rightMatches.length !== 1) {
|
|
244
|
+
exclusions.push(leftMatches.length === 0 || rightMatches.length === 0 ? "unmatched-unit" : "ambiguous-matched-unit");
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const gate = gates.get(candidatePairKey({ id: leftMatches[0].runId, manifestDigest: leftMatches[0].manifestDigest }, { id: rightMatches[0].runId, manifestDigest: rightMatches[0].manifestDigest }));
|
|
248
|
+
if (gate === undefined) {
|
|
249
|
+
exclusions.push("comparison-eligibility-missing");
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (gate.report.status === "unsupported") {
|
|
253
|
+
exclusions.push("comparison-eligibility-unsupported");
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (gate.report.measure !== comparison.measure) {
|
|
257
|
+
exclusions.push("comparison-measure-not-gated");
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (measureCapabilities(comparison.measure, [leftMatches[0], rightMatches[0]], observations)
|
|
261
|
+
.some((capability) => !gate.report.policy.requiredCapabilities.includes(capability))) {
|
|
262
|
+
exclusions.push("comparison-measure-capability-not-gated");
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const leftCandidate = gate.request.left.id === leftMatches[0].runId ? gate.request.left : gate.request.right;
|
|
266
|
+
const rightCandidate = gate.request.left.id === rightMatches[0].runId ? gate.request.left : gate.request.right;
|
|
267
|
+
if (!candidateMatchesAttempt(leftCandidate, leftMatches[0], observations)
|
|
268
|
+
|| !candidateMatchesAttempt(rightCandidate, rightMatches[0], observations)) {
|
|
269
|
+
exclusions.push("comparison-candidate-evidence-mismatch");
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
const leftValue = measureValue(leftMatches[0], comparison.measure, observations);
|
|
273
|
+
const rightValue = measureValue(rightMatches[0], comparison.measure, observations);
|
|
274
|
+
if (leftValue === undefined || rightValue === undefined)
|
|
275
|
+
exclusions.push("measure-unavailable");
|
|
276
|
+
else if (leftValue.unit !== rightValue.unit || differenceUnit !== undefined && differenceUnit !== leftValue.unit)
|
|
277
|
+
exclusions.push("measure-unit-mismatch");
|
|
278
|
+
else {
|
|
279
|
+
differenceUnit = leftValue.unit;
|
|
280
|
+
differences.push(rightValue.value - leftValue.value);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const differing = differences.filter((value) => value !== 0).length;
|
|
284
|
+
const measurement = differences.length === 0
|
|
285
|
+
? unavailableMeasurement("No uniquely matched units have the requested measure on both sides.", 0, "matched-unit", exclusionCounts(exclusions, "matched-unit"))
|
|
286
|
+
: availableMeasurement(differences.reduce((sum, value) => sum + value, 0), differences.length, `right-minus-left-${differenceUnit}`, "matched-unit", exclusionCounts(exclusions, "matched-unit"));
|
|
287
|
+
return {
|
|
288
|
+
id: comparison.id,
|
|
289
|
+
measure: comparison.measure,
|
|
290
|
+
eligibility: comparison.eligibility.map(({ report }) => structuredClone(report)),
|
|
291
|
+
matchedDifference: measurement,
|
|
292
|
+
differingMatchedUnits: differing,
|
|
293
|
+
claimStatus: differences.length === 0 ? "unavailable" : differing === 0 ? "no-difference"
|
|
294
|
+
: differing >= recurrenceMinimum ? "recurring-description" : "case-study",
|
|
295
|
+
limitations: [
|
|
296
|
+
...new Set(comparison.eligibility.flatMap(({ report }) => report.reasons.map(({ detail }) => detail))),
|
|
297
|
+
"Matched differences are descriptive and do not establish causality or statistical significance.",
|
|
298
|
+
],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function candidateMatchesAttempt(candidate, attempt, observations) {
|
|
302
|
+
const exact = candidate.id === attempt.runId
|
|
303
|
+
&& candidate.manifestDigest === attempt.manifestDigest
|
|
304
|
+
&& candidate.task.id === attempt.taskId
|
|
305
|
+
&& candidate.task.digest === attempt.taskDigest
|
|
306
|
+
&& candidate.fixture.id === attempt.fixtureId
|
|
307
|
+
&& candidate.fixture.digest === attempt.fixtureDigest
|
|
308
|
+
&& candidate.model.id === attempt.modelId
|
|
309
|
+
&& candidate.model.configurationDigest === attempt.modelConfigurationDigest
|
|
310
|
+
&& candidate.harness.id === attempt.harnessId
|
|
311
|
+
&& candidate.harness.version === attempt.harnessVersion
|
|
312
|
+
&& candidate.harness.configurationDigest === attempt.harnessConfigurationDigest
|
|
313
|
+
&& candidate.assessmentMode === attempt.assessmentMode
|
|
314
|
+
&& candidate.captureProfileDigest === attempt.captureProfileDigest
|
|
315
|
+
&& candidate.budgetDigest === attempt.budgetDigest
|
|
316
|
+
&& candidate.toolPolicyDigest === attempt.toolPolicyDigest;
|
|
317
|
+
if (!exact)
|
|
318
|
+
return false;
|
|
319
|
+
const observationSet = observations.get(attemptKey(attempt));
|
|
320
|
+
if (observationSet?.normalization.capabilityProfile === undefined)
|
|
321
|
+
return false;
|
|
322
|
+
return candidate.adapterVersion === observationSet.normalization.adapter.version
|
|
323
|
+
&& canonicalizeMetadata(candidate.capabilityProfile) === canonicalizeMetadata(observationSet.normalization.capabilityProfile);
|
|
324
|
+
}
|
|
325
|
+
function measureCapabilities(measure, attempts, observations) {
|
|
326
|
+
if (measure === "attempt:infrastructure-failure" || measure === "attempt:terminal-completed" || measure === "verified:verifier-passed") {
|
|
327
|
+
return ["family:outcome"];
|
|
328
|
+
}
|
|
329
|
+
if (measure.startsWith("structural:")) {
|
|
330
|
+
const extractorId = measure.slice("structural:".length);
|
|
331
|
+
return [...new Set(attempts.flatMap((attempt) => observations.get(attemptKey(attempt))?.observations
|
|
332
|
+
.find(({ extractor }) => extractor.id === extractorId)?.extractor.requiredCapabilities ?? []))];
|
|
333
|
+
}
|
|
334
|
+
throw new Error(`Unsupported aggregate comparison measure "${measure}".`);
|
|
335
|
+
}
|
|
336
|
+
function candidatePairKey(left, right) {
|
|
337
|
+
return canonicalizeMetadata([[left.id, left.manifestDigest], [right.id, right.manifestDigest]]
|
|
338
|
+
.sort((leftValue, rightValue) => canonicalizeMetadata(leftValue).localeCompare(canonicalizeMetadata(rightValue))));
|
|
339
|
+
}
|
|
340
|
+
function measureValue(attempt, measure, observations) {
|
|
341
|
+
if (measure === "attempt:infrastructure-failure")
|
|
342
|
+
return { value: attempt.failureClass === "infrastructure" ? 1 : 0, unit: "attempt" };
|
|
343
|
+
if (measure === "attempt:terminal-completed")
|
|
344
|
+
return attempt.terminalState === undefined ? undefined : { value: attempt.terminalState === "completed" ? 1 : 0, unit: "attempt" };
|
|
345
|
+
if (measure === "verified:verifier-passed")
|
|
346
|
+
return attempt.assessmentMode !== "verified" || attempt.verifierStatuses.length !== 1
|
|
347
|
+
|| !["passed", "failed"].includes(attempt.verifierStatuses[0])
|
|
348
|
+
? undefined : { value: attempt.verifierStatuses[0] === "passed" ? 1 : 0, unit: "verified-attempt" };
|
|
349
|
+
if (measure.startsWith("structural:")) {
|
|
350
|
+
const value = observations.get(attemptKey(attempt))?.observations.find(({ extractor }) => extractor.id === measure.slice("structural:".length))?.value;
|
|
351
|
+
return value?.status === "known" && (typeof value.value === "number" || typeof value.value === "boolean")
|
|
352
|
+
? { value: typeof value.value === "boolean" ? Number(value.value) : value.value, unit: value.unit }
|
|
353
|
+
: undefined;
|
|
354
|
+
}
|
|
355
|
+
throw new Error(`Unsupported aggregate comparison measure "${measure}".`);
|
|
356
|
+
}
|
|
357
|
+
function reviewOutcomeIndex(calibrations) {
|
|
358
|
+
const outcomes = new Map();
|
|
359
|
+
for (const { selection, history } of calibrations) {
|
|
360
|
+
validateReviewHistory(selection, history);
|
|
361
|
+
for (const candidate of selection.candidates) {
|
|
362
|
+
const key = bindingKey(candidate.assertion.id, candidate.assertion.digest);
|
|
363
|
+
const outcome = {
|
|
364
|
+
outcome: effectiveReviewOutcome(candidate, history.decisions),
|
|
365
|
+
disputed: isDisputedReviewOutcome(candidate, history.decisions),
|
|
366
|
+
};
|
|
367
|
+
const current = outcomes.get(key);
|
|
368
|
+
if (current !== undefined && canonicalizeMetadata(current) !== canonicalizeMetadata(outcome))
|
|
369
|
+
throw new Error(`Calibration inputs conflict for assertion "${candidate.assertion.id}".`);
|
|
370
|
+
outcomes.set(key, outcome);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return outcomes;
|
|
374
|
+
}
|
|
375
|
+
function uniqueAttempts(entries) {
|
|
376
|
+
const attempts = new Map();
|
|
377
|
+
let duplicates = 0;
|
|
378
|
+
for (const entry of entries) {
|
|
379
|
+
if (entry.manifestKind !== "run" || entry.runId === undefined || entry.attemptId === undefined)
|
|
380
|
+
continue;
|
|
381
|
+
const key = attemptKey(entry);
|
|
382
|
+
const current = attempts.get(key);
|
|
383
|
+
if (current === undefined)
|
|
384
|
+
attempts.set(key, structuredClone(entry));
|
|
385
|
+
else if (canonicalizeMetadata(current) === canonicalizeMetadata(entry))
|
|
386
|
+
duplicates += 1;
|
|
387
|
+
else
|
|
388
|
+
throw new Error(`Corpus inputs conflict for run "${entry.runId}" attempt "${entry.attemptId}".`);
|
|
389
|
+
}
|
|
390
|
+
return { attempts: [...attempts.values()].sort((left, right) => attemptKey(left).localeCompare(attemptKey(right))), duplicates };
|
|
391
|
+
}
|
|
392
|
+
function assertIndexedBundle(bundleRoot, runId, attemptId, attempts) {
|
|
393
|
+
const indexed = attempts.filter((attempt) => attempt.runId === runId && attempt.attemptId === attemptId);
|
|
394
|
+
if (indexed.length !== 1)
|
|
395
|
+
throw new Error(`Derived source run "${runId}" attempt "${attemptId}" has no unique corpus entry.`);
|
|
396
|
+
const actual = `sha256:${digestBytes(readBoundedFile(join(resolve(bundleRoot), "manifest.json"), "Derived source manifest")).value}`;
|
|
397
|
+
if (actual !== indexed[0].manifestDigest) {
|
|
398
|
+
throw new Error(`Derived source run "${runId}" attempt "${attemptId}" does not match the indexed manifest digest.`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function selectAttempts(attempts, policy) {
|
|
402
|
+
if (policy === "all-attempts")
|
|
403
|
+
return [...attempts];
|
|
404
|
+
const latest = new Map();
|
|
405
|
+
for (const attempt of attempts) {
|
|
406
|
+
const current = latest.get(attempt.runId);
|
|
407
|
+
if (current === undefined || (attempt.attemptNumber ?? -1) > (current.attemptNumber ?? -1))
|
|
408
|
+
latest.set(attempt.runId, attempt);
|
|
409
|
+
else if ((attempt.attemptNumber ?? -1) === (current.attemptNumber ?? -1) && attempt.attemptId !== current.attemptId) {
|
|
410
|
+
throw new Error(`Run "${attempt.runId}" has ambiguous latest attempts.`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return [...latest.values()].sort((left, right) => attemptKey(left).localeCompare(attemptKey(right)));
|
|
414
|
+
}
|
|
415
|
+
function groupAttempts(attempts, dimensions) {
|
|
416
|
+
const groups = new Map();
|
|
417
|
+
for (const attempt of attempts) {
|
|
418
|
+
const values = Object.fromEntries(dimensions.map((dimension) => [dimension, dimensionValue(attempt, dimension) ?? "unavailable"]));
|
|
419
|
+
const key = canonicalizeMetadata(values);
|
|
420
|
+
const group = groups.get(key) ?? [values, []];
|
|
421
|
+
group[1].push(attempt);
|
|
422
|
+
groups.set(key, group);
|
|
423
|
+
}
|
|
424
|
+
return [...groups.values()].sort(([left], [right]) => canonicalizeMetadata(left).localeCompare(canonicalizeMetadata(right)));
|
|
425
|
+
}
|
|
426
|
+
function dimensionValue(attempt, dimension) {
|
|
427
|
+
return ({
|
|
428
|
+
task: attempt.taskId,
|
|
429
|
+
model: attempt.modelId,
|
|
430
|
+
harness: attempt.harnessId,
|
|
431
|
+
trial: attempt.trialId,
|
|
432
|
+
"capture-qualification": attempt.captureQualification,
|
|
433
|
+
})[dimension];
|
|
434
|
+
}
|
|
435
|
+
function matches(attempt, filters) {
|
|
436
|
+
return Object.entries(filters).every(([dimension, value]) => (dimensionValue(attempt, dimension) ?? "unavailable") === value);
|
|
437
|
+
}
|
|
438
|
+
function indexedMatches(attempts, dimensions) {
|
|
439
|
+
const result = new Map();
|
|
440
|
+
for (const attempt of attempts) {
|
|
441
|
+
const key = canonicalizeMetadata(dimensions.map((dimension) => dimensionValue(attempt, dimension) ?? null));
|
|
442
|
+
const values = result.get(key) ?? [];
|
|
443
|
+
values.push(attempt);
|
|
444
|
+
result.set(key, values);
|
|
445
|
+
}
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
function hasMissingDimension(attempt, dimensions) {
|
|
449
|
+
return dimensions.some((dimension) => dimensionValue(attempt, dimension) === undefined);
|
|
450
|
+
}
|
|
451
|
+
function metric(id, population, numerator, denominator, numeratorUnit, denominatorUnit, exclusions) {
|
|
452
|
+
const measurement = denominator === 0
|
|
453
|
+
? unavailableMeasurement("The eligible denominator is empty.", numerator, denominatorUnit, exclusions, numeratorUnit)
|
|
454
|
+
: availableMeasurement(numerator, denominator, numeratorUnit, denominatorUnit, exclusions);
|
|
455
|
+
return { id, population, claimStatus: measurement.status === "available" ? "descriptive" : "unavailable", measurement };
|
|
456
|
+
}
|
|
457
|
+
function rateMetric(id, population, numerator, denominator, unit, exclusions) {
|
|
458
|
+
return metric(id, population, numerator, denominator, unit, unit, exclusions);
|
|
459
|
+
}
|
|
460
|
+
function unavailableMetric(id, population, reason, denominator, unit, exclusions = []) {
|
|
461
|
+
return { id, population, claimStatus: "unavailable", measurement: unavailableMeasurement(reason, 0, unit, exclusions, unit, denominator) };
|
|
462
|
+
}
|
|
463
|
+
function availableMeasurement(numerator, denominator, numeratorUnit, denominatorUnit, exclusions) {
|
|
464
|
+
return { status: "available", numerator: { value: numerator, unit: numeratorUnit }, denominator: { value: denominator, unit: denominatorUnit }, rate: numerator / denominator, exclusions };
|
|
465
|
+
}
|
|
466
|
+
function unavailableMeasurement(reason, numerator, denominatorUnit, exclusions, numeratorUnit = denominatorUnit, denominator = 0) {
|
|
467
|
+
return { status: "unavailable", numerator: { value: numerator, unit: numeratorUnit }, denominator: { value: denominator, unit: denominatorUnit }, exclusions, reason };
|
|
468
|
+
}
|
|
469
|
+
function exclusionCounts(reasons, unit) {
|
|
470
|
+
const counts = new Map();
|
|
471
|
+
for (const reason of reasons)
|
|
472
|
+
counts.set(reason, (counts.get(reason) ?? 0) + 1);
|
|
473
|
+
return [...counts].sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => ({ reason, count, unit }));
|
|
474
|
+
}
|
|
475
|
+
function uniqueBy(values, key, label) {
|
|
476
|
+
const result = new Map();
|
|
477
|
+
for (const value of values) {
|
|
478
|
+
const identity = key(value);
|
|
479
|
+
const current = result.get(identity);
|
|
480
|
+
if (current === undefined)
|
|
481
|
+
result.set(identity, structuredClone(value));
|
|
482
|
+
else if (canonicalizeMetadata(current) !== canonicalizeMetadata(value))
|
|
483
|
+
throw new Error(`Duplicate ${label} identity has conflicting content.`);
|
|
484
|
+
}
|
|
485
|
+
return result;
|
|
486
|
+
}
|
|
487
|
+
function assertPolicy(policy) {
|
|
488
|
+
if (new Set(policy.groupBy).size !== policy.groupBy.length)
|
|
489
|
+
throw new Error("Aggregate grouping dimensions must be unique.");
|
|
490
|
+
if (!Number.isSafeInteger(policy.recurrence.minimumOccurrences) || policy.recurrence.minimumOccurrences < 2) {
|
|
491
|
+
throw new Error("Aggregate recurrence minimum must be an integer of at least two.");
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function assertArtifact(label, value) {
|
|
495
|
+
const errors = validateArtifact(label, value);
|
|
496
|
+
if (errors.length > 0)
|
|
497
|
+
throw new Error(errors.map(({ field, message }) => `${label} ${field}: ${message}`).join("\n"));
|
|
498
|
+
}
|
|
499
|
+
function attemptKey(attempt) {
|
|
500
|
+
return `${attempt.runId}\0${attempt.attemptId}`;
|
|
501
|
+
}
|
|
502
|
+
function assertionKey(assertion) {
|
|
503
|
+
return `${assertion.runId}\0${assertion.attemptId}\0${assertion.id}`;
|
|
504
|
+
}
|
|
505
|
+
function assertionBindingKey(assertion) {
|
|
506
|
+
return bindingKey(assertion.id, `sha256:${digestMetadata(assertion).value}`);
|
|
507
|
+
}
|
|
508
|
+
function bindingKey(id, digest) {
|
|
509
|
+
return `${id}\0${digest}`;
|
|
510
|
+
}
|
|
511
|
+
function metadataDigest(value) {
|
|
512
|
+
return `sha256:${digestMetadata(value).value}`;
|
|
513
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type BundleRootHandle, type Digest } from "./contracts.js";
|
|
2
|
+
export type ArtifactValidationError = {
|
|
3
|
+
artifact: string;
|
|
4
|
+
schemaVersion: string;
|
|
5
|
+
field: string;
|
|
6
|
+
message: string;
|
|
7
|
+
};
|
|
8
|
+
export type ArtifactIdentity = {
|
|
9
|
+
id: string;
|
|
10
|
+
relativePath: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const SUPPORTED_ARTIFACT_SCHEMA_VERSIONS: string[];
|
|
13
|
+
export declare function canonicalizeMetadata(value: unknown): string;
|
|
14
|
+
export declare function digestBytes(bytes: Uint8Array): Digest;
|
|
15
|
+
export declare function digestMetadata(metadata: unknown): Digest;
|
|
16
|
+
export declare function verifyDigest(bytes: Uint8Array, expected: Digest): boolean;
|
|
17
|
+
export declare function assertNoDuplicateJsonKeys(text: string): void;
|
|
18
|
+
export declare function assertUniqueArtifactIdentities(identities: readonly ArtifactIdentity[]): void;
|
|
19
|
+
export declare function validateArtifact(artifact: string, document: unknown): ArtifactValidationError[];
|
|
20
|
+
export declare function validateRunManifestEvidence(artifact: string, manifest: unknown, bundleRoot: string): ArtifactValidationError[];
|
|
21
|
+
export declare function validateExportManifest(artifact: string, exportManifest: unknown, containingManifest: unknown | undefined, bundleRoot?: string): ArtifactValidationError[];
|
|
22
|
+
export declare function readVerifiedArtifact(artifactRoot: string, relativePath: string, expectedDigest: Digest, maxBytes?: number): Promise<Buffer>;
|
|
23
|
+
export declare function inspectRetainedArtifact(artifactRoot: string, relativePath: string): Promise<{
|
|
24
|
+
digest: Digest;
|
|
25
|
+
sizeBytes: number;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function writeMetadataAtomically(artifactRoot: string, relativePath: string, metadata: unknown, signal?: AbortSignal, options?: {
|
|
28
|
+
overwrite?: boolean;
|
|
29
|
+
}): Promise<Digest>;
|
|
30
|
+
export declare function writeArtifactAtomically(artifactRoot: string, relativePath: string, content: Uint8Array, signal?: AbortSignal, options?: {
|
|
31
|
+
overwrite?: boolean;
|
|
32
|
+
}): Promise<Digest>;
|
|
33
|
+
export declare function writeMetadataAtomicallyIfAbsentSync(artifactRoot: string, relativePath: string, metadata: unknown, rootHandle?: BundleRootHandle, beforeDestinationPublish?: () => void, afterDestinationPublish?: () => void): {
|
|
34
|
+
created: boolean;
|
|
35
|
+
digest: Digest;
|
|
36
|
+
};
|