@openpond/evals 0.1.1 → 0.3.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 (46) hide show
  1. package/CONTRACT.md +50 -2
  2. package/README.md +38 -6
  3. package/RELEASING.md +30 -24
  4. package/dist/compatibility.js +45 -0
  5. package/dist/conformance.js +6 -9
  6. package/dist/evidence/authoring.js +40 -0
  7. package/dist/evidence/conformance.js +194 -0
  8. package/dist/evidence/contracts.js +407 -0
  9. package/dist/evidence/eligibility.js +168 -0
  10. package/dist/evidence/index.js +4 -0
  11. package/dist/harness-improvements.js +329 -0
  12. package/dist/harness-workspaces.js +368 -0
  13. package/dist/harness.js +4 -6
  14. package/dist/index.js +4 -0
  15. package/dist/runs.js +33 -5
  16. package/dist/tasksets.js +2 -3
  17. package/dist/types/common.d.ts.map +1 -1
  18. package/dist/types/compatibility.d.ts +11 -0
  19. package/dist/types/compatibility.d.ts.map +1 -0
  20. package/dist/types/conformance.d.ts +8 -48
  21. package/dist/types/conformance.d.ts.map +1 -1
  22. package/dist/types/evidence/authoring.d.ts +41 -0
  23. package/dist/types/evidence/authoring.d.ts.map +1 -0
  24. package/dist/types/evidence/conformance.d.ts +767 -0
  25. package/dist/types/evidence/conformance.d.ts.map +1 -0
  26. package/dist/types/evidence/contracts.d.ts +873 -0
  27. package/dist/types/evidence/contracts.d.ts.map +1 -0
  28. package/dist/types/evidence/eligibility.d.ts +348 -0
  29. package/dist/types/evidence/eligibility.d.ts.map +1 -0
  30. package/dist/types/evidence/index.d.ts +5 -0
  31. package/dist/types/evidence/index.d.ts.map +1 -0
  32. package/dist/types/graders.d.ts.map +1 -1
  33. package/dist/types/harness-improvements.d.ts +523 -0
  34. package/dist/types/harness-improvements.d.ts.map +1 -0
  35. package/dist/types/harness-workspaces.d.ts +802 -0
  36. package/dist/types/harness-workspaces.d.ts.map +1 -0
  37. package/dist/types/harness.d.ts +6 -68
  38. package/dist/types/harness.d.ts.map +1 -1
  39. package/dist/types/index.d.ts +4 -0
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/types/runs.d.ts +46 -1
  42. package/dist/types/runs.d.ts.map +1 -1
  43. package/dist/types/sha256.d.ts.map +1 -1
  44. package/dist/types/tasksets.d.ts +2 -10
  45. package/dist/types/tasksets.d.ts.map +1 -1
  46. package/package.json +14 -2
@@ -0,0 +1,329 @@
1
+ import { z } from "zod";
2
+ import { HarnessImprovementRouteSchema, HarnessOverlaySnapshotRefSchema, } from "./harness-workspaces.js";
3
+ import { contentHash, ImmutableReleaseRefSchema, ReleaseHashSchema, ReleaseIdSchema, ReleaseTimestampSchema, } from "./common.js";
4
+ const BoundedTextSchema = z.string().trim().min(1).max(100_000);
5
+ const MetadataSchema = z.record(z.string(), z.unknown()).default({});
6
+ export const ImprovementSafeBoundaryKindSchema = z.enum([
7
+ "completed_tool_batch",
8
+ "before_model_step",
9
+ "turn_completed",
10
+ "turn_paused",
11
+ ]);
12
+ export const ImprovementSafeBoundarySchema = z
13
+ .object({
14
+ kind: ImprovementSafeBoundaryKindSchema,
15
+ eventSequence: z.number().int().nonnegative(),
16
+ occurredAt: ReleaseTimestampSchema,
17
+ })
18
+ .strict();
19
+ export const ImprovementEventRefSchema = z
20
+ .object({
21
+ id: ReleaseIdSchema,
22
+ sequence: z.number().int().nonnegative().nullable(),
23
+ contentHash: ReleaseHashSchema,
24
+ })
25
+ .strict();
26
+ export const ImprovementObservationKindSchema = z.enum([
27
+ "tool_failure",
28
+ "retry",
29
+ "recovery",
30
+ "validation",
31
+ "user_correction",
32
+ "reusable_success",
33
+ "completion_detour",
34
+ ]);
35
+ export const ImprovementObservationStateSchema = z.enum([
36
+ "open",
37
+ "recovered",
38
+ "terminal",
39
+ ]);
40
+ export const ImprovementToolIdentitySchema = z
41
+ .object({
42
+ name: z.string().trim().min(1).max(500),
43
+ invocationKey: ReleaseHashSchema,
44
+ })
45
+ .strict();
46
+ export const ImprovementObservationContentSchema = z
47
+ .object({
48
+ schemaVersion: z.literal("openpond.improvementObservation.v1"),
49
+ id: ReleaseIdSchema,
50
+ runRef: ReleaseIdSchema,
51
+ turnId: ReleaseIdSchema,
52
+ harnessRelease: ImmutableReleaseRefSchema,
53
+ overlay: HarnessOverlaySnapshotRefSchema.nullable(),
54
+ eventRefs: z.array(ImprovementEventRefSchema).min(1).max(100),
55
+ kind: ImprovementObservationKindSchema,
56
+ state: ImprovementObservationStateSchema,
57
+ tool: ImprovementToolIdentitySchema.nullable(),
58
+ deterministicClass: z.string().trim().min(1).max(500).nullable(),
59
+ summary: BoundedTextSchema,
60
+ createdAt: ReleaseTimestampSchema,
61
+ metadata: MetadataSchema,
62
+ })
63
+ .strict()
64
+ .superRefine((observation, context) => {
65
+ if (new Set(observation.eventRefs.map((reference) => reference.id)).size !==
66
+ observation.eventRefs.length) {
67
+ context.addIssue({
68
+ code: "custom",
69
+ message: "observation event refs must be unique",
70
+ path: ["eventRefs"],
71
+ });
72
+ }
73
+ if (["tool_failure", "retry", "recovery", "completion_detour"].includes(observation.kind) &&
74
+ observation.tool === null) {
75
+ context.addIssue({
76
+ code: "custom",
77
+ message: `${observation.kind} observations require a tool identity`,
78
+ path: ["tool"],
79
+ });
80
+ }
81
+ });
82
+ export const ImprovementObservationSchema = ImprovementObservationContentSchema.extend({
83
+ contentHash: ReleaseHashSchema,
84
+ }).strict();
85
+ export const RefinementTriggerPolicySchema = z
86
+ .object({
87
+ schemaVersion: z.literal("openpond.refinementTriggerPolicy.v1"),
88
+ maxEstimatedCostUsd: z.number().finite().nonnegative(),
89
+ cooldownMs: z.number().int().nonnegative(),
90
+ maxPendingPlans: z.number().int().min(1).max(100),
91
+ maxEvidenceEvents: z.number().int().min(1).max(1_000),
92
+ maxProposalEdits: z.number().int().min(1).max(1_000),
93
+ maxProposalBytes: z.number().int().min(1).max(10_000_000),
94
+ })
95
+ .strict();
96
+ export const RefinementTriggerDecisionKindSchema = z.enum([
97
+ "no_action",
98
+ "route_deterministically",
99
+ "queue_refiner",
100
+ ]);
101
+ export const RefinementTriggerDecisionContentSchema = z
102
+ .object({
103
+ schemaVersion: z.literal("openpond.refinementTriggerDecision.v1"),
104
+ id: ReleaseIdSchema,
105
+ runRef: ReleaseIdSchema,
106
+ turnId: ReleaseIdSchema,
107
+ harnessRelease: ImmutableReleaseRefSchema,
108
+ overlay: HarnessOverlaySnapshotRefSchema.nullable(),
109
+ observations: z.array(ImmutableReleaseRefSchema).max(100),
110
+ decision: RefinementTriggerDecisionKindSchema,
111
+ deterministicRoute: HarnessImprovementRouteSchema.nullable(),
112
+ suggestedRoutes: z.array(HarnessImprovementRouteSchema).max(8),
113
+ reason: BoundedTextSchema,
114
+ deduplicationKey: ReleaseHashSchema,
115
+ policy: RefinementTriggerPolicySchema,
116
+ estimatedMaxCostUsd: z.number().finite().nonnegative(),
117
+ pendingPlanCount: z.number().int().nonnegative(),
118
+ boundary: ImprovementSafeBoundarySchema,
119
+ cooldownUntil: ReleaseTimestampSchema.nullable(),
120
+ createdAt: ReleaseTimestampSchema,
121
+ metadata: MetadataSchema,
122
+ })
123
+ .strict()
124
+ .superRefine((trigger, context) => {
125
+ if (trigger.estimatedMaxCostUsd > trigger.policy.maxEstimatedCostUsd) {
126
+ context.addIssue({
127
+ code: "custom",
128
+ message: "estimated Refiner cost exceeds the trigger policy budget",
129
+ path: ["estimatedMaxCostUsd"],
130
+ });
131
+ }
132
+ if (trigger.pendingPlanCount > trigger.policy.maxPendingPlans) {
133
+ context.addIssue({
134
+ code: "custom",
135
+ message: "pending plan count exceeds the trigger policy limit",
136
+ path: ["pendingPlanCount"],
137
+ });
138
+ }
139
+ if (trigger.observations.length > trigger.policy.maxEvidenceEvents) {
140
+ context.addIssue({
141
+ code: "custom",
142
+ message: "trigger observations exceed the evidence budget",
143
+ path: ["observations"],
144
+ });
145
+ }
146
+ if (trigger.decision === "route_deterministically" &&
147
+ trigger.deterministicRoute === null) {
148
+ context.addIssue({
149
+ code: "custom",
150
+ message: "deterministic decisions require a route",
151
+ path: ["deterministicRoute"],
152
+ });
153
+ }
154
+ if (trigger.decision !== "route_deterministically" &&
155
+ trigger.deterministicRoute !== null) {
156
+ context.addIssue({
157
+ code: "custom",
158
+ message: "only deterministic decisions may declare a deterministic route",
159
+ path: ["deterministicRoute"],
160
+ });
161
+ }
162
+ if (trigger.decision === "no_action" && trigger.suggestedRoutes.length > 0) {
163
+ context.addIssue({
164
+ code: "custom",
165
+ message: "no-action decisions cannot suggest routes",
166
+ path: ["suggestedRoutes"],
167
+ });
168
+ }
169
+ if (trigger.decision !== "no_action" && trigger.observations.length === 0) {
170
+ context.addIssue({
171
+ code: "custom",
172
+ message: "actionable trigger decisions require observations",
173
+ path: ["observations"],
174
+ });
175
+ }
176
+ });
177
+ export const RefinementTriggerDecisionSchema = RefinementTriggerDecisionContentSchema.extend({
178
+ contentHash: ReleaseHashSchema,
179
+ }).strict();
180
+ export const ImprovementRouteAuthoritySchema = z.enum([
181
+ "runtime_service",
182
+ "refiner_model",
183
+ "human_review",
184
+ "evaluation_system",
185
+ "training_system",
186
+ ]);
187
+ export const ImprovementRouteDecisionContentSchema = z
188
+ .object({
189
+ schemaVersion: z.literal("openpond.improvementRouteDecision.v1"),
190
+ id: ReleaseIdSchema,
191
+ trigger: ImmutableReleaseRefSchema,
192
+ route: HarnessImprovementRouteSchema,
193
+ authority: ImprovementRouteAuthoritySchema,
194
+ automatic: z.boolean(),
195
+ reason: BoundedTextSchema,
196
+ createdAt: ReleaseTimestampSchema,
197
+ metadata: MetadataSchema,
198
+ })
199
+ .strict()
200
+ .superRefine((decision, context) => {
201
+ if (decision.automatic &&
202
+ ["human_review", "evaluation_system", "training_system"].includes(decision.authority)) {
203
+ context.addIssue({
204
+ code: "custom",
205
+ message: `${decision.authority} routes cannot be marked automatic`,
206
+ path: ["automatic"],
207
+ });
208
+ }
209
+ if (decision.route === "training" && decision.authority !== "training_system") {
210
+ context.addIssue({
211
+ code: "custom",
212
+ message: "training routes require training-system authority",
213
+ path: ["authority"],
214
+ });
215
+ }
216
+ });
217
+ export const ImprovementRouteDecisionSchema = ImprovementRouteDecisionContentSchema.extend({
218
+ contentHash: ReleaseHashSchema,
219
+ }).strict();
220
+ export const HarnessRefinerOutcomeContentSchema = z
221
+ .object({
222
+ schemaVersion: z.literal("openpond.harnessRefinerOutcome.v1"),
223
+ id: ReleaseIdSchema,
224
+ trigger: ImmutableReleaseRefSchema,
225
+ decision: z.enum(["no_action", "proposed"]),
226
+ proposal: ImmutableReleaseRefSchema.nullable(),
227
+ reason: BoundedTextSchema,
228
+ evidenceRefs: z.array(ImmutableReleaseRefSchema).max(100),
229
+ estimatedCostUsd: z.number().finite().nonnegative(),
230
+ createdAt: ReleaseTimestampSchema,
231
+ metadata: MetadataSchema,
232
+ })
233
+ .strict()
234
+ .superRefine((outcome, context) => {
235
+ if ((outcome.decision === "proposed") !== (outcome.proposal !== null)) {
236
+ context.addIssue({
237
+ code: "custom",
238
+ message: "proposed Refiner outcomes require a proposal; no-action outcomes cannot include one",
239
+ path: ["proposal"],
240
+ });
241
+ }
242
+ });
243
+ export const HarnessRefinerOutcomeSchema = HarnessRefinerOutcomeContentSchema.extend({
244
+ contentHash: ReleaseHashSchema,
245
+ }).strict();
246
+ export const ImprovementApplyDecisionSchema = z.enum([
247
+ "applied",
248
+ "retained",
249
+ "declined",
250
+ "conflict",
251
+ "rolled_back",
252
+ ]);
253
+ export const ImprovementApplyReceiptContentSchema = z
254
+ .object({
255
+ schemaVersion: z.literal("openpond.improvementApplyReceipt.v1"),
256
+ id: ReleaseIdSchema,
257
+ proposal: ImmutableReleaseRefSchema,
258
+ beforeOverlay: HarnessOverlaySnapshotRefSchema,
259
+ afterOverlay: HarnessOverlaySnapshotRefSchema.nullable(),
260
+ decision: ImprovementApplyDecisionSchema,
261
+ boundary: ImprovementSafeBoundarySchema,
262
+ validationRefs: z.array(ImmutableReleaseRefSchema).max(100),
263
+ outcomeEvidenceRefs: z.array(ImprovementEventRefSchema).max(1_000),
264
+ rollbackOf: ImmutableReleaseRefSchema.nullable(),
265
+ createdAt: ReleaseTimestampSchema,
266
+ metadata: MetadataSchema,
267
+ })
268
+ .strict()
269
+ .superRefine((receipt, context) => {
270
+ if ((receipt.decision === "applied") !== (receipt.afterOverlay !== null)) {
271
+ context.addIssue({
272
+ code: "custom",
273
+ message: "applied receipts require an after-overlay; other decisions cannot include one",
274
+ path: ["afterOverlay"],
275
+ });
276
+ }
277
+ if ((receipt.decision === "rolled_back") !== (receipt.rollbackOf !== null)) {
278
+ context.addIssue({
279
+ code: "custom",
280
+ message: "only rollback receipts require rollbackOf",
281
+ path: ["rollbackOf"],
282
+ });
283
+ }
284
+ });
285
+ export const ImprovementApplyReceiptSchema = ImprovementApplyReceiptContentSchema.extend({
286
+ contentHash: ReleaseHashSchema,
287
+ }).strict();
288
+ function createHashedContract(input) {
289
+ const parsed = input.contentSchema.parse(input.content);
290
+ return input.resultSchema.parse({
291
+ ...parsed,
292
+ contentHash: contentHash(parsed),
293
+ });
294
+ }
295
+ export function createImprovementObservation(content) {
296
+ return createHashedContract({
297
+ content,
298
+ contentSchema: ImprovementObservationContentSchema,
299
+ resultSchema: ImprovementObservationSchema,
300
+ });
301
+ }
302
+ export function createRefinementTriggerDecision(content) {
303
+ return createHashedContract({
304
+ content,
305
+ contentSchema: RefinementTriggerDecisionContentSchema,
306
+ resultSchema: RefinementTriggerDecisionSchema,
307
+ });
308
+ }
309
+ export function createImprovementRouteDecision(content) {
310
+ return createHashedContract({
311
+ content,
312
+ contentSchema: ImprovementRouteDecisionContentSchema,
313
+ resultSchema: ImprovementRouteDecisionSchema,
314
+ });
315
+ }
316
+ export function createHarnessRefinerOutcome(content) {
317
+ return createHashedContract({
318
+ content,
319
+ contentSchema: HarnessRefinerOutcomeContentSchema,
320
+ resultSchema: HarnessRefinerOutcomeSchema,
321
+ });
322
+ }
323
+ export function createImprovementApplyReceipt(content) {
324
+ return createHashedContract({
325
+ content,
326
+ contentSchema: ImprovementApplyReceiptContentSchema,
327
+ resultSchema: ImprovementApplyReceiptSchema,
328
+ });
329
+ }
@@ -0,0 +1,368 @@
1
+ import { z } from "zod";
2
+ import { contentHash, ImmutableReleaseRefSchema, ReleaseHashSchema, ReleaseIdSchema, ReleaseTimestampSchema, } from "./common.js";
3
+ const MetadataSchema = z.record(z.string(), z.unknown()).default({});
4
+ const RevisionSchema = z.number().int().nonnegative();
5
+ const BoundedTextSchema = z.string().trim().min(1).max(100_000);
6
+ export const HarnessOwnerScopeSchema = z
7
+ .object({
8
+ kind: z.enum(["personal", "team"]),
9
+ id: ReleaseIdSchema,
10
+ })
11
+ .strict();
12
+ export const HarnessCurrentChannelSchema = z
13
+ .object({
14
+ name: ReleaseIdSchema,
15
+ release: ImmutableReleaseRefSchema.nullable(),
16
+ revision: RevisionSchema,
17
+ })
18
+ .strict();
19
+ /**
20
+ * Mutable, host-owned authoring state. Portable runs never consume this object;
21
+ * they consume the immutable release selected from currentChannel.
22
+ */
23
+ export const HarnessWorkspaceSchema = z
24
+ .object({
25
+ schemaVersion: z.literal("openpond.harnessWorkspace.v1"),
26
+ id: ReleaseIdSchema,
27
+ ownerScope: HarnessOwnerScopeSchema,
28
+ name: z.string().trim().min(1).max(240),
29
+ location: z.enum(["local", "hosted"]),
30
+ sourceRevision: z.string().trim().min(1).max(500),
31
+ revision: RevisionSchema,
32
+ dirty: z.boolean(),
33
+ currentChannel: HarnessCurrentChannelSchema,
34
+ createdAt: ReleaseTimestampSchema,
35
+ updatedAt: ReleaseTimestampSchema,
36
+ metadata: MetadataSchema,
37
+ })
38
+ .strict();
39
+ export const HarnessImprovementRouteSchema = z.enum([
40
+ "runtime",
41
+ "memory",
42
+ "prompt",
43
+ "skill",
44
+ "agent",
45
+ "product",
46
+ "taskset",
47
+ "training",
48
+ ]);
49
+ export const HarnessChangeEffectSchema = z.enum([
50
+ "text_instruction",
51
+ "memory",
52
+ "dependency_selection",
53
+ "executable_code",
54
+ "business_logic",
55
+ "financial_logic",
56
+ "permission",
57
+ "connected_app",
58
+ "publication",
59
+ "deployment",
60
+ "training",
61
+ "model_binding",
62
+ "team_or_global",
63
+ ]);
64
+ export const HarnessOverlayEditSchema = z
65
+ .object({
66
+ id: ReleaseIdSchema,
67
+ route: HarnessImprovementRouteSchema,
68
+ operation: z.enum(["create", "update", "delete"]),
69
+ target: z.string().trim().min(1).max(2_000),
70
+ summary: z.string().trim().min(1).max(2_000),
71
+ content: z.string().max(1_000_000).nullable(),
72
+ contentHash: ReleaseHashSchema.nullable(),
73
+ effects: z.array(HarnessChangeEffectSchema).min(1).max(20),
74
+ })
75
+ .strict()
76
+ .superRefine((edit, context) => {
77
+ if (edit.operation === "delete") {
78
+ if (edit.content !== null || edit.contentHash !== null) {
79
+ context.addIssue({
80
+ code: "custom",
81
+ message: "delete edits cannot include replacement content",
82
+ path: ["content"],
83
+ });
84
+ }
85
+ return;
86
+ }
87
+ if (edit.content === null || edit.contentHash === null) {
88
+ context.addIssue({
89
+ code: "custom",
90
+ message: "create and update edits require content and contentHash",
91
+ path: ["content"],
92
+ });
93
+ return;
94
+ }
95
+ const expected = contentHash(edit.content);
96
+ if (edit.contentHash !== expected) {
97
+ context.addIssue({
98
+ code: "custom",
99
+ message: `edit contentHash is ${edit.contentHash}; expected ${expected}`,
100
+ path: ["contentHash"],
101
+ });
102
+ }
103
+ });
104
+ export const HarnessWorkspaceRevisionRefSchema = z
105
+ .object({
106
+ workspaceId: ReleaseIdSchema,
107
+ revision: RevisionSchema,
108
+ sourceRevision: z.string().trim().min(1).max(500),
109
+ channelRevision: RevisionSchema,
110
+ })
111
+ .strict();
112
+ export const HarnessOverlaySnapshotRefSchema = z
113
+ .object({
114
+ id: ReleaseIdSchema,
115
+ revision: RevisionSchema,
116
+ contentHash: ReleaseHashSchema,
117
+ })
118
+ .strict();
119
+ export const HarnessTurnSnapshotSchema = z
120
+ .object({
121
+ schemaVersion: z.literal("openpond.harnessTurnSnapshot.v1"),
122
+ workspaceId: ReleaseIdSchema,
123
+ workspaceRevision: RevisionSchema,
124
+ sourceRevision: z.string().trim().min(1).max(500),
125
+ channelName: ReleaseIdSchema,
126
+ channelRevision: RevisionSchema,
127
+ harnessRelease: ImmutableReleaseRefSchema,
128
+ overlay: HarnessOverlaySnapshotRefSchema.nullable().optional().default(null),
129
+ })
130
+ .strict();
131
+ export const HarnessRunOverlayContentSchema = z
132
+ .object({
133
+ schemaVersion: z.literal("openpond.harnessRunOverlay.v1"),
134
+ id: ReleaseIdSchema,
135
+ runId: ReleaseIdSchema,
136
+ baseHarnessRelease: ImmutableReleaseRefSchema,
137
+ workspace: HarnessWorkspaceRevisionRefSchema,
138
+ revision: RevisionSchema,
139
+ status: z.enum(["active", "frozen", "abandoned"]),
140
+ edits: z.array(HarnessOverlayEditSchema).max(1_000),
141
+ createdAt: ReleaseTimestampSchema,
142
+ updatedAt: ReleaseTimestampSchema,
143
+ metadata: MetadataSchema,
144
+ })
145
+ .strict();
146
+ export const HarnessRunOverlaySchema = HarnessRunOverlayContentSchema.extend({
147
+ contentHash: ReleaseHashSchema,
148
+ }).strict();
149
+ export const HarnessProposalEvidenceRefSchema = z
150
+ .object({
151
+ kind: z.enum([
152
+ "tool_event",
153
+ "recovery",
154
+ "validation",
155
+ "user_correction",
156
+ "work_output",
157
+ "work_receipt",
158
+ ]),
159
+ id: ReleaseIdSchema,
160
+ contentHash: ReleaseHashSchema.nullable(),
161
+ })
162
+ .strict();
163
+ export const HarnessValidationPlanItemSchema = z
164
+ .object({
165
+ id: ReleaseIdSchema,
166
+ kind: z.enum([
167
+ "dependency",
168
+ "schema",
169
+ "package",
170
+ "file_render",
171
+ "skill",
172
+ "prompt",
173
+ "business_formula",
174
+ "targeted_evaluation",
175
+ "observed_recovery",
176
+ ]),
177
+ description: BoundedTextSchema,
178
+ required: z.boolean(),
179
+ })
180
+ .strict();
181
+ export const HarnessImprovementProposalContentSchema = z
182
+ .object({
183
+ schemaVersion: z.literal("openpond.harnessImprovementProposal.v1"),
184
+ id: ReleaseIdSchema,
185
+ overlay: HarnessOverlaySnapshotRefSchema,
186
+ baseHarnessRelease: ImmutableReleaseRefSchema,
187
+ expectedWorkspace: HarnessWorkspaceRevisionRefSchema,
188
+ requestedScope: z.enum(["personal", "team", "global"]),
189
+ route: HarnessImprovementRouteSchema,
190
+ risk: z.enum(["low", "review", "restricted"]),
191
+ effects: z.array(HarnessChangeEffectSchema).min(1).max(20),
192
+ evidence: z.array(HarnessProposalEvidenceRefSchema).min(1).max(1_000),
193
+ edits: z.array(HarnessOverlayEditSchema).min(1).max(1_000),
194
+ validationPlan: z.array(HarnessValidationPlanItemSchema).min(1).max(100),
195
+ expectedOutcome: BoundedTextSchema,
196
+ createdAt: ReleaseTimestampSchema,
197
+ metadata: MetadataSchema,
198
+ })
199
+ .strict()
200
+ .superRefine((proposal, context) => {
201
+ const declaredEffects = new Set(proposal.effects);
202
+ for (const [index, edit] of proposal.edits.entries()) {
203
+ if (edit.route !== proposal.route) {
204
+ context.addIssue({
205
+ code: "custom",
206
+ message: "every edit route must match the proposal route",
207
+ path: ["edits", index, "route"],
208
+ });
209
+ }
210
+ for (const effect of edit.effects) {
211
+ if (!declaredEffects.has(effect)) {
212
+ context.addIssue({
213
+ code: "custom",
214
+ message: `edit effect ${effect} is missing from proposal effects`,
215
+ path: ["edits", index, "effects"],
216
+ });
217
+ }
218
+ }
219
+ }
220
+ if (new Set(proposal.validationPlan.map((item) => item.id)).size !== proposal.validationPlan.length) {
221
+ context.addIssue({
222
+ code: "custom",
223
+ message: "validation plan ids must be unique",
224
+ path: ["validationPlan"],
225
+ });
226
+ }
227
+ });
228
+ export const HarnessImprovementProposalSchema = HarnessImprovementProposalContentSchema.extend({
229
+ contentHash: ReleaseHashSchema,
230
+ }).strict();
231
+ export const HarnessTargetedValidationReceiptContentSchema = z
232
+ .object({
233
+ schemaVersion: z.literal("openpond.harnessTargetedValidationReceipt.v1"),
234
+ id: ReleaseIdSchema,
235
+ proposal: ImmutableReleaseRefSchema,
236
+ validationId: ReleaseIdSchema,
237
+ kind: HarnessValidationPlanItemSchema.shape.kind,
238
+ status: z.enum(["passed", "failed", "blocked", "skipped"]),
239
+ summary: BoundedTextSchema,
240
+ evidenceRefs: z.array(HarnessProposalEvidenceRefSchema).max(1_000),
241
+ createdAt: ReleaseTimestampSchema,
242
+ metadata: MetadataSchema,
243
+ })
244
+ .strict();
245
+ export const HarnessTargetedValidationReceiptSchema = HarnessTargetedValidationReceiptContentSchema.extend({
246
+ contentHash: ReleaseHashSchema,
247
+ }).strict();
248
+ export const HarnessAdvanceDecisionSchema = z.enum([
249
+ "advanced",
250
+ "retained",
251
+ "conflict",
252
+ "rolled_back",
253
+ ]);
254
+ export const HarnessAdvanceReceiptContentSchema = z
255
+ .object({
256
+ schemaVersion: z.literal("openpond.harnessAdvanceReceipt.v1"),
257
+ id: ReleaseIdSchema,
258
+ workspaceId: ReleaseIdSchema,
259
+ ownerScope: HarnessOwnerScopeSchema,
260
+ proposal: ImmutableReleaseRefSchema.nullable(),
261
+ expectedWorkspaceRevision: RevisionSchema,
262
+ observedWorkspaceRevision: RevisionSchema,
263
+ previousChannelRevision: RevisionSchema,
264
+ nextChannelRevision: RevisionSchema,
265
+ previousRelease: ImmutableReleaseRefSchema.nullable(),
266
+ nextRelease: ImmutableReleaseRefSchema.nullable(),
267
+ validationReceipts: z.array(ImmutableReleaseRefSchema).max(100),
268
+ decision: HarnessAdvanceDecisionSchema,
269
+ reason: BoundedTextSchema,
270
+ rollbackOf: ImmutableReleaseRefSchema.nullable(),
271
+ createdAt: ReleaseTimestampSchema,
272
+ metadata: MetadataSchema,
273
+ })
274
+ .strict()
275
+ .superRefine((receipt, context) => {
276
+ const changed = receipt.nextChannelRevision === receipt.previousChannelRevision + 1;
277
+ if (["advanced", "rolled_back"].includes(receipt.decision) !== changed) {
278
+ context.addIssue({
279
+ code: "custom",
280
+ message: "only successful advancement or rollback increments the channel revision",
281
+ path: ["nextChannelRevision"],
282
+ });
283
+ }
284
+ if (receipt.decision === "rolled_back" && receipt.rollbackOf === null) {
285
+ context.addIssue({
286
+ code: "custom",
287
+ message: "rollback receipts require rollbackOf",
288
+ path: ["rollbackOf"],
289
+ });
290
+ }
291
+ if (receipt.decision !== "rolled_back" && receipt.rollbackOf !== null) {
292
+ context.addIssue({
293
+ code: "custom",
294
+ message: "rollbackOf is only valid for rollback receipts",
295
+ path: ["rollbackOf"],
296
+ });
297
+ }
298
+ });
299
+ export const HarnessAdvanceReceiptSchema = HarnessAdvanceReceiptContentSchema.extend({
300
+ contentHash: ReleaseHashSchema,
301
+ }).strict();
302
+ export const HarnessOverlayMergeReceiptContentSchema = z
303
+ .object({
304
+ schemaVersion: z.literal("openpond.harnessOverlayMergeReceipt.v1"),
305
+ id: ReleaseIdSchema,
306
+ workspaceId: ReleaseIdSchema,
307
+ baseHarnessRelease: ImmutableReleaseRefSchema,
308
+ leftOverlay: HarnessOverlaySnapshotRefSchema,
309
+ rightOverlay: HarnessOverlaySnapshotRefSchema,
310
+ decision: z.enum(["merged", "conflict"]),
311
+ mergedOverlay: HarnessOverlaySnapshotRefSchema.nullable(),
312
+ conflictTargets: z.array(z.string().trim().min(1).max(2_000)).max(1_000),
313
+ requiresRevalidation: z.literal(true),
314
+ createdAt: ReleaseTimestampSchema,
315
+ metadata: MetadataSchema,
316
+ })
317
+ .strict()
318
+ .superRefine((receipt, context) => {
319
+ if ((receipt.decision === "merged") !== (receipt.mergedOverlay !== null)) {
320
+ context.addIssue({
321
+ code: "custom",
322
+ message: "merged decisions require a merged overlay; conflicts cannot provide one",
323
+ path: ["mergedOverlay"],
324
+ });
325
+ }
326
+ if ((receipt.decision === "conflict") !== (receipt.conflictTargets.length > 0)) {
327
+ context.addIssue({
328
+ code: "custom",
329
+ message: "conflict decisions require conflict targets and merged decisions cannot include them",
330
+ path: ["conflictTargets"],
331
+ });
332
+ }
333
+ });
334
+ export const HarnessOverlayMergeReceiptSchema = HarnessOverlayMergeReceiptContentSchema.extend({
335
+ contentHash: ReleaseHashSchema,
336
+ }).strict();
337
+ export function createHarnessRunOverlay(content) {
338
+ const parsed = HarnessRunOverlayContentSchema.parse(content);
339
+ return HarnessRunOverlaySchema.parse({ ...parsed, contentHash: contentHash(parsed) });
340
+ }
341
+ export function createHarnessImprovementProposal(content) {
342
+ const parsed = HarnessImprovementProposalContentSchema.parse(content);
343
+ return HarnessImprovementProposalSchema.parse({
344
+ ...parsed,
345
+ contentHash: contentHash(parsed),
346
+ });
347
+ }
348
+ export function createHarnessTargetedValidationReceipt(content) {
349
+ const parsed = HarnessTargetedValidationReceiptContentSchema.parse(content);
350
+ return HarnessTargetedValidationReceiptSchema.parse({
351
+ ...parsed,
352
+ contentHash: contentHash(parsed),
353
+ });
354
+ }
355
+ export function createHarnessAdvanceReceipt(content) {
356
+ const parsed = HarnessAdvanceReceiptContentSchema.parse(content);
357
+ return HarnessAdvanceReceiptSchema.parse({
358
+ ...parsed,
359
+ contentHash: contentHash(parsed),
360
+ });
361
+ }
362
+ export function createHarnessOverlayMergeReceipt(content) {
363
+ const parsed = HarnessOverlayMergeReceiptContentSchema.parse(content);
364
+ return HarnessOverlayMergeReceiptSchema.parse({
365
+ ...parsed,
366
+ contentHash: contentHash(parsed),
367
+ });
368
+ }