@openpond/evals 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONTRACT.md CHANGED
@@ -13,6 +13,9 @@ authorization, model streaming, artifact bytes, and runtime processes.
13
13
  | `GradeResult` | `GraderEvidence[]` | Preserve component score, pass, reward eligibility, failure class, feedback, and visible/private evidence references. Aggregate UI results remain host projections. |
14
14
  | 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. |
15
15
  | resolved training bundle | `HarnessRelease` + host training binding | Environment, tools, program, policy, files, and grader interface belong to the Harness. Dataset/evidence, recipe, compute, engine, approval, and opaque leases remain explicit host-side bindings. |
16
+ | 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. |
17
+ | Agent plus environment runtime events | `WorkProcessTrace` | Emit one ordered trace with `agent` and `environment` layers. Bind every environment step to its outer Agent tool call or stable Agent-turn receipt hash. Hash inputs/outputs and expose only enumerated, bounded attributes. |
18
+ | user feedback on Work output | `WorkFeedbackReceipt` | Append a new receipt bound to the evidence receipt and, when selected, the exact content-addressed output-revision descriptor. Corrections are separate artifacts and never mutate prior receipts. |
16
19
 
17
20
  ## Compatibility policy
18
21
 
@@ -29,6 +32,40 @@ authorization, model streaming, artifact bytes, and runtime processes.
29
32
  upper bounds.
30
33
  - Immutable content never contains secret values, opaque lease values, mutable
31
34
  provider resource IDs, database keys, UI state, or process handles.
35
+ - The Work evidence schemas first ship as additive `0.2.x` package exports. The
36
+ package version does not replace the `openpond.workEvidenceReceipt.v1`,
37
+ `openpond.workProcessTrace.v1`, `openpond.workFeedbackReceipt.v1`, or
38
+ `openpond.workEvidenceEligibility.v1` schema literals.
39
+
40
+ ## Work evidence boundary
41
+
42
+ - One `WorkEvidenceReceipt` spans the Agent and its environment or sandbox. A
43
+ later Taskset/Harness replay emits one `AttemptReceipt`, not separate Agent and
44
+ sandbox attempts.
45
+ - Portable traces contain no private-trace path or reference. Source identities
46
+ are opaque SHA-256 URNs; artifacts and output-revision descriptors are
47
+ content-addressed. Hidden reasoning, prompts, raw command arguments/output,
48
+ local paths, credentials, provider handles, and raw validation text do not
49
+ cross the portable boundary.
50
+ - Step attributes are closed and bounded: tool/validation/transition categories,
51
+ intervention outcomes, artifact counts, exit status, duration, available CPU
52
+ time and peak memory, and error class. Adding an arbitrary metadata bag would
53
+ be a privacy-boundary change.
54
+ - Explicit process-and-artifact consent is required. Existing transcript consent
55
+ is not upgraded implicitly. Workspace-owned or multi-participant evidence must
56
+ satisfy the host's ownership and participant policy before projection.
57
+ - Receipts are immutable observations; current consent status is not. Revoked,
58
+ expired, or source-deleted evidence is ineligible for all downstream uses.
59
+ Hosts enforce source-bound deletion and distribution withdrawal outside the
60
+ immutable receipt.
61
+ - An incomplete trace remains valid observational evidence but is blocked from
62
+ eval, training, reward, and qualitative use according to the eligibility
63
+ report. Reward candidacy additionally requires a verified and reward-eligible
64
+ `AttemptReceipt` bound by hash.
65
+ - Feedback is append-only. `outputRevisionRef` identifies an exact revision
66
+ descriptor even when two revisions contain identical bytes; correction
67
+ content remains a separate private artifact unless a later policy explicitly
68
+ permits disclosure.
32
69
 
33
70
  ## Runtime interfaces
34
71
 
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # `@openpond/evals`
2
2
 
3
3
  Portable OpenPond contracts and reference helpers for immutable Agent snapshots,
4
- Harness releases, Taskset releases, run manifests, attempt receipts, deterministic
5
- graders, and conformance fixtures.
4
+ Harness releases, Taskset releases, run manifests, attempt receipts, observational
5
+ Work evidence, deterministic graders, and conformance fixtures.
6
6
 
7
7
  ```ts
8
8
  import {
@@ -12,13 +12,38 @@ import {
12
12
  validateTasksetRelease,
13
13
  verifyAttemptReceipt,
14
14
  } from "@openpond/evals";
15
+
16
+ import {
17
+ WorkEvidenceReceiptSchema,
18
+ classifyWorkEvidence,
19
+ workEvidenceConformance,
20
+ } from "@openpond/evals/evidence";
15
21
  ```
16
22
 
17
23
  Subpath exports are available at `/harness`, `/tasksets`, `/graders`, `/runs`,
18
- and `/conformance`. The package is a protocol library, not a hosted client. It
24
+ `/conformance`, and `/evidence`. The package is a protocol library, not a hosted client. It
19
25
  does not execute OpenPond Desktop or Sandbox sessions, resolve credentials, or
20
26
  persist artifacts.
21
27
 
28
+ ## Work evidence
29
+
30
+ `WorkEvidenceReceipt` records one completed Work or Development turn as a single
31
+ observational evidence unit. Its sanitized `WorkProcessTrace` combines Agent
32
+ actions with environment or sandbox actions. Every environment step is bound to
33
+ the outer Agent tool-call receipt hash or, for automatic lifecycle work, the
34
+ stable Agent-turn receipt hash. Commands, exit status, lifecycle transitions,
35
+ output and validation references, timing, and available CPU or peak-memory
36
+ observations stay in that one trace.
37
+
38
+ This receipt is not an evaluation result. A Taskset/Harness replay still emits
39
+ exactly one `AttemptReceipt`; callers may classify Work evidence as an eval,
40
+ training, reward, or qualitative candidate only through the explicit eligibility
41
+ report. Reward candidacy requires a verified, reward-eligible Attempt receipt.
42
+
43
+ Feedback is append-only and can bind an exact content-addressed output revision.
44
+ Corrections remain separate artifacts and are valid only for
45
+ `needs_correction` feedback.
46
+
22
47
  ## Privacy boundary
23
48
 
24
49
  Release content may include approved policy-visible assets and immutable hashes
@@ -28,6 +53,14 @@ mutable local UI state. Private graders receive only their declared evidence and
28
53
  must execute outside the policy-controlled Agent environment when tamper
29
54
  resistance matters.
30
55
 
56
+ Work evidence requires explicit `work_process_and_artifacts` consent; transcript
57
+ consent is not sufficient. Portable receipts expose only opaque source/workspace
58
+ URNs, content hashes, bounded enums and counters, and sanitized artifact
59
+ references. Raw traces and correction bytes stay host-private. Consent,
60
+ revocation, expiry, deletion, ownership, participant policy, and source-bound
61
+ retention remain authoritative host policy state; an immutable receipt never
62
+ overrides that state.
63
+
31
64
  ## Runtime adapters
32
65
 
33
66
  Implement `HarnessRuntime` for environment state and `HarnessExecutor` around
@@ -55,6 +88,5 @@ pnpm --dir packages/evals run check
55
88
  pnpm run release:evals:patch -- --dry-run
56
89
  ```
57
90
 
58
- The first npm publication must be bootstrapped manually before trusted publishing
59
- can take over. Publication uses package-specific `evals-vX.Y.Z` tags and npm
60
- provenance; see the release workflow for the exact one-time setup.
91
+ Publication uses package-specific `evals-vX.Y.Z` tags, npm trusted publishing,
92
+ and provenance. See the release workflow for release preparation and verification.
package/RELEASING.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Releasing `@openpond/evals`
2
2
 
3
3
  The package version is independent from OpenPond application and schema
4
- versions. Version `0.1.0` supports these initial schema literals:
4
+ versions. Version `0.1.1` is the current provenance-backed baseline and supports
5
+ these initial schema literals:
5
6
 
6
7
  - `openpond.agentSnapshot.v1`
7
8
  - `openpond.harnessRelease.v1`
@@ -12,38 +13,42 @@ versions. Version `0.1.0` supports these initial schema literals:
12
13
  - `openpond.graderEvidence.v1`
13
14
  - `openpond.evaluationResult.v1`
14
15
 
15
- ## One-time npm bootstrap
16
+ This branch prepares the additive Work evidence schemas for `0.2.0`:
16
17
 
17
- Trusted publishing can only be configured after the scoped package exists. Once
18
- this feature is merged, use a clean current `master` checkout:
18
+ - `openpond.workEvidenceReceipt.v1`
19
+ - `openpond.workProcessTrace.v1`
20
+ - `openpond.workFeedbackReceipt.v1`
21
+ - `openpond.workEvidenceEligibility.v1`
22
+
23
+ ## Trusted publishing
24
+
25
+ The package already exists on npm and the `npm-production` environment is
26
+ configured for trusted publishing through `release-evals.yml`. Do not run a
27
+ manual `npm publish` or repeat the original bootstrap. The workflow uses its own
28
+ pinned npm version for trusted publication and provenance.
29
+
30
+ To inspect an already-published version:
19
31
 
20
32
  ```bash
21
33
  pnpm evals:check
22
- npm login
23
- npm publish ./packages/evals --access public --ignore-scripts --provenance=false
24
- npm install --global npm@^11.15.0
25
- npm trust github @openpond/evals \
26
- --repo openpond/openpond \
27
- --file release-evals.yml \
28
- --environment npm-production \
29
- --allow-publish
30
34
  npm trust list @openpond/evals
31
- gh workflow run release-evals.yml --ref master
35
+ npm view @openpond/evals version dist.integrity dist.attestations
32
36
  ```
33
37
 
34
- The last command is idempotent recovery: because `0.1.0` is already on npm, the
35
- workflow skips publication and creates the missing `evals-v0.1.0` GitHub release.
36
-
37
38
  ## Later releases
38
39
 
39
- From a clean current `master` checkout:
40
+ For releases that do not already carry an intentional version bump, merge
41
+ feature work without changing the package version. When the intended package
42
+ changes are on `master`, prepare a separate release from a clean current
43
+ `master` checkout:
40
44
 
41
45
  ```bash
42
46
  pnpm release:evals:patch
43
47
  # or release:evals:minor / release:evals:major
44
48
  ```
45
49
 
46
- The release helper creates an Evals-only release PR. Merging it triggers trusted
47
- publishing with provenance, registry verification, and a package-specific tag.
48
- Record the registry integrity/provenance evidence and merge commit in the
49
- release notes after publication.
50
+ The release helper creates an Evals-only release PR and updates the package
51
+ version and lockfile. Merging it triggers trusted publishing with provenance,
52
+ registry verification, and a package-specific tag. Record the registry
53
+ integrity/provenance evidence and merge commit in the release notes after
54
+ publication.
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ import { ImmutableReleaseRefSchema, ReleaseHashSchema } from "../common.js";
3
+ import { EvidenceArtifactRefSchema, WorkEvidenceReceiptSchema, evidenceArtifactRef, } from "./contracts.js";
4
+ import { WorkEvidenceEligibilitySchema } from "./eligibility.js";
5
+ export const WorkEvidenceAuthoringInputSchema = z.object({
6
+ schemaVersion: z.literal("openpond.workEvidenceAuthoringInput.v1"),
7
+ evidenceReceiptRef: EvidenceArtifactRefSchema,
8
+ inputHash: ReleaseHashSchema,
9
+ agentSnapshot: ImmutableReleaseRefSchema.nullable(),
10
+ sanitizedTraceRef: EvidenceArtifactRefSchema,
11
+ outputRefs: z.array(EvidenceArtifactRefSchema).max(10_000),
12
+ validationEvidenceRefs: z.array(EvidenceArtifactRefSchema).max(10_000),
13
+ incomplete: z.boolean(),
14
+ evalCandidate: z.boolean(),
15
+ blockerCodes: z.array(z.string().trim().min(1).max(120)).max(32),
16
+ }).strict();
17
+ export function toWorkEvidenceAuthoringInput(evidenceInput, eligibilityInput) {
18
+ const evidence = WorkEvidenceReceiptSchema.parse(evidenceInput);
19
+ const eligibility = WorkEvidenceEligibilitySchema.parse(eligibilityInput);
20
+ if (eligibility.evidenceReceiptHash !== evidence.contentHash) {
21
+ throw new Error("Eligibility report belongs to a different Work evidence receipt.");
22
+ }
23
+ const receiptRef = evidenceArtifactRef({
24
+ contentHash: evidence.contentHash,
25
+ mediaType: "application/vnd.openpond.work-evidence+json",
26
+ sizeBytes: null,
27
+ });
28
+ return WorkEvidenceAuthoringInputSchema.parse({
29
+ schemaVersion: "openpond.workEvidenceAuthoringInput.v1",
30
+ evidenceReceiptRef: receiptRef,
31
+ inputHash: evidence.inputHash,
32
+ agentSnapshot: evidence.agentSnapshot,
33
+ sanitizedTraceRef: evidence.trace.sanitizedRef,
34
+ outputRefs: evidence.outputRefs,
35
+ validationEvidenceRefs: evidence.validationEvidenceRefs,
36
+ incomplete: evidence.trace.incomplete,
37
+ evalCandidate: eligibility.decisions.eval_candidate.eligible,
38
+ blockerCodes: eligibility.decisions.eval_candidate.blockers,
39
+ });
40
+ }
@@ -0,0 +1,194 @@
1
+ import { canonicalJson, contentHash } from "../common.js";
2
+ import { createWorkEvidenceReceipt, createWorkFeedbackReceipt, createWorkProcessTrace, evidenceArtifactRef, workEvidenceReceiptRef, workSourceOpaqueRef, } from "./contracts.js";
3
+ import { classifyWorkEvidence } from "./eligibility.js";
4
+ const startedAt = "2026-08-04T12:00:00.000Z";
5
+ const completedAt = "2026-08-04T12:00:02.500Z";
6
+ const sourceRevisionHash = contentHash({ fixture: "desktop-work", revision: 1 });
7
+ const output = evidenceArtifactRef({
8
+ contentHash: contentHash("fixture output"),
9
+ mediaType: "text/plain",
10
+ sizeBytes: 14,
11
+ });
12
+ const validation = evidenceArtifactRef({
13
+ contentHash: contentHash({ kind: "test", status: "passed" }),
14
+ mediaType: "application/json",
15
+ sizeBytes: null,
16
+ });
17
+ const consent = evidenceArtifactRef({
18
+ contentHash: contentHash({ scope: "work_process_and_artifacts", grantedAt: startedAt }),
19
+ mediaType: "application/json",
20
+ sizeBytes: null,
21
+ });
22
+ export const completeWorkProcessTraceFixture = createWorkProcessTrace({
23
+ schemaVersion: "openpond.workProcessTrace.v1",
24
+ sourceRevisionHash,
25
+ sanitationPolicyVersion: "openpond.desktop-work-sanitizer.v1",
26
+ incomplete: false,
27
+ incompleteReasons: [],
28
+ droppedEventCount: 1,
29
+ steps: [
30
+ step(0, startedAt, "agent", "state_transition", "turn_started", "started", {
31
+ transitionState: "running",
32
+ }),
33
+ step(1, "2026-08-04T12:00:00.500Z", "agent", "tool", "tool_invoked", "started", {
34
+ toolCategory: "filesystem",
35
+ inputHash: contentHash({ path: "hashed-only" }),
36
+ receiptHash: contentHash("fixture-tool-call"),
37
+ }),
38
+ step(2, "2026-08-04T12:00:01.000Z", "environment", "artifact", "artifact_created", "completed", {
39
+ outputHash: output.contentHash,
40
+ artifacts: [output],
41
+ receiptHash: contentHash("fixture-environment-operation"),
42
+ parentReceiptHash: contentHash("fixture-tool-call"),
43
+ }),
44
+ step(3, "2026-08-04T12:00:01.500Z", "environment", "validation", "validation_completed", "completed", {
45
+ validationKind: "test",
46
+ outputHash: validation.contentHash,
47
+ receiptHash: contentHash("fixture-validation"),
48
+ parentReceiptHash: contentHash("fixture-tool-call"),
49
+ }),
50
+ step(4, completedAt, "agent", "state_transition", "turn_completed", "completed", {
51
+ transitionState: "completed",
52
+ }),
53
+ ],
54
+ });
55
+ const traceBytes = new TextEncoder().encode(canonicalJson(completeWorkProcessTraceFixture)).byteLength;
56
+ const traceRef = evidenceArtifactRef({
57
+ contentHash: completeWorkProcessTraceFixture.contentHash,
58
+ mediaType: "application/vnd.openpond.work-process-trace+json",
59
+ sizeBytes: traceBytes,
60
+ });
61
+ export const completeWorkEvidenceFixture = createWorkEvidenceReceipt({
62
+ schemaVersion: "openpond.workEvidenceReceipt.v1",
63
+ id: `work-evidence-${contentHash([sourceRevisionHash, traceRef.contentHash]).slice(0, 24)}`,
64
+ source: {
65
+ surface: "desktop",
66
+ experience: "work",
67
+ opaqueRef: workSourceOpaqueRef(["fixture-session", "fixture-turn"]),
68
+ revisionHash: sourceRevisionHash,
69
+ },
70
+ agentSnapshot: { id: "fixture-agent-snapshot", contentHash: contentHash("fixture-agent") },
71
+ model: {
72
+ provider: "fixture",
73
+ model: "scripted",
74
+ revision: "1",
75
+ artifactHash: null,
76
+ tokenizerRevision: null,
77
+ chatTemplateHash: null,
78
+ },
79
+ runtime: {
80
+ adapterId: "desktop-work",
81
+ adapterVersion: "1",
82
+ capabilityRef: null,
83
+ },
84
+ inputHash: contentHash("fixture prompt"),
85
+ terminal: { status: "completed", failureClass: null },
86
+ trace: {
87
+ sanitizedRef: traceRef,
88
+ traceHash: traceRef.contentHash,
89
+ sanitationPolicyVersion: completeWorkProcessTraceFixture.sanitationPolicyVersion,
90
+ incomplete: false,
91
+ },
92
+ outputRefs: [output],
93
+ artifactRefs: [output],
94
+ validationEvidenceRefs: [validation],
95
+ interventions: { approvals: 0, questions: 0, steeringEvents: 0, otherUserInterventions: 0 },
96
+ timing: { startedAt, completedAt, latencyMs: 2_500 },
97
+ usage: { promptTokens: 12, completionTokens: 8, totalTokens: 20 },
98
+ costUsd: 0,
99
+ provenance: {
100
+ consentReceiptRef: consent,
101
+ consentScope: "work_process_and_artifacts",
102
+ consentGrantedAt: startedAt,
103
+ policyVersion: "openpond.work-evidence-policy.v1",
104
+ projectorVersion: "openpond.desktop-work-evidence-projector.v1",
105
+ disclosure: "portable_sanitized",
106
+ ownershipScope: "personal",
107
+ workspaceRef: null,
108
+ participantPolicy: "creator_only",
109
+ retention: { policy: "source_bound", deleteWithSource: true, expiresAt: null },
110
+ },
111
+ });
112
+ export const acceptedWorkFeedbackFixture = createWorkFeedbackReceipt({
113
+ schemaVersion: "openpond.workFeedbackReceipt.v1",
114
+ id: `work-feedback-${contentHash([completeWorkEvidenceFixture.contentHash, "accepted"]).slice(0, 24)}`,
115
+ evidenceReceiptRef: workEvidenceReceiptRef(completeWorkEvidenceFixture),
116
+ outputRevisionRef: output,
117
+ verdict: "accepted",
118
+ reasonCodes: ["correct", "complete"],
119
+ correctionRef: null,
120
+ correctedOutputRevisionRef: null,
121
+ actor: "user",
122
+ createdAt: "2026-08-04T12:01:00.000Z",
123
+ }, completeWorkEvidenceFixture);
124
+ export const incompleteWorkProcessTraceFixture = createWorkProcessTrace({
125
+ schemaVersion: "openpond.workProcessTrace.v1",
126
+ sourceRevisionHash: contentHash({ fixture: "incomplete", revision: 1 }),
127
+ sanitationPolicyVersion: "openpond.desktop-work-sanitizer.v1",
128
+ incomplete: true,
129
+ incompleteReasons: ["missing_terminal", "unsupported_events_dropped"],
130
+ droppedEventCount: 2,
131
+ steps: [step(0, startedAt, "agent", "state_transition", "turn_started", "started", {
132
+ transitionState: "running",
133
+ })],
134
+ });
135
+ export const activeWorkEvidenceEligibilityFixture = classifyWorkEvidence({
136
+ evidence: completeWorkEvidenceFixture,
137
+ feedback: [acceptedWorkFeedbackFixture],
138
+ policyState: "active",
139
+ reconstructability: { input: true, environment: true, verifier: true },
140
+ });
141
+ export const revokedWorkEvidenceEligibilityFixture = classifyWorkEvidence({
142
+ evidence: completeWorkEvidenceFixture,
143
+ feedback: [acceptedWorkFeedbackFixture],
144
+ policyState: "revoked",
145
+ reconstructability: { input: true, environment: true, verifier: true },
146
+ });
147
+ export const invalidRawEvidenceFixture = {
148
+ ...completeWorkEvidenceFixture,
149
+ source: {
150
+ ...completeWorkEvidenceFixture.source,
151
+ opaqueRef: "raw-database-turn-id",
152
+ },
153
+ privateRef: "/private/runtime/trace.json",
154
+ publicData: {
155
+ reasoning: "hidden model reasoning",
156
+ apiKey: "secret-like-value",
157
+ },
158
+ };
159
+ export const workEvidenceConformance = {
160
+ trace: completeWorkProcessTraceFixture,
161
+ receipt: completeWorkEvidenceFixture,
162
+ feedback: acceptedWorkFeedbackFixture,
163
+ activeEligibility: activeWorkEvidenceEligibilityFixture,
164
+ revokedEligibility: revokedWorkEvidenceEligibilityFixture,
165
+ incompleteTrace: incompleteWorkProcessTraceFixture,
166
+ invalidRawEvidence: invalidRawEvidenceFixture,
167
+ };
168
+ function step(sequence, timestamp, layer, kind, action, status, input) {
169
+ return {
170
+ sequence,
171
+ timestamp,
172
+ layer,
173
+ kind,
174
+ action,
175
+ status,
176
+ inputHash: input.inputHash ?? null,
177
+ outputHash: input.outputHash ?? null,
178
+ receiptHash: input.receiptHash ?? null,
179
+ parentReceiptHash: input.parentReceiptHash ?? null,
180
+ artifactRefs: input.artifacts ?? [],
181
+ attributes: {
182
+ toolCategory: input.toolCategory ?? null,
183
+ validationKind: input.validationKind ?? null,
184
+ transitionState: input.transitionState ?? null,
185
+ interventionOutcome: null,
186
+ artifactCount: input.artifacts?.length ?? 0,
187
+ exitCode: null,
188
+ durationMs: null,
189
+ cpuTimeMs: null,
190
+ memoryPeakBytes: null,
191
+ errorClass: null,
192
+ },
193
+ };
194
+ }