@openpond/evals 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CONTRACT.md +14 -0
  2. package/README.md +6 -1
  3. package/dist/artifact-verification.js +172 -0
  4. package/dist/builtin-benchmarks/harness-refiner.js +469 -24
  5. package/dist/execution-contracts.js +153 -0
  6. package/dist/execution-receipts.js +185 -0
  7. package/dist/graders.js +8 -2
  8. package/dist/index.js +5 -0
  9. package/dist/preferences.js +737 -0
  10. package/dist/rollouts.js +241 -0
  11. package/dist/tasksets.js +24 -0
  12. package/dist/types/artifact-verification.d.ts +31 -0
  13. package/dist/types/artifact-verification.d.ts.map +1 -0
  14. package/dist/types/builtin-benchmarks/harness-refiner.d.ts +22 -0
  15. package/dist/types/builtin-benchmarks/harness-refiner.d.ts.map +1 -1
  16. package/dist/types/conformance.d.ts +44 -0
  17. package/dist/types/conformance.d.ts.map +1 -1
  18. package/dist/types/evidence/conformance.d.ts +24 -24
  19. package/dist/types/evidence/contracts.d.ts +42 -42
  20. package/dist/types/execution-contracts.d.ts +806 -0
  21. package/dist/types/execution-contracts.d.ts.map +1 -0
  22. package/dist/types/execution-receipts.d.ts +55 -0
  23. package/dist/types/execution-receipts.d.ts.map +1 -0
  24. package/dist/types/graders.d.ts.map +1 -1
  25. package/dist/types/index.d.ts +5 -0
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/preferences.d.ts +535 -0
  28. package/dist/types/preferences.d.ts.map +1 -0
  29. package/dist/types/review-conformance.d.ts +20 -15
  30. package/dist/types/review-conformance.d.ts.map +1 -1
  31. package/dist/types/rollouts.d.ts +221 -0
  32. package/dist/types/rollouts.d.ts.map +1 -0
  33. package/dist/types/tasksets.d.ts +89 -0
  34. package/dist/types/tasksets.d.ts.map +1 -1
  35. package/package.json +9 -1
package/CONTRACT.md CHANGED
@@ -17,6 +17,8 @@ runtime processes.
17
17
  | `HarnessRunTrace` | `HarnessTrace` | Preserve ordered actions, observations, lifecycle events, terminal state, failure class, and trace hash. Learning-signal envelopes remain a training projection of the trace and receipt. |
18
18
  | `TaskAttemptResult` | `AttemptReceipt` | Preserve the old record for application persistence while adding a lossless receipt reference. Output becomes `outputHash`; trace and artifacts are separately hash-bound. |
19
19
  | `GradeResult` | `GraderEvidence[]` | Preserve component score, pass, reward eligibility, failure class, feedback, and visible/private evidence references. Aggregate UI results remain host projections. |
20
+ | Human or model comparison of attempts | `PreferenceComparisonRelease` + `ComparisonAssignment` + `PreferenceReceipt` | Keep release policy, blinded candidate ordering, exact attempt/artifact references, reviewer identity, and ranking immutable. Human and model reviewers emit the same receipt; provider sessions, identity details, queue state, and artifact bytes remain host-owned. |
21
+ | Comparative reward | `PreferenceAggregationReceipt` + standard `RewardComponentReceipt` | Aggregate only with the release's named quorum algorithm, calibrate automated reviewers against non-frozen human receipts, then project the named pairwise-win fraction into the existing per-attempt reward component boundary. |
20
22
  | managed-RL local receipt | `AttemptReceipt` | Submit canonical manifest/task/trace/artifact/grader identities; policy token responses and provider request IDs remain host-private trace data. |
21
23
  | resolved training bundle | `HarnessRelease` + host training binding | Agent snapshot, program, lifecycle, tool declarations, files, and grader interface belong to the Harness. Taskset environment/policy/graders and dataset/evidence, recipe, compute, engine, approval, and opaque leases remain explicit host-side bindings. |
22
24
  | completed Work or Development turn | `WorkEvidenceReceipt` | Project the authoritative terminal turn, immutable Agent snapshot when available, model/runtime identity, sanitized trace reference, exact output revisions, validation evidence, interventions, timing, usage, and explicit consent provenance. Keep the raw source and trace host-private. |
@@ -51,6 +53,18 @@ runtime processes.
51
53
  upper bounds.
52
54
  - Immutable content never contains secret values, opaque lease values, mutable
53
55
  provider resource IDs, database keys, UI state, or process handles.
56
+ - Preference comparisons are an additive group-evaluation protocol, not a new
57
+ per-attempt grader kind and not a replacement for benchmark comparisons.
58
+ Releases contain two through four candidates, an ordered-tie-group result
59
+ policy, a frozen rubric artifact, presentation policy, aggregation policy,
60
+ reward projection, and calibration thresholds. Assignments bind every visible
61
+ artifact to an existing attempt and manifest under one Taskset, Harness,
62
+ Environment, Verifier, tool, and policy lineage.
63
+ - A model preference receipt is reward-eligible only when a passed calibration
64
+ report binds the exact comparison release and immutable model-reviewer
65
+ release. Frozen-evaluation assignments cannot contribute calibration or
66
+ training evidence. Invalid or unrenderable candidates remain unscorable;
67
+ they are not silently converted into aesthetic losses.
54
68
  - The Work evidence schemas first ship as additive `0.2.x` package exports. The
55
69
  package version does not replace the `openpond.workEvidenceReceipt.v1`,
56
70
  `openpond.workProcessTrace.v1`, `openpond.workFeedbackReceipt.v1`, or
package/README.md CHANGED
@@ -8,6 +8,11 @@ eligibility, and no-training/SFT/preference/RL qualification receipts. The packa
8
8
  does not re-export Harness APIs. Applications import the two packages directly,
9
9
  which keeps refinement and evaluation authority visibly separate.
10
10
 
11
+ The `@openpond/evals/preferences` subpath adds a bounded, artifact-backed
12
+ comparison protocol: two to four attempts can be ranked by a human or model
13
+ reviewer, calibrated against held-out human receipts, then projected into the
14
+ existing reward-component boundary without encoding a specific visual domain.
15
+
11
16
  ```ts
12
17
  import {
13
18
  AttemptReceiptSchema,
@@ -29,7 +34,7 @@ import {
29
34
  ```
30
35
 
31
36
  Subpath exports are available at `/harness`, `/tasksets`, `/benchmarks`, `/graders`, `/runs`,
32
- `/conformance`, `/evidence`, `/review`, and
37
+ `/conformance`, `/evidence`, `/preferences`, `/review`, and
33
38
  `/model-improvement-qualification`. The package is an evaluation protocol library,
34
39
  not a hosted client. It does not execute OpenPond Desktop or Sandbox sessions,
35
40
  resolve credentials, or persist artifacts.
@@ -0,0 +1,172 @@
1
+ import { ArtifactManifestEntrySchema, RewardComponentReceiptSchema, } from "./execution-contracts.js";
2
+ import { createArtifactManifest } from "./execution-receipts.js";
3
+ export function buildArtifactManifest(input) {
4
+ const byPath = new Map();
5
+ for (const artifact of input.collectedArtifacts) {
6
+ const matches = byPath.get(artifact.path) ?? [];
7
+ matches.push(artifact);
8
+ byPath.set(artifact.path, matches);
9
+ }
10
+ const consumed = new Set();
11
+ const entries = input.requiredOutputs.map((required) => {
12
+ const matches = byPath.get(required.path) ?? [];
13
+ if (matches.length === 0) {
14
+ return ArtifactManifestEntrySchema.parse({
15
+ requiredOutputPath: required.path,
16
+ collectedPath: null,
17
+ declaredMediaType: required.mediaType,
18
+ detectedMediaType: null,
19
+ artifact: null,
20
+ status: "missing",
21
+ parseStatus: "not_requested",
22
+ schemaStatus: "not_requested",
23
+ errorCode: "required_output_missing",
24
+ failureOwner: "policy",
25
+ evidenceRefs: [],
26
+ metadata: { maxBytes: required.maxBytes, schemaRef: required.schemaRef },
27
+ });
28
+ }
29
+ if (matches.length > 1) {
30
+ for (const match of matches)
31
+ consumed.add(match);
32
+ return ArtifactManifestEntrySchema.parse({
33
+ requiredOutputPath: required.path,
34
+ collectedPath: required.path,
35
+ declaredMediaType: required.mediaType,
36
+ detectedMediaType: null,
37
+ artifact: null,
38
+ status: "failed",
39
+ parseStatus: "not_requested",
40
+ schemaStatus: "not_requested",
41
+ errorCode: "artifact_collection_ambiguous",
42
+ failureOwner: "collector",
43
+ evidenceRefs: matches.flatMap((match) => match.evidenceRefs ?? []),
44
+ metadata: { duplicateCount: matches.length },
45
+ });
46
+ }
47
+ const [collected] = matches;
48
+ consumed.add(collected);
49
+ return manifestEntry(required, collected);
50
+ });
51
+ for (const collected of input.collectedArtifacts) {
52
+ if (consumed.has(collected))
53
+ continue;
54
+ entries.push(ArtifactManifestEntrySchema.parse({
55
+ requiredOutputPath: null,
56
+ collectedPath: collected.path,
57
+ declaredMediaType: null,
58
+ detectedMediaType: collected.detectedMediaType,
59
+ artifact: collected.artifact,
60
+ status: collected.status,
61
+ parseStatus: collected.parseStatus ?? "not_requested",
62
+ schemaStatus: collected.schemaStatus ?? "not_requested",
63
+ errorCode: collected.errorCode ?? null,
64
+ failureOwner: collected.failureOwner ?? null,
65
+ evidenceRefs: collected.evidenceRefs ?? [],
66
+ metadata: collected.metadata ?? {},
67
+ }));
68
+ }
69
+ return createArtifactManifest({
70
+ schemaVersion: "openpond.artifactManifest.v1",
71
+ id: input.id,
72
+ attemptRef: input.attemptRef,
73
+ entries,
74
+ createdAt: input.createdAt,
75
+ metadata: input.metadata ?? {},
76
+ });
77
+ }
78
+ export function verifyRequiredOutputs(input) {
79
+ return input.requiredOutputs.map((required) => {
80
+ const entry = input.manifest.entries.find((candidate) => candidate.requiredOutputPath === required.path);
81
+ if (!entry || entry.status === "missing") {
82
+ return requiredOutputComponent(required, entry ?? null, {
83
+ status: "scored",
84
+ score: 0,
85
+ passed: false,
86
+ rewardEligible: true,
87
+ failureOwner: "policy",
88
+ feedback: `Required output ${required.path} was not collected.`,
89
+ });
90
+ }
91
+ if (entry.status === "failed" && entry.failureOwner !== "policy") {
92
+ return requiredOutputComponent(required, entry, {
93
+ status: "unscorable",
94
+ score: null,
95
+ passed: false,
96
+ rewardEligible: false,
97
+ failureOwner: entry.failureOwner ?? "collector",
98
+ feedback: `Required output ${required.path} could not be collected reliably.`,
99
+ });
100
+ }
101
+ const failures = structuralFailures(required, entry);
102
+ return requiredOutputComponent(required, entry, {
103
+ status: "scored",
104
+ score: failures.length === 0 ? 1 : 0,
105
+ passed: failures.length === 0,
106
+ rewardEligible: true,
107
+ failureOwner: failures.length === 0 ? null : "policy",
108
+ feedback: failures.length === 0
109
+ ? `Required output ${required.path} passed structural verification.`
110
+ : failures.join(" "),
111
+ });
112
+ });
113
+ }
114
+ function manifestEntry(required, collected) {
115
+ return ArtifactManifestEntrySchema.parse({
116
+ requiredOutputPath: required.path,
117
+ collectedPath: collected.path,
118
+ declaredMediaType: required.mediaType,
119
+ detectedMediaType: collected.detectedMediaType,
120
+ artifact: collected.artifact,
121
+ status: collected.status,
122
+ parseStatus: collected.parseStatus ?? "not_requested",
123
+ schemaStatus: collected.schemaStatus ?? "not_requested",
124
+ errorCode: collected.errorCode ?? null,
125
+ failureOwner: collected.failureOwner ?? null,
126
+ evidenceRefs: collected.evidenceRefs ?? [],
127
+ metadata: {
128
+ ...collected.metadata,
129
+ maxBytes: required.maxBytes,
130
+ schemaRef: required.schemaRef,
131
+ },
132
+ });
133
+ }
134
+ function structuralFailures(required, entry) {
135
+ const failures = [];
136
+ if (!entry.artifact)
137
+ failures.push(`Required output ${required.path} has no immutable artifact reference.`);
138
+ if (entry.detectedMediaType !== required.mediaType) {
139
+ failures.push(`Required output ${required.path} has media type ${entry.detectedMediaType ?? "unknown"}; expected ${required.mediaType}.`);
140
+ }
141
+ if (required.maxBytes !== null && entry.artifact?.sizeBytes !== null && entry.artifact && entry.artifact.sizeBytes > required.maxBytes) {
142
+ failures.push(`Required output ${required.path} exceeds ${required.maxBytes} bytes.`);
143
+ }
144
+ if (entry.parseStatus === "failed")
145
+ failures.push(`Required output ${required.path} could not be parsed.`);
146
+ if (entry.schemaStatus === "failed")
147
+ failures.push(`Required output ${required.path} failed schema validation.`);
148
+ return failures;
149
+ }
150
+ function requiredOutputComponent(required, entry, result) {
151
+ const evidenceRefs = [
152
+ ...(entry?.evidenceRefs ?? []),
153
+ ...(entry?.artifact ? [entry.artifact] : []),
154
+ ];
155
+ return RewardComponentReceiptSchema.parse({
156
+ verifierId: `required-output:${required.path}`,
157
+ verifierVersion: "1",
158
+ status: result.status,
159
+ rawScore: result.score,
160
+ normalizedScore: result.score,
161
+ weight: 1,
162
+ passed: result.passed,
163
+ hardGate: true,
164
+ rewardEligible: result.rewardEligible,
165
+ rewardContribution: result.rewardEligible ? result.score : null,
166
+ failureOwner: result.failureOwner,
167
+ feedback: [result.feedback],
168
+ visibleEvidenceRefs: evidenceRefs,
169
+ privilegedEvidenceRefs: [],
170
+ metadata: { requiredOutputPath: required.path },
171
+ });
172
+ }