opencode-effect-enforcer 0.2.5 → 0.2.6

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 (49) hide show
  1. package/README.md +42 -140
  2. package/docs/effect-4.0.0-rc.116-changelog.md +2654 -0
  3. package/docs/effect-4.0.0-rc.116.md +102 -0
  4. package/guidance/effect-first-development.md +23 -14
  5. package/guidance/progressive-disclosure-guidance.md +3 -3
  6. package/package.json +2 -2
  7. package/patterns/avoid-direct-tag-checks.md +1 -1
  8. package/patterns/avoid-process-env.md +4 -4
  9. package/patterns/context-tag-extends.md +4 -4
  10. package/patterns/prefer-redacted-config.md +10 -10
  11. package/patterns/require-effect-concurrency.md +1 -1
  12. package/skills/effect-ai-chat/SKILL.md +2 -2
  13. package/skills/effect-ai-language-model/SKILL.md +36 -4
  14. package/skills/effect-ai-prompt/SKILL.md +1 -1
  15. package/skills/effect-ai-provider/SKILL.md +23 -12
  16. package/skills/effect-ai-tool/SKILL.md +13 -0
  17. package/skills/effect-atom-rpc/SKILL.md +7 -1
  18. package/skills/effect-atom-state/SKILL.md +8 -2
  19. package/skills/effect-cache/SKILL.md +10 -1
  20. package/skills/effect-cli/SKILL.md +105 -94
  21. package/skills/effect-command-executor/SKILL.md +7 -1
  22. package/skills/effect-config/SKILL.md +67 -44
  23. package/skills/effect-domain-modeling/SKILL.md +3 -3
  24. package/skills/effect-error-handling/SKILL.md +2 -2
  25. package/skills/effect-fiber/SKILL.md +2 -2
  26. package/skills/effect-filesystem/SKILL.md +34 -4
  27. package/skills/effect-http-api/SKILL.md +17 -2
  28. package/skills/effect-http-client/SKILL.md +11 -2
  29. package/skills/effect-http-server/SKILL.md +28 -11
  30. package/skills/effect-layer-design/SKILL.md +6 -2
  31. package/skills/effect-mcp-server/SKILL.md +21 -4
  32. package/skills/effect-observability/SKILL.md +2 -2
  33. package/skills/effect-optics/SKILL.md +1 -1
  34. package/skills/effect-parallelization/SKILL.md +2 -2
  35. package/skills/effect-pattern-matching/SKILL.md +1 -1
  36. package/skills/effect-platform-abstraction/SKILL.md +3 -3
  37. package/skills/effect-rpc-api/SKILL.md +3 -3
  38. package/skills/effect-rpc-client/SKILL.md +14 -13
  39. package/skills/effect-rpc-cluster/SKILL.md +15 -12
  40. package/skills/effect-rpc-server/SKILL.md +11 -13
  41. package/skills/effect-scheduling/SKILL.md +7 -0
  42. package/skills/effect-schema-composition/SKILL.md +12 -4
  43. package/skills/effect-schema-v4/SKILL.md +75 -20
  44. package/skills/effect-scope/SKILL.md +4 -4
  45. package/skills/effect-socket/SKILL.md +161 -658
  46. package/skills/effect-sql/SKILL.md +50 -12
  47. package/skills/effect-stream/SKILL.md +19 -24
  48. package/skills/effect-testing/SKILL.md +35 -22
  49. package/skills/effect-workflow/SKILL.md +13 -2
@@ -81,7 +81,7 @@ yield* Effect.forEach(ids, fetchUser, { concurrency: 8 });
81
81
  yield* Effect.forEach(ids, fetchUser, { concurrency: 'unbounded' });
82
82
  ```
83
83
 
84
- As of beta.102, `"inherit"`, `References.CurrentConcurrency`, and `Effect.withConcurrency` are removed. Reusable APIs that expose fan-out policy should accept a `Types.Concurrency` value and pass it explicitly to each combinator instead of relying on ambient configuration.
84
+ Reusable APIs that expose fan-out policy should accept a `Types.Concurrency` value and pass it explicitly to each combinator instead of relying on ambient configuration.
85
85
 
86
86
  ### Failure semantics under concurrency
87
87
 
@@ -652,7 +652,7 @@ const checkConfig = (entries: ReadonlyArray<Entry>) =>
652
652
  ## Common Mistakes
653
653
 
654
654
  1. **Assuming `Effect.all` / `Effect.forEach` are parallel by default** — they are sequential. Pass `{ concurrency: n | 'unbounded' }` explicitly; without it you also silently lose request batching (see effect-batching).
655
- 2. **Using removed ambient concurrency APIs** — beta.102 removed `"inherit"`, `References.CurrentConcurrency`, and `Effect.withConcurrency`. Pass a number or `"unbounded"` explicitly at each combinator.
655
+ 2. **Relying on ambient concurrency** — pass a number or `"unbounded"` explicitly at each combinator.
656
656
  3. **Wrong option key on zips** — `Effect.zip`/`zipWith` take `{ concurrent: true }` (boolean), not `{ concurrency: ... }`.
657
657
  4. **v3 `mode: 'either'` / `mode: 'validate'` on `Effect.all`** — gone. v4 has `mode: 'result'` (slots become `Result<A, E>`); for accumulate-all-failures use `Effect.validate`, which fails with `NonEmptyArray<E>`.
658
658
  5. **`Effect.makeSemaphore` / `Effect.makeLatch` no longer exist** — they moved to their own modules: `Semaphore.make(n)`, `Latch.make(open?)`, both importable from `'effect'`.
@@ -28,7 +28,7 @@ Reference this for:
28
28
  - Declarative, not imperative
29
29
  - Pipeline-friendly composition
30
30
 
31
- ## Schema-First Matching (rc.112)
31
+ ## Schema-First Matching
32
32
 
33
33
  For domain/wire models, prefer class variants combined with
34
34
  `Schema.Union([...]).pipe(Schema.toTaggedUnion('kind'))`. Use `.match` for
@@ -206,7 +206,7 @@ const fileOperations = Effect.gen(function* () {
206
206
  });
207
207
  ```
208
208
 
209
- `fs.watch(directory)` reports direct-child changes by default; pass `{ recursive: true }` to include nested subdirectories. For open handles, `file.seek(offset, 'start' | 'current')` returns the new offset as a branded `FileSystem.Size`. The old `FileSystem.File.Descriptor` type and `file.descriptor` property were removed in beta.103; use scoped `File` methods instead.
209
+ `fs.watch(directory)` reports direct-child changes by default; pass `{ recursive: true }` for nested subdirectories. `file.seek(offset, 'start' | 'current')` accepts and returns `bigint`; negative resulting positions fail without moving the cursor. File byte counts use `ByteSize.ByteSize`. Use scoped File methods for handle operations.
210
210
 
211
211
  **Streaming Files:**
212
212
 
@@ -782,10 +782,10 @@ declare const someOperation: Effect.Effect<string>;
782
782
 
783
783
  // ✅ CORRECT - Type-safe CLI with full Effect integration
784
784
  // Define arguments
785
- const inputArg = Argument.file('input');
785
+ const inputArg = Argument.File('input');
786
786
 
787
787
  // Define flags
788
- const verboseFlag = Flag.boolean('verbose').pipe(Flag.withAlias('v'));
788
+ const verboseFlag = Flag.Boolean('verbose').pipe(Flag.withAlias('v'), Flag.withDefault(false));
789
789
 
790
790
  // Define command
791
791
  const command = CliCommand.make(
@@ -9,11 +9,11 @@ This skill covers the **contract layer**: the definitions that client and server
9
9
 
10
10
  ## Effect Source Reference
11
11
 
12
- In rc.112 schema encoding is selected by the transport's `codecFor`, rather
12
+ Schema encoding is selected by the transport's `codecFor`, rather
13
13
  than being fixed to canonical JSON for every protocol. Preserve the schemas and
14
14
  their decoding/encoding requirements in shared contracts; custom protocol
15
15
  implementations must supply `codecFor` (see `effect-rpc-client` /
16
- `effect-rpc-server`). Existing JSON, NDJSON, JSON-RPC, and MsgPack formats keep
16
+ `effect-rpc-server`). JSON, NDJSON, and JSON-RPC formats use
17
17
  their wire representation; the new SchemaBinary transport has its own codecs.
18
18
 
19
19
  The Effect v4 source is at `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/`. Read it directly when in doubt — these modules are under `unstable` and move between betas.
@@ -627,5 +627,5 @@ it('GetUser exits round-trip', () => {
627
627
  11. **Mutating in place.** `annotate`, `prefix`, `middleware`, `setSuccess`, etc. all return new `Rpc`/`RpcGroup` values; discarding the return value is a no-op.
628
628
  12. **Renaming or re-prefixing rpcs after deployment.** `_tag` is the wire identity; old clients will send tags the server no longer knows. Treat tag changes like breaking schema changes.
629
629
  13. **Passing a union to `Schema.Union` variadically.** v4 takes an array: `Schema.Union([OrderError, RateLimited])`, not `Schema.Union(OrderError, RateLimited)`.
630
- 14. **Declaring errors as plain `Schema.Struct`s.** Use `Schema.TaggedError` (or `Schema.Error` with a `Schema.tag` field) so errors are yieldable, `catchTag`-able, and carry a stable `_tag` on the wire. The former `Schema.TaggedErrorClass` / `Schema.ErrorClass` names were removed in beta.104.
630
+ 14. **Declaring errors as plain `Schema.Struct`s.** Use `Schema.TaggedError` (or `Schema.Error` with a `Schema.tag` field) so errors are yieldable, `catchTag`-able, and carry a stable `_tag` on the wire.
631
631
  15. **Expecting stack traces in remote defects.** The default `Schema.Defect()` strips stacks; opt in per rpc with `defect: Schema.Defect({ includeStack: true })`.
@@ -15,7 +15,7 @@ Key files:
15
15
 
16
16
  - `packages/effect/src/unstable/rpc/RpcClient.ts` — `make`, `makeNoSerialization`, the `Protocol` service, `layerProtocolHttp`/`layerProtocolSocket`/`layerProtocolWorker` (+ `makeProtocol*`), `CurrentHeaders`, `withHeaders`, `ConnectionHooks`
17
17
  - `packages/effect/src/unstable/rpc/RpcClientError.ts` — `RpcClientError` and `RpcClientDefect`
18
- - `packages/effect/src/unstable/rpc/RpcSerialization.ts` — `json`, `ndjson`, `jsonRpc`, `ndJsonRpc`, `msgPack` codecs, their layers, the `Parser` interface and `includesFraming`
18
+ - `packages/effect/src/unstable/rpc/RpcSerialization.ts` — JSON, NDJSON, JSON-RPC, SchemaBinary codecs, their layers, the `Parser` interface and `includesFraming`
19
19
  - `packages/effect/src/unstable/rpc/RpcTest.ts` — `makeClient` in-process test client
20
20
  - `packages/effect/src/unstable/rpc/RpcWorker.ts` — `InitialMessage`, `layerInitialMessage`, `initialMessage`
21
21
  - `packages/effect/src/unstable/rpc/RpcMessage.ts` — wire vocabulary: `Request`, `Ack`, `Interrupt`, `Chunk`, `Exit`, `Defect`, `Ping`/`Pong`, `ClientProtocolError`, `RequestId`
@@ -30,7 +30,7 @@ A client is assembled from three replaceable layers plus your shared `RpcGroup`
30
30
  ```
31
31
  RpcClient.make(group) typed methods, spans, request ids
32
32
  requires: Protocol ← RpcClient.layerProtocolHttp / Socket / Worker
33
- requires: RpcSerialization ← RpcSerialization.layerJson / Ndjson / MsgPack / JsonRpc
33
+ requires: RpcSerialization ← JSON / NDJSON / SchemaBinary / JSON-RPC layer
34
34
  requires: transport service ← HttpClient | Socket.Socket | WorkerPlatform + Spawner
35
35
  ```
36
36
 
@@ -263,14 +263,14 @@ const BrowserProtocolLive = RpcClient.layerProtocolSocket().pipe(
263
263
  );
264
264
  ```
265
265
 
266
- `Socket.layerWebSocket(url, { closeCodeIsError?, openTimeout?, protocols? })` requires a `WebSocketConstructor`; `layerWebSocketConstructorGlobal` uses `globalThis.WebSocket`. `NodeSocket.layerWebSocket` bundles the constructor.
266
+ `Socket.layerWebSocket(url, { openTimeout?, protocols?, highWaterMark? })` requires a `WebSocketConstructor`; `layerWebSocketConstructorGlobal` uses `globalThis.WebSocket`. `NodeSocket.layerWebSocket` bundles the constructor. Close is a typed failure; reconnect policies reacquire the scoped reader.
267
267
 
268
268
  ### Raw TCP
269
269
 
270
270
  ```ts
271
271
  const TcpProtocolLive = RpcClient.layerProtocolSocket().pipe(
272
272
  Layer.provide(NodeSocket.layerNet({ port: 9000 })),
273
- Layer.provide(RpcSerialization.layerMsgPack)
273
+ Layer.provide(RpcSerialization.layerSchemaBinary())
274
274
  );
275
275
  ```
276
276
 
@@ -293,7 +293,7 @@ const TcpProtocolLive = RpcClient.layerProtocolSocket().pipe(
293
293
 
294
294
  Build your own with `RpcClient.Protocol.make((writeResponse, clientIds) => Effect<Omit<Service, 'run'>>)` — it buffers server responses per client until that client's `run` loop is installed. You rarely need this; prefer `RpcTest` for in-memory wiring (section 11).
295
295
 
296
- As of rc.112 the returned service must include `codecFor`, normally forwarded
296
+ The returned service must include `codecFor`, normally forwarded
297
297
  from the selected `RpcSerialization` service. It chooses the schema codec for
298
298
  RPC payloads/results while the protocol handles envelopes. JSON-compatible
299
299
  custom transports can use `codecFor: Schema.toCodecJson`. Preserve the schema's
@@ -310,26 +310,27 @@ full `RpcSerialization.CodecFor` contract.
310
310
  |---|---|---|---|
311
311
  | `RpcSerialization.layerJson` | `application/json` | no | HTTP (response decoded once, as an array); WebSocket (each ws message is one frame already) |
312
312
  | `RpcSerialization.layerNdjson` | `application/ndjson` | yes (newline) | anything — the safe default; enables streaming over HTTP |
313
- | `RpcSerialization.layerMsgPack` | `application/msgpack` | yes (msgpack frames) | binary framing with JSON-compatible schema codecs; msgpackr `useRecords: true` |
314
313
  | `RpcSerialization.layerSchemaBinary(options?)` | `application/vnd.effect.rpc+schema-binary` | yes | schema-derived binary framing and payload codecs; pair on both peers |
315
314
 
316
315
  `layerSchemaBinary({ maxFrameSize?, fingerprintPayloads? })` defaults to a
317
316
  16 MiB frame limit and no payload fingerprint. Envelopes are fingerprinted and
318
317
  dictionary-enabled; payload fingerprints opt into strict layout agreement.
319
- Existing built-in formats retain their wire encoding in rc.112. Workers use
318
+ Workers use
320
319
  `Schema.toCodecJson` with structured clone without a serialization layer.
321
320
  | `RpcSerialization.layerJsonRpc({ contentType? })` | `application/json` | no | JSON-RPC 2.0 interop over HTTP/WebSocket; maps rpc tag ↔ `method`, supports batch arrays |
322
321
  | `RpcSerialization.layerNdJsonRpc({ contentType? })` | `application/json-rpc` | yes (newline) | JSON-RPC 2.0 over sockets |
323
322
 
324
- Rules of thumb, verified by the upstream e2e matrix (http: ndjson/msgpack/ndJsonRpc; ws: ndjson/json/msgpack/jsonRpc; tcp: ndjson/msgpack/ndJsonRpc):
323
+ Choose serialization according to transport framing:
325
324
 
326
- - **Raw TCP needs a framed codec** (`ndjson` / `msgPack` / `ndJsonRpc`) — plain `json` cannot find message boundaries in a byte stream.
325
+ - **Raw TCP needs a framed codec** (NDJSON, SchemaBinary, or newline JSON-RPC) — plain JSON cannot find message boundaries in a byte stream.
327
326
  - **WebSocket works with any codec** including plain `json`, because the ws transport frames messages itself.
328
327
  - **HTTP + unframed (`json`/`jsonRpc`)**: the client buffers the whole response and expects a JSON array of response messages — streaming rpcs only flush when the response ends.
329
- - **HTTP + framed (`ndjson`/`msgPack`/`ndJsonRpc`)**: the client incrementally parses the response body — streaming rpc chunks arrive as the server emits them, over a single POST.
330
- - **Client and server must use the same codec.** A msgpack client against a json server garbles both directions.
328
+ - **HTTP + framed serialization**: the client incrementally parses the response body — streaming rpc chunks arrive as the server emits them, over a single POST.
329
+ - **Client and server must use the same codec.** SchemaBinary and JSON are distinct wire contracts.
331
330
 
332
- `RpcSerialization.makeMsgPack(options)` customizes msgpackr (`useRecords`, `useFloat32`, ...); wrap with `Layer.succeed(RpcSerialization.RpcSerialization)(RpcSerialization.makeMsgPack({ useRecords: false }))` if the peer cannot handle msgpackr record extensions.
331
+ Use `layerSchemaBinary()` for schema-derived binary transport, and coordinate
332
+ both peers when changing formats. Do not treat a serialization change as a
333
+ transparent migration of persisted data.
333
334
 
334
335
  ---
335
336
 
@@ -666,7 +667,7 @@ const WorkerClientLive = UsersClient.layer.pipe(
666
667
  1. **Importing from `@effect/rpc`.** Gone in v4 — everything is `effect/unstable/rpc`. And `import { RpcClientError } from 'effect/unstable/rpc'` gives you a *namespace*; import the class from `effect/unstable/rpc/RpcClientError`.
667
668
  2. **Missing `Scope` for `RpcClient.make`.** The constructor is scoped. Build clients inside `Layer.effect(Tag)(RpcClient.make(group))` or under `Effect.scoped` — providing protocol layers alone will not discharge `Scope`.
668
669
  3. **Forgetting the client middleware layer.** A group with a `requiredForClient: true` middleware needs `Layer.provide(RpcMiddleware.layerClient(M, ...))` on the client (and in `RpcTest.makeClient`'s context). The compile error points at an unsatisfied `ForClient<M>` requirement — provide the layer, don't cast.
669
- 4. **Wrong codec/transport pairing.** Raw TCP + `layerJson` corrupts framing — use `layerNdjson`/`layerMsgPack`/`layerNdJsonRpc`. Over HTTP, unframed `layerJson` buffers the whole response, so streaming rpcs stall until the request ends — use a framed codec. WebSocket alone is fine with any codec (the transport frames messages).
670
+ 4. **Wrong codec/transport pairing.** Raw TCP requires `layerNdjson`, `layerSchemaBinary()`, or `layerNdJsonRpc()`. HTTP with unframed JSON buffers the response; use a framed codec for streaming. WebSocket supplies its own framing.
670
671
  5. **Mismatched client/server serialization.** Both sides share one `RpcSerialization` choice; there is no negotiation. Garbled `RpcClientDefect: Error decoding ...` errors usually mean the codecs differ.
671
672
  6. **Reading mixed-case header names server-side.** `Headers.fromInput` lowercases keys: send `{ userId: '123' }`, read `headers.userid`. Same for `withHeaders` and middleware `Headers.set`.
672
673
  7. **Expecting `discard: true` to be error-free.** It only removes the rpc's *declared* error; `RpcClientError` and middleware errors remain, and over HTTP the POST round-trip is still awaited.
@@ -18,7 +18,7 @@ Key files:
18
18
  - `packages/effect/src/unstable/rpc/RpcServer.ts` — `make`, `layer`, `layerHttp`, every `layerProtocol*` and `toHttpEffect*`
19
19
  - `packages/effect/src/unstable/rpc/RpcClient.ts` — `make`, `Protocol`, every `layerProtocol*`, `withHeaders`, `CurrentHeaders`, `ConnectionHooks`
20
20
  - `packages/effect/src/unstable/rpc/RpcMiddleware.ts` — `Service` constructor, `layerClient`
21
- - `packages/effect/src/unstable/rpc/RpcSerialization.ts` — json/ndjson/jsonRpc/ndJsonRpc/msgPack codecs and their layers
21
+ - `packages/effect/src/unstable/rpc/RpcSerialization.ts` — JSON/NDJSON/JSON-RPC/SchemaBinary codecs and layers
22
22
  - `packages/effect/src/unstable/rpc/RpcTest.ts` — in-process test client
23
23
  - `packages/effect/src/unstable/rpc/RpcWorker.ts` — `InitialMessage` for worker transports
24
24
  - `packages/effect/src/unstable/rpc/RpcSchema.ts` — `Stream` schema marker, `ClientAbort` cause annotation
@@ -99,7 +99,7 @@ import { BunClusterHttp, BunClusterSocket } from '@effect/platform-bun';
99
99
  ## Architecture at a Glance
100
100
 
101
101
  ```
102
- wire format (json | ndjson | msgpack | jsonRpc | ndJsonRpc)
102
+ wire format (json | ndjson | schema-binary | jsonRpc | ndJsonRpc)
103
103
  │
104
104
  ┌──────────────┐ Protocol │ Protocol ┌──────────────┐
105
105
  │ RpcClient │ ───────────────► │ ◄───────────────── │ RpcServer │
@@ -794,21 +794,24 @@ The choice of serialization is load-bearing because of *framing*. Some transport
794
794
  | `RpcSerialization.layerNdjson` | `application/ndjson` | yes (newline) | `layerProtocolWebsocket`, sockets, http+stream | Newline-delimited JSON; one of several streaming formats |
795
795
  | `RpcSerialization.layerJsonRpc()` | `application/json` (configurable) | no | JSON-RPC 2.0 interop | Maps `_tag` to `method`; preserves batched arrays |
796
796
  | `RpcSerialization.layerNdJsonRpc()` | `application/json-rpc` (configurable) | yes (newline) | JSON-RPC 2.0 over sockets | |
797
- | `RpcSerialization.layerMsgPack` | `application/msgpack` | yes (msgpack frames) | binary transports | JSON-compatible schema codecs; uses `useRecords: true` |
798
797
  | `RpcSerialization.layerSchemaBinary(options?)` | `application/vnd.effect.rpc+schema-binary` | yes | binary transports, framed HTTP | schema-aware binary payloads and envelopes |
799
798
 
800
- In rc.112 serializations and both protocol services require `codecFor`.
799
+ Serializations and both protocol services require `codecFor`.
801
800
  Custom protocols forward it from the serialization; custom JSON-compatible
802
801
  protocols can use `Schema.toCodecJson`. Cluster network codecs now follow the
803
802
  transport while persistent message storage remains JSON. See `effect-rpc-server`
804
803
  for the complete contract, binary options, and compatibility constraints.
805
804
 
806
- `RpcSerialization.makeMsgPack(options?)` lets you customize msgpackr (`useRecords`, `useFloat32`, etc.).
805
+ SchemaBinary is the default cluster network serialization. Select NDJSON explicitly
806
+ for JSON transport. Event-log persistence and remote messages also use SchemaBinary;
807
+ coordinate persisted-data and peer format changes. Cluster message storage uses
808
+ its own JSON codecs: when a reply cannot be stored, waiting callers receive the
809
+ same defect fallback that storage records.
807
810
 
808
811
  Picking the wrong one is a real bug:
809
812
 
810
813
  - `layerJson` over a websocket → no framing → the first chunk past the first message is misinterpreted
811
- - `layerMsgPack` against a JSON-only HTTP client → garbled responses
814
+ - SchemaBinary against a JSON-only HTTP client → incompatible responses
812
815
  - `layerNdjson` against `layerProtocolHttp` → streams incrementally through a bounded response queue (default 16), without RPC acks
813
816
 
814
817
  ## Testing — `RpcTest.makeClient`
@@ -1232,7 +1235,7 @@ The Node and Bun platform packages ship opinionated all-in-one layers that wire
1232
1235
  import { NodeClusterSocket } from '@effect/platform-node';
1233
1236
 
1234
1237
  const ClusterLayer = NodeClusterSocket.layer({
1235
- serialization: 'msgpack', // or 'ndjson'; default 'msgpack'
1238
+ serialization: 'binary', // or 'ndjson'; default 'binary'
1236
1239
  clientOnly: false, // true → don't bind a server port
1237
1240
  storage: 'sql', // 'sql' | 'memory' | 'byo'; default 'sql'
1238
1241
  runnerHealth: 'ping', // 'ping' | 'k8s'; default 'ping'
@@ -1361,9 +1364,9 @@ The generated **RPC** payload wraps the original payload as `{ entityId: string,
1361
1364
 
1362
1365
  ### Encrypted Event-Log Compatibility
1363
1366
 
1364
- As of beta.106, `EventLogEncryption.encrypt` returns `{ iv, encryptedEntry }` for every input entry, and `EventLogMessage.WriteEntries.encryptedEntries` carries `{ entryId, iv, encryptedEntry }` values. A fresh AES-GCM IV is generated per entry. This changes the encrypted replication wire format: upgrade encrypted event-log clients and servers together rather than performing a mixed-version rolling deployment.
1367
+ `EventLogEncryption.encrypt` returns `{ iv, encryptedEntry }` for every input entry, and `EventLogMessage.WriteEntries.encryptedEntries` carries `{ entryId, iv, encryptedEntry }` values. Each entry uses a fresh AES-GCM IV. Encrypted replication peers must agree on the wire format.
1365
1368
 
1366
- In rc.112, `EventJournal.withRemoteUncommited` (spelling intentional) receives a
1369
+ `EventJournal.withRemoteUncommited` (spelling intentional) receives a
1367
1370
  non-empty readonly entry array in its callback and returns `Effect<Option<A>, ...>`.
1368
1371
  No pending entries means `None` and the callback is not invoked; `Some(result)`
1369
1372
  means it ran. Update adapters/tests that assumed every flush calls the writer.
@@ -1397,7 +1400,7 @@ To namespace the generated rpcs, pass `prefix` as the **second** argument: `Work
1397
1400
 
1398
1401
  These proxies are how you give a frontend or an external system a typed RPC/HTTP surface that drives durable workflows, without leaking workflow-engine internals.
1399
1402
 
1400
- In rc.112 workflow discard endpoints return a `Schema.String` execution ID
1403
+ Workflow discard endpoints return a `Schema.String` execution ID
1401
1404
  instead of `void`, for both RPC and HTTP. Call the generated `<Name>Discard` RPC
1402
1405
  normally to receive it; passing the RPC client's `{ discard: true }` option
1403
1406
  discards even that ID. Entity discard endpoints keep their separate contract.
@@ -1622,7 +1625,7 @@ const usersByName = Effect.gen(function*() {
1622
1625
  3. **Treating `Rpc.fork` and `Rpc.uninterruptible` like options.** They are wrappers — apply with `.pipe(Rpc.fork)` on the handler's return value.
1623
1626
  4. **Forgetting `primaryKey` on entity rpcs that need dedup.** Without it, retried sends are *not* deduplicated; this is critical for clustered handlers that should be idempotent.
1624
1627
  5. **Using `JSON.parse`/`JSON.stringify` on rpc payloads.** Schemas already round-trip; if you need a JSON string boundary, use `Schema.fromJsonString(...)`.
1625
- 6. **Picking `layerJson` for a streaming or socket transport.** No framing → message corruption. Use `layerNdjson` or `layerMsgPack`.
1628
+ 6. **Picking unframed JSON for raw TCP or incremental HTTP streaming.** Use `layerNdjson` or `layerSchemaBinary()`.
1626
1629
  7. **Forgetting `Layer.provide(AuthClient)` on a `requiredForClient: true` middleware.** Compile error, but a confusing one if you don't know to look.
1627
1630
  8. **Using `WorkflowEngine.layerMemory` in production.** It is testing-only; use `ClusterWorkflowEngine.layer` plus a real cluster bundle.
1628
1631
  9. **Forgetting `ShardingConfig` when using `Entity.makeTestClient`.** `TestRunner.layer` provides one; `makeTestClient` does not.
@@ -1649,5 +1652,5 @@ const usersByName = Effect.gen(function*() {
1649
1652
  - Use `RpcTest.makeClient` for handler tests and `Entity.makeTestClient` for entity tests; reach for `TestRunner.layer` for full-cluster integration tests.
1650
1653
  - For production cluster, use `NodeClusterSocket.layer` / `NodeClusterHttp.layer` (or the Bun equivalents) unless you specifically need to assemble layers manually.
1651
1654
  - Size `maxResidentEntities` and `unprocessedMessageBatchSize` deliberately for the runner's memory and storage throughput.
1652
- - Match transport ↔ serialization: HTTP → `layerJson`; sockets/websocket/streaming → `layerNdjson` or `layerMsgPack`.
1655
+ - Match serialization to framing: unframed JSON for whole messages; NDJSON or SchemaBinary for byte streams and incremental responses.
1653
1656
  - Pattern-match on `client.GetUser(...).pipe(Effect.catchTag('UserNotFound', ...), Effect.catchFilter(...))` for typed recovery; reserve broad `Effect.catch` for an explicit boundary recovery policy.
@@ -17,7 +17,7 @@ Key files:
17
17
  - `packages/effect/src/unstable/rpc/RpcGroup.ts` — `toLayer`, `toLayerHandler`, `toHandlers`, `accessHandler`, `of`, handler type derivation
18
18
  - `packages/effect/src/unstable/rpc/Rpc.ts` — `ServerClient`, `Handler`, `ToHandlerFn`, `ResultFrom`, `fork`, `uninterruptible`, `ServicesServer`
19
19
  - `packages/effect/src/unstable/rpc/RpcMiddleware.ts` — `Service` constructor, server middleware function shape, `layerClient`
20
- - `packages/effect/src/unstable/rpc/RpcSerialization.ts` — `json`, `ndjson`, `jsonRpc`, `ndJsonRpc`, `msgPack` parsers and their layers
20
+ - `packages/effect/src/unstable/rpc/RpcSerialization.ts` — JSON, NDJSON, JSON-RPC, and SchemaBinary parsers and layers
21
21
  - `packages/effect/src/unstable/rpc/RpcMessage.ts` — the wire vocabulary (`Request`, `Ack`, `Interrupt`, `Eof`, `Chunk`, `Exit`, `Defect`, `ClientEnd`)
22
22
  - `packages/effect/src/unstable/rpc/RpcWorker.ts` — `InitialMessage` for worker transports
23
23
  - `packages/effect/src/unstable/rpc/RpcTest.ts` — in-process test client
@@ -293,7 +293,7 @@ const TcpServer = RpcServer.layer(UserRpcs).pipe(
293
293
  Layer.provide(UsersLive),
294
294
  Layer.provide(RpcServer.layerProtocolSocketServer),
295
295
  Layer.provide(NodeSocketServer.layer({ port: 9000 })),
296
- Layer.provide(RpcSerialization.layerMsgPack) // must be a framed format (§4)
296
+ Layer.provide(RpcSerialization.layerSchemaBinary()) // must be a framed format (§4)
297
297
  );
298
298
  ```
299
299
 
@@ -349,10 +349,9 @@ Provide exactly one `RpcSerialization` layer. The load-bearing property is `incl
349
349
  | `RpcSerialization.layerNdjson` | `application/ndjson` | yes | newline-delimited JSON |
350
350
  | `RpcSerialization.layerJsonRpc({ contentType? })` | `application/json` | no | JSON-RPC 2.0 interop |
351
351
  | `RpcSerialization.layerNdJsonRpc({ contentType? })` | `application/json-rpc` | yes | JSON-RPC 2.0, newline-framed |
352
- | `RpcSerialization.layerMsgPack` | `application/msgpack` | yes | msgpackr `useRecords: true`; schema payloads still use JSON codecs |
353
352
  | `RpcSerialization.layerSchemaBinary(options?)` | `application/vnd.effect.rpc+schema-binary` | yes | schema-derived binary payloads and envelopes |
354
353
 
355
- ### Schema-aware serialization (rc.112)
354
+ ### Schema-aware serialization
356
355
 
357
356
  `RpcSerialization.RpcSerialization`, `RpcClient.Protocol`, and
358
357
  `RpcServer.Protocol` now require `codecFor: RpcSerialization.CodecFor`:
@@ -364,9 +363,9 @@ type CodecFor = <S extends Schema.Top>(schema: S) =>
364
363
 
365
364
  Forward the selected serialization's `codecFor` when implementing a protocol.
366
365
  This selects codecs for payloads, successes, errors, defects, and stream elements;
367
- envelope framing remains the serialization's responsibility. Existing JSON,
368
- NDJSON, JSON-RPC, and MsgPack wire formats keep their JSON-compatible schema
369
- codecs. Workers supply `Schema.toCodecJson` themselves over structured clone and
366
+ envelope framing remains the serialization's responsibility. JSON,
367
+ NDJSON, and JSON-RPC use JSON-compatible schema codecs.
368
+ Workers supply `Schema.toCodecJson` themselves over structured clone and
370
369
  still need no serialization layer. Cluster network traffic follows the protocol
371
370
  codec; cluster persistence continues to use JSON.
372
371
 
@@ -374,17 +373,16 @@ codec; cluster persistence continues to use JSON.
374
373
  both peers. The default maximum frame size is 16 MiB. Envelopes use fingerprints
375
374
  and a connection-local string dictionary. Payload fingerprints default to `false`
376
375
  to allow compatible schema evolution; enable them for strict layout agreement.
377
- This is an Effect RPC format, not generic MsgPack or JSON-RPC interoperability.
376
+ This is an Effect RPC format; use JSON-RPC layers for JSON-RPC interoperability.
378
377
  See `effect-schema-composition` for binary layout/ownership constraints.
379
378
 
380
379
  Rules, verified against the protocol implementations and the e2e matrix:
381
380
 
382
- - **Raw TCP sockets need a framed format** (`ndjson`, `ndJsonRpc`, `msgPack`). Plain `json` cannot split the byte stream — decoding breaks as soon as two messages share a chunk.
383
- - **WebSocket frames messages itself**, so *any* format works there — the e2e suite runs ws with json, ndjson, msgpack, and jsonRpc.
384
- - **HTTP POST works with both**, with different response behavior: with an **unframed** format the server buffers all responses and returns one JSON array when every request finishes (a streaming rpc arrives as one big batch at the end); with a **framed** format the server returns a chunked streaming response and chunks arrive incrementally. Use `layerNdjson` (or msgpack) over HTTP if you serve streaming rpcs.
381
+ - **Raw TCP sockets need a framed format** (NDJSON, newline JSON-RPC, or SchemaBinary). Plain JSON cannot split the byte stream.
382
+ - **WebSocket frames messages itself**, so it also supports unframed serialization.
383
+ - **HTTP POST works with both**, with different response behavior: an **unframed** format buffers responses until all requests finish; a **framed** format emits chunks incrementally. Use `layerNdjson` or `layerSchemaBinary()` for streaming RPCs.
385
384
  - The response `content-type` is the serialization's `contentType`.
386
385
  - `RpcSerialization.layerJsonRpc()` / `layerNdJsonRpc()` speak JSON-RPC 2.0: rpc tags map to `method`, batched arrays are preserved, and internal signals travel as `@effect/rpc/Ack`-style methods. Use for interop with non-Effect JSON-RPC clients.
387
- - `RpcSerialization.makeMsgPack(options)` customizes msgpackr (`useRecords`, `useFloat32`, ...); wrap with `Layer.succeed(RpcSerialization.RpcSerialization)(RpcSerialization.makeMsgPack({ ... }))`.
388
386
 
389
387
  Client and server must use the **same** serialization.
390
388
 
@@ -778,7 +776,7 @@ const ServerLayer = RpcServer.layer(StoreRpcs, { concurrency: 1 }).pipe(
778
776
  1. **Importing from `@effect/rpc`.** v3 habit; the package does not exist in v4. Everything is `effect/unstable/rpc` (and platform layers come from `@effect/platform-node` / `-bun` / `-browser`).
779
777
  2. **Forgetting `protocol: 'http'` on `layerHttp`.** The default is `'websocket'` — your `POST /rpc` curl returns 404 and only a `GET` upgrade route exists. Also note `layerHttp` takes `{ group, path, ... }` as one options bag, while `layer(group, options)` takes the group positionally.
780
778
  3. **Providing handlers/middleware but no `Protocol` or `RpcSerialization`.** `RpcServer.layer` requires all of: handler layer(s), middleware implementation layers, a `layerProtocol*`, and (for non-worker protocols) a `RpcSerialization.layer*`. Missing ones surface as unresolved layer requirements.
781
- 4. **`layerJson` on a raw TCP socket server.** No framing — decode breaks when messages span chunks. Sockets need `layerNdjson`, `layerNdJsonRpc()`, or `layerMsgPack`. (WebSocket is fine with `layerJson` — ws frames messages itself.)
779
+ 4. **`layerJson` on a raw TCP socket server.** Sockets need `layerNdjson`, `layerNdJsonRpc()`, or `layerSchemaBinary()` for framing. WebSocket supplies its own framing and supports `layerJson`.
782
780
  5. **Streaming rpcs over `layerProtocolHttp` + `layerJson` and wondering why chunks arrive all at once.** Unframed HTTP buffers the whole response until every request in the call finishes. Framed HTTP streams incrementally with a bounded response queue (`streamBufferSize`, default 16). HTTP has no RPC acknowledgment protocol, but the bounded queue still backpressures producers; full-duplex transports additionally support RPC acks.
783
781
  6. **Leaving `disableFatalDefects: false` in production.** One handler `die` then nukes every in-flight request on that connection with a connection-level defect. Set `true` to confine defects to the failing request.
784
782
  7. **Assuming `concurrency` is per-client.** It is one semaphore per server instance shared by all clients. Use `Rpc.fork` to exempt cheap read handlers instead of raising the global limit.
@@ -11,6 +11,13 @@ Verify APIs against `~/.local/share/opencode/repos/github.com/Effect-TS/effect@m
11
11
 
12
12
  ## Semantics
13
13
 
14
+ `Effect.timeoutOrElse` finishes interrupting the source before evaluating the
15
+ fallback. The fallback runs in the caller fiber with its interruptibility and
16
+ supervision. `Effect.repeatOrElse` receives the previous step's `Schedule.Metadata`
17
+ in its fallback. Custom `RateLimiterStore.tokenBucket` implementations return
18
+ `[remaining, elapsedMillis]` atomically; fabricating zero elapsed time produces
19
+ incorrect delay, retryAfter, and resetAfter values.
20
+
14
21
  - `Effect.retry` reruns typed failures. It does not retry defects or interruption.
15
22
  - `Effect.repeat` reruns successes. A typed failure stops repetition unless the pass handles it first.
16
23
  - The source effect runs once before the schedule is stepped.
@@ -465,7 +465,7 @@ function split(separator: string) {
465
465
 
466
466
  ## Custom Transformations
467
467
 
468
- ### Schema-derived binary boundaries (rc.112)
468
+ ### Schema-derived binary boundaries
469
469
 
470
470
  Use `SchemaBinary.toCodec(schema)` from `effect/unstable/encoding` for a compact
471
471
  `Uint8Array` representation. It derives the wire layout from the schema's
@@ -535,9 +535,9 @@ const BooleanFromString = Schema.Literals(['on', 'off']).pipe(
535
535
  );
536
536
  ```
537
537
 
538
- ### SchemaTransformation.transformOrFail — Transformations That Can Fail
538
+ ### SchemaTransformation.transformEffect — Transformations That Can Fail
539
539
 
540
- Use `SchemaTransformation.transformOrFail` when transformation might fail:
540
+ Use `SchemaTransformation.transformEffect` when transformation might fail:
541
541
 
542
542
  ```typescript
543
543
  import {
@@ -551,7 +551,7 @@ import {
551
551
 
552
552
  const NumberFromString = Schema.String.pipe(
553
553
  Schema.decodeTo(Schema.Number, {
554
- decode: SchemaGetter.transformOrFail((s) =>
554
+ decode: SchemaGetter.transformEffect((s) =>
555
555
  Option.match(Number.parse(s), {
556
556
  onNone: () =>
557
557
  Effect.fail(
@@ -633,6 +633,14 @@ export const beGreaterThan =
633
633
 
634
634
  ### Constructor vs Boundary Decoder
635
635
 
636
+ `make`, `makeOption`, and `makeEffect` return an existing class
637
+ instance unchanged. Use `new` when a distinct instance is required. Pass parsing
638
+ options at the adapter call, not in annotations; options apply operation-wide.
639
+ Products support parsing concurrency but union alternatives stay sequential.
640
+ Retain extra object fields with Record/StructWithRest; excess-property modes are
641
+ `ignore` or `error`. See `effect-schema-v4` for inherited-field, template-literal,
642
+ JSON Schema, Getter composition, and optional JIT/AOT contracts.
643
+
636
644
  Keep decoded shapes schema-first with `Schema.Class`. Choose construction and decoding APIs by input trust and failure semantics:
637
645
 
638
646
  | API | Use Case | Failure |
@@ -20,7 +20,7 @@ Reference this for:
20
20
 
21
21
  ## 1. Key Renames (find-and-replace safe)
22
22
 
23
- ### Current type model (rc.112)
23
+ ### Type model
24
24
 
25
25
  `Schema.Schema<T>` describes only the decoded type. Preserve wire types and
26
26
  services with `Schema.Codec<T, E, RD, RE>`: decoded Type, Encoded representation,
@@ -35,13 +35,13 @@ schema helpers so concrete schema operations are retained.
35
35
  | `typeSchema(schema)` | `toType(schema)` | |
36
36
  | `asSchema(schema)` | `revealCodec(schema)` | |
37
37
  | `equivalence()` | `toEquivalence()` | |
38
- | `arbitrary()` | `toArbitrary()` | Returns a factory that accepts the `fast-check` module |
38
+ | `arbitrary()` | `Arbitrary.schema(schema)` | Native `effect/unstable/arbitrary`; fast-check bridge removed |
39
39
  | `pretty()` | `toFormatter()` | |
40
- | `parseJson()` | `fromJsonString(Schema.Unknown)` | `UnknownFromJsonString` is internal as of beta.103 |
40
+ | `parseJson()` | `fromJsonString(Schema.Unknown)` | Public unknown-JSON codec |
41
41
  | `parseJson(schema)` | `fromJsonString(schema)` | With-schema version |
42
- | `TaggedErrorClass` | `TaggedError` | Renamed in beta.104 |
43
- | `ErrorClass` | `Error` | Renamed in beta.104 |
44
- | `Error` (instance schema) | `ErrorInstance` | Renamed in beta.104 |
42
+ | `TaggedErrorClass` | `TaggedError` | Tagged error constructor |
43
+ | `ErrorClass` | `Error` | Error constructor |
44
+ | `Error` (instance schema) | `ErrorInstance` | Error instance schema |
45
45
  | `BigIntFromSelf` | `BigInt` | |
46
46
  | `SymbolFromSelf` | `Symbol` | |
47
47
  | `URLFromSelf` | `URL` | |
@@ -66,7 +66,7 @@ schema helpers so concrete schema operations are retained.
66
66
  | `standardSchemaV1` | `toStandardSchemaV1` | |
67
67
  | `nonEmptyString` | `isNonEmpty()` | Now used with `.check()` |
68
68
  | `disableValidation` | `disableChecks` | In `MakeOptions` for Class constructors |
69
- | standalone `SchemaError` module | `Schema.SchemaError` | The root `SchemaError` namespace export was removed in rc.108; use `Schema.isSchemaError` to narrow |
69
+ | standalone `SchemaError` module | `Schema.SchemaError` | Use `Schema.isSchemaError` to narrow |
70
70
 
71
71
  ### Parser/Codec Function Renames
72
72
 
@@ -161,7 +161,7 @@ const isNegative = Schema.isLessThan(0);
161
161
  const isNonPositive = Schema.isLessThanOrEqualTo(0);
162
162
  ```
163
163
 
164
- For the common non-negative safe-integer domain, use the canonical `Schema.Natural` added in beta.102 instead of composing checks manually.
164
+ For the non-negative safe-integer domain, use `Schema.Natural`.
165
165
 
166
166
  ### Custom Filters
167
167
 
@@ -252,7 +252,7 @@ import {
252
252
 
253
253
  const NumberFromString = Schema.String.pipe(
254
254
  Schema.decodeTo(Schema.Number, {
255
- decode: SchemaGetter.transformOrFail((s) =>
255
+ decode: SchemaGetter.transformEffect((s) =>
256
256
  Option.match(Number.parse(s), {
257
257
  onNone: () =>
258
258
  Effect.fail(
@@ -386,8 +386,8 @@ const fallback = SchemaGetter.withDefault(Effect.succeed('viewer'));
386
386
  - `Schema.resolveInto` was renamed to `Schema.resolveAnnotations`.
387
387
  - `Schema.resolveAnnotationsKey(schema)` returns key-level annotations.
388
388
  - `Schema.annotateEncoded({...})` annotates the encoded side of a transformed schema; use `Schema.annotate({...})` for the decoded Type side.
389
- - Schemas are directly extendable as classes; `Schema.asClass` was removed in beta.102.
390
- - `Schema.toArbitrary(schema)` returns a factory that must be called with the `fast-check` module; `Schema.toArbitraryLazy` and arbitrary derivation reports were removed in beta.106.
389
+ - Schemas are directly extendable as classes.
390
+ - Derive native generators with `Arbitrary.schema(schema)` from `effect/unstable/arbitrary`. See `effect-testing` for sampling, bounded generation, shrinking, and replay.
391
391
  - New built-in schemas:
392
392
  - `Schema.DateFromString`
393
393
  - `Schema.BigIntFromString`
@@ -404,7 +404,7 @@ const fallback = SchemaGetter.withDefault(Effect.succeed('viewer'));
404
404
 
405
405
  ```ts
406
406
  import { Effect, Schema } from 'effect';
407
- import * as FastCheck from 'fast-check';
407
+ import { Arbitrary } from 'effect/unstable/arbitrary';
408
408
 
409
409
  class UserName extends Schema.NonEmptyString {
410
410
  static readonly decodeUnknownSync = Schema.decodeUnknownSync(this);
@@ -428,8 +428,7 @@ const duration = Schema.DurationFromString;
428
428
 
429
429
  const parsed = Schema.String.makeEffect('alice');
430
430
 
431
- const makeNameArbitrary = Schema.toArbitrary(Schema.NonEmptyString);
432
- const nameArbitrary = makeNameArbitrary(FastCheck);
431
+ const nameArbitrary = Arbitrary.schema(Schema.NonEmptyString);
433
432
  ```
434
433
 
435
434
  ### Graph Schemas
@@ -493,12 +492,12 @@ The input is `{ ast, occurrences, identifier }`; return a name to extract that c
493
492
 
494
493
  The standalone `SchemaError` root module was removed. Parser adapters such as `Schema.decodeUnknownEffect` fail with `Schema.SchemaError`, which contains the structured `issue`; narrow unknown failures with `Schema.isSchemaError`. By contrast, schema/class `makeEffect` and constructor defaults fail directly with `SchemaIssue.Issue`.
495
494
 
496
- In rc.112, `SchemaError` skips stack-frame capture for lower construction cost.
495
+ `SchemaError` skips stack-frame capture for lower construction cost.
497
496
  Use its `issue` and formatter/message for validation diagnostics; a captured
498
497
  parser-error stack is not a diagnostic contract. Synchronous parser optimizations
499
498
  do not change the choice between constructors, sync decoders, and Effect decoders.
500
499
 
501
- ### Partial tagged-union matching (rc.112)
500
+ ### Partial tagged-union matching
502
501
 
503
502
  `Schema.TaggedUnion(...)` and `Schema.Union([...]).pipe(Schema.toTaggedUnion(tag))`
504
503
  now expose `matchOrElse(value, cases, fallback)` and its curried
@@ -508,7 +507,7 @@ one behavior. The `toTaggedUnion` helper narrows the fallback to omitted variant
508
507
  the direct `TaggedUnion` overload types its fallback as the full union.
509
508
  Neither matcher decodes unknown input. See `effect-pattern-matching` for a checked example.
510
509
 
511
- ### JSON Schema import, conversion, and Standard Schema (rc.112)
510
+ ### JSON Schema import, conversion, and Standard Schema
512
511
 
513
512
  - `SchemaRepresentation.fromJsonSchemaDocument` rejects unsupported references,
514
513
  validation keywords, object/array `const` or `enum` values, and intersections
@@ -527,6 +526,50 @@ Neither matcher decodes unknown input. See `effect-pattern-matching` for a check
527
526
  - Binary encoding is available from `effect/unstable/encoding` as `SchemaBinary`.
528
527
  See `effect-schema-composition` for codecs and `effect-stream` for framing.
529
528
 
529
+ ## Parsing and compilation contracts
530
+
531
+ - Pass parse options to decoder, encoder, or constructor adapters. `parseOptions`
532
+ annotations no longer affect parsing, and operation options apply throughout
533
+ the complete parse. `propertyOrder` is removed; order presentation explicitly.
534
+ - `onExcessProperty` supports `"ignore"` and `"error"`. Model retained extra fields
535
+ with `Schema.Record` or `Schema.StructWithRest`, rather than unvalidated preserve.
536
+ - Parsing concurrency applies to product children (arrays, tuples, fields, record
537
+ entries), defaults to sequential, and applies independently at each nesting.
538
+ Union candidates remain sequential. Concurrent transformed record-key collisions
539
+ retain the value that finishes last.
540
+ - Declared fields may be inherited and are copied to own output properties;
541
+ dynamic record keys remain own-only and `__proto__` remains own-only. Enforce
542
+ ownership at the boundary when the protocol requires own declared fields.
543
+ - Class `make`, `makeOption`, and `makeEffect` preserve existing instances. Use
544
+ `new MyClass(fields)` for a distinct instance. Class equivalence now compares
545
+ declared fields, excluding unrelated runtime properties.
546
+ - `TemplateLiteral` rejects encoded/transformed parts, even in nested templates
547
+ or unions. Use `TemplateLiteralParser` for transformed tuple parts and provide
548
+ their decoding/encoding services. Its encoded projection validates the template.
549
+ - JSON Schema generation uses `onExcessProperty`, replacing its former
550
+ `additionalProperties` option; generated objects are open by default. Schema-valued
551
+ extra properties belong in Record/StructWithRest. `Enum` rejects non-finite
552
+ members; `isMultipleOf` rejects zero/non-finite divisors and normalizes negatives.
553
+ - Built-in revivers live in `SchemaRepresentation`; constructors are
554
+ `makeReviverDeclaration`, `makeReviverFilter`, and `makeReviverFilterGroup`.
555
+ `toEncoderXml` fails directly with `SchemaIssue.Issue`.
556
+ - For tooling, use `SchemaAST.AST` and named instance interfaces rather than
557
+ `SchemaAST.Base` or constructor prototype types. Union mode is
558
+ `ast.options?.mode` (default `anyOf`); migrate persisted representation documents.
559
+ `SchemaAST.Context.constructorDefault` holds an Effect directly, not a Link.
560
+ - JSON Schema imports support `{ not: {} }` and closed single-pattern records
561
+ with `patterns: "apply"`. Open patterned objects and references beneath a nested
562
+ `$id` are rejected; flatten references or explicitly choose to ignore constraints.
563
+
564
+ Experimental JIT/AOT compilation uses the existing `SchemaParser` APIs. Opt in
565
+ globally with `effect/unstable/schema/SchemaJITCompiler/enable` or selectively with
566
+ `SchemaJITCompiler.enable(ast)`. AOT's `SchemaAOTCompiler/Build` discovers direct
567
+ schema exports from explicit loaders and writes a self-installing module via
568
+ FileSystem/Path. Choose prepared operations explicitly: omitted operations use
569
+ the interpreter, and detailed effectful parsing remains the fallback. Keep the
570
+ interpreter as the default unless measured performance warrants compilation;
571
+ use AOT where dynamic function construction is unavailable.
572
+
530
573
  ## 8. New Modules
531
574
 
532
575
  ### SchemaTransformation
@@ -563,10 +606,15 @@ const DurationFromString = Schema.String.pipe(
563
606
 
564
607
  ### SchemaGetter
565
608
 
566
- Single-direction transform primitives. A `Getter<T, E, R>` is `Option<E> → Effect<Option<T>, Issue, R>`. Key exports:
609
+ Single-direction transform primitives. `Getter<T, E, R>` is a tagged
610
+ union of synchronous, optional, and effectful transformations. Interpret one with
611
+ `SchemaGetter.run(getter, option)` to obtain `Effect<Option<T>, Issue, R>`.
612
+ Getter values expose only `pipe`; use standalone dual `map`, `compose`, and `run`.
613
+ Key exports:
567
614
 
568
615
  - `transform(fn)` — pure map over present values
569
- - `transformOrFail(fn)` — fallible map returning `Effect`
616
+ - `transformEffect(fn)` — fallible map of a present value returning `Effect`
617
+ - `transformOptionalEffect(fn)` — effectful map that handles missing values too
570
618
  - `transformOptional(fn)` — full `Option<E> → Option<T>` control (for optional field transforms)
571
619
  - `passthrough()` — identity getter
572
620
  - `withDefault(effect)` — provide a default `Effect` for missing values
@@ -574,6 +622,13 @@ Single-direction transform primitives. A `Getter<T, E, R>` is `Option<E> → Eff
574
622
  - `checkEffect(fn)` — effectful validation
575
623
  - `String()`, `Number()`, `Boolean()`, `BigInt()`, `Date()` — coercion getters
576
624
 
625
+ Use `transformOptionalEffect` instead of constructing a Getter directly.
626
+ Use `transformEffect` / `transformOptionalEffect` instead of `onSome` / `onNone`.
627
+ `SchemaGetter.forbiddenEncoding` is the encode getter for a decode-only codec.
628
+ For transformation pairs, use `SchemaTransformation.makeTransformation` and
629
+ the standalone dual `composeTransformation(first, second)`; transformations and
630
+ middleware support `.pipe`.
631
+
577
632
  ```ts
578
633
  import { Schema, SchemaGetter } from 'effect';
579
634
 
@@ -680,7 +735,7 @@ class Cat extends Schema.TaggedClass<Cat>()('Cat', {
680
735
 
681
736
  ## 10. TaggedError
682
737
 
683
- `Schema.TaggedErrorClass` was renamed to `Schema.TaggedError` in beta.104. The constructor pattern is unchanged:
738
+ Define yieldable tagged errors with `Schema.TaggedError`:
684
739
 
685
740
  ```ts
686
741
  import { Effect, Schema } from 'effect';