Skip to content

Commit abbbb53

Browse files
Nick Ficanoclaude
andcommitted
effect(core-messages-session): session/lease zod→Schema
Add Effect Schema definitions for every session.* payload (hello/welcome/error/bye/ping/pong/ack/list_jobs/jobs) and lease/ lease-constraints alongside the existing zod schemas. Zod schemas are renamed to *ZodSchema and continue to drive messageEnvelope() because envelope.ts itself stays zod-typed until slice #50. events.ts now consumes the native Effect LeaseConstraintsSchema; LeaseSchema stays on the zod-twin bridge to preserve the mutable Lease shape consumers depend on. Closes #37 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c9882f0 commit abbbb53

6 files changed

Lines changed: 681 additions & 77 deletions

File tree

packages/core/src/messages/events.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { z } from "zod";
44
import { ERROR_CODES } from "../errors.js";
55

66
import { ArtifactRefSchema } from "./artifacts.js";
7-
import { LeaseConstraintsSchema, LeaseSchema } from "./lease-schema.js";
7+
import { LeaseConstraintsSchema, LeaseZodSchema } from "./lease-schema.js";
88
import { LogPayloadSchema, MetricPayloadSchema } from "./telemetry.js";
99
import { fromZod } from "./zod-adapter.js";
1010

@@ -93,20 +93,22 @@ export const StatusBodySchema = Schema.Struct({
9393
});
9494
export type StatusBody = Schema.Schema.Type<typeof StatusBodySchema>;
9595

96-
// `lease_request` / `lease_constraints` still live as zod (slice-boundary;
97-
// `lease-schema.ts` migration is its own future slice). Bridge through
98-
// `fromZod` so the surrounding Effect `Schema.Struct` can compose them.
99-
const LeaseEffectSchema = fromZod(LeaseSchema);
100-
const LeaseConstraintsEffectSchema = fromZod(LeaseConstraintsSchema);
96+
// `LeaseSchema` infers `Record<string, ReadonlyArray<string>>`; the rest of
97+
// the runtime (lease.ts, job-runner.ts) treats `Lease` as the mutable
98+
// `Record<string, string[]>` shape from the zod twin. Bridge through
99+
// `fromZod` so the inferred `DelegateBody.lease_request` stays
100+
// assignment-compatible with the zod-derived `Lease` alias until slice #50
101+
// migrates the `Lease` consumers to readonly arrays.
102+
const LeaseMutableEffectSchema = fromZod(LeaseZodSchema);
101103

102104
/** §8.2 `delegate` event-kind body. */
103105
export const DelegateBodySchema = Schema.Struct({
104106
delegate_id: Schema.String.pipe(Schema.nonEmptyString()),
105107
agent: Schema.String.pipe(Schema.nonEmptyString()),
106108
input: Schema.Unknown,
107-
lease_request: Schema.optional(LeaseEffectSchema),
109+
lease_request: Schema.optional(LeaseMutableEffectSchema),
108110
/** v1.1 §9.4/§9.5 — child lease bound; MUST NOT exceed parent's. */
109-
lease_constraints: Schema.optional(LeaseConstraintsEffectSchema),
111+
lease_constraints: Schema.optional(LeaseConstraintsSchema),
110112
});
111113
export type DelegateBody = Schema.Schema.Type<typeof DelegateBodySchema>;
112114

packages/core/src/messages/execution.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111

1212
import { JobEventPayloadZodSchema } from "./events.js";
1313
import {
14-
LeaseConstraintsSchema,
15-
LeaseSchema,
14+
LeaseConstraintsZodSchema,
15+
LeaseZodSchema,
1616
} from "./lease-schema.js";
1717

1818
// ARCP v1.0 §7-§8 job-related envelopes.
@@ -22,8 +22,10 @@ export {
2222
isValidCapabilityName,
2323
type Lease,
2424
LeaseConstraintsSchema,
25+
LeaseConstraintsZodSchema,
2526
type LeaseConstraints,
2627
LeaseSchema,
28+
LeaseZodSchema,
2729
RESERVED_CAPABILITY_NAMES,
2830
type ReservedCapabilityName,
2931
} from "./lease-schema.js";
@@ -159,8 +161,8 @@ export const JobSubmitPayloadSchema = Schema.Struct({
159161
export const JobSubmitPayloadZodSchema = z.object({
160162
agent: z.string().min(1),
161163
input: z.unknown(),
162-
lease_request: LeaseSchema.optional(),
163-
lease_constraints: LeaseConstraintsSchema.optional(),
164+
lease_request: LeaseZodSchema.optional(),
165+
lease_constraints: LeaseConstraintsZodSchema.optional(),
164166
idempotency_key: z.string().min(1).optional(),
165167
max_runtime_sec: z.number().int().positive().optional(),
166168
});
@@ -187,9 +189,9 @@ export const JobAcceptedPayloadZodSchema = z.object({
187189
job_id: z.string().min(1).brand<"JobId">(),
188190
/** Resolved `name@version` when v1.1 agent_versions is in use; bare name otherwise. */
189191
agent: z.string().min(1).optional(),
190-
lease: LeaseSchema,
192+
lease: LeaseZodSchema,
191193
/** v1.1 §9.5 — echoed lease constraints. */
192-
lease_constraints: LeaseConstraintsSchema.optional(),
194+
lease_constraints: LeaseConstraintsZodSchema.optional(),
193195
/** v1.1 §9.6 — initial budget counters, when `cost.budget` is in the lease. */
194196
budget: JobBudgetZodSchema.optional(),
195197
accepted_at: z.string().min(1),
@@ -358,8 +360,8 @@ export const JobSubscribedPayloadZodSchema = z.object({
358360
job_id: z.string().min(1).brand<"JobId">(),
359361
current_status: JobStateZodSchema,
360362
agent: z.string().min(1),
361-
lease: LeaseSchema,
362-
lease_constraints: LeaseConstraintsSchema.optional(),
363+
lease: LeaseZodSchema,
364+
lease_constraints: LeaseConstraintsZodSchema.optional(),
363365
budget: JobBudgetZodSchema.optional(),
364366
parent_job_id: z.string().brand<"JobId">().nullable().optional(),
365367
trace_id: z.string().brand<"TraceId">().optional(),

packages/core/src/messages/lease-schema.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Schema } from "effect";
12
import { z } from "zod";
23

34
/**
@@ -31,21 +32,49 @@ export function isValidCapabilityName(name: string): boolean {
3132
return /^x-vendor(\.[a-z0-9_-]+){2,}$/.test(name);
3233
}
3334

34-
/** §9.1 lease: capability → list of glob patterns. */
35-
export const LeaseSchema = z.record(
35+
/**
36+
* §9.1 lease (Effect Schema): capability → list of glob patterns.
37+
*
38+
* Native Effect surface for in-process consumers. The zod twin
39+
* {@link LeaseZodSchema} still feeds `messageEnvelope()` (slice #50).
40+
*
41+
* Effect's `Schema.Record` silently drops keys that fail the key schema, so
42+
* `{ "": [...] }` decodes to `{}`. The zod twin rejects empty keys at the
43+
* wire layer where it counts.
44+
*/
45+
export const LeaseSchema = Schema.Record({
46+
key: Schema.String.pipe(Schema.nonEmptyString()),
47+
value: Schema.Array(Schema.String.pipe(Schema.nonEmptyString())),
48+
});
49+
50+
/** §9.1 lease (zod twin) — drives the zod-typed envelope wrappers. */
51+
export const LeaseZodSchema = z.record(
3652
z.string().min(1),
3753
z.array(z.string().min(1)),
3854
);
39-
export type Lease = z.infer<typeof LeaseSchema>;
55+
/**
56+
* `Lease` is the zod-inferred type so it stays structurally equivalent to
57+
* `Record<string, string[]>` for the many in-process consumers
58+
* (`runtime/lease.ts`, `client-handle.ts`, etc.). The Effect schema infers a
59+
* `ReadonlyArray<string>` value which would be a non-trivial breaking change
60+
* to callers — defer that to slice #50.
61+
*/
62+
export type Lease = z.infer<typeof LeaseZodSchema>;
4063

4164
/**
42-
* v1.1 §9.5 lease constraints. Currently carries only `expires_at` (ISO 8601
43-
* UTC with `Z` suffix), which sets a hard upper bound on the lease's lifetime.
65+
* v1.1 §9.5 lease constraints (Effect Schema). Currently carries only
66+
* `expires_at` (ISO 8601 UTC with `Z` suffix), which sets a hard upper bound
67+
* on the lease's lifetime.
4468
*
4569
* The schema validates `expires_at` is a non-empty string. Stricter checks
4670
* (UTC, future-dated) are enforced at submit time by the runtime.
4771
*/
48-
export const LeaseConstraintsSchema = z.object({
72+
export const LeaseConstraintsSchema = Schema.Struct({
73+
expires_at: Schema.optional(Schema.String.pipe(Schema.nonEmptyString())),
74+
});
75+
76+
/** v1.1 §9.5 lease constraints (zod twin). */
77+
export const LeaseConstraintsZodSchema = z.object({
4978
expires_at: z.string().min(1).optional(),
5079
});
51-
export type LeaseConstraints = z.infer<typeof LeaseConstraintsSchema>;
80+
export type LeaseConstraints = z.infer<typeof LeaseConstraintsZodSchema>;

0 commit comments

Comments
 (0)