opencode-effect-enforcer 0.2.6 → 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.
- package/README.md +3 -3
- package/docs/effect-4.0.0-changelog.md +3213 -0
- package/docs/effect-4.0.0.md +110 -0
- package/guidance/effect-first-development.md +15 -305
- package/guidance/progressive-disclosure-guidance.md +18 -24
- package/package.json +2 -2
- package/patterns/avoid-any.md +2 -2
- package/patterns/avoid-direct-json.md +6 -6
- package/patterns/avoid-native-fetch.md +8 -6
- package/patterns/avoid-node-imports.md +2 -2
- package/patterns/avoid-non-null-assertion.md +2 -2
- package/patterns/avoid-object-type.md +2 -2
- package/patterns/avoid-platform-coupling.md +1 -1
- package/patterns/avoid-process-env.md +3 -4
- package/patterns/avoid-ts-ignore.md +1 -1
- package/patterns/context-tag-extends.md +11 -8
- package/patterns/effect-promise-vs-trypromise.md +6 -7
- package/patterns/prefer-arr-sort.md +2 -2
- package/patterns/prefer-effect-fn.md +21 -65
- package/patterns/prefer-schema-class.md +4 -4
- package/patterns/throw-in-effect-gen.md +1 -1
- package/patterns/use-clock-service.md +4 -0
- package/patterns/use-command-executor-service.md +2 -2
- package/patterns/use-http-client-service.md +8 -6
- package/patterns/use-random-service.md +6 -7
- package/skills/effect-ai-chat/SKILL.md +13 -7
- package/skills/effect-ai-language-model/SKILL.md +50 -21
- package/skills/effect-ai-prompt/SKILL.md +25 -14
- package/skills/effect-ai-provider/SKILL.md +50 -22
- package/skills/effect-ai-streaming/SKILL.md +27 -12
- package/skills/effect-ai-tool/SKILL.md +37 -28
- package/skills/effect-atom-rpc/SKILL.md +57 -36
- package/skills/effect-atom-state/SKILL.md +57 -19
- package/skills/effect-batching/SKILL.md +5 -3
- package/skills/effect-cache/SKILL.md +19 -7
- package/skills/effect-cli/SKILL.md +17 -8
- package/skills/effect-command-executor/SKILL.md +115 -64
- package/skills/effect-concurrency-testing/SKILL.md +26 -6
- package/skills/effect-config/SKILL.md +53 -2
- package/skills/effect-context-witness/SKILL.md +6 -6
- package/skills/effect-domain-modeling/SKILL.md +8 -1
- package/skills/effect-error-handling/SKILL.md +15 -2
- package/skills/effect-fiber/SKILL.md +20 -25
- package/skills/effect-filesystem/SKILL.md +69 -57
- package/skills/effect-http-api/SKILL.md +72 -22
- package/skills/effect-http-client/SKILL.md +25 -21
- package/skills/effect-http-server/SKILL.md +51 -21
- package/skills/effect-incremental-migration/SKILL.md +17 -8
- package/skills/effect-layer-design/SKILL.md +8 -0
- package/skills/effect-managed-runtime/SKILL.md +6 -0
- package/skills/effect-mcp-server/SKILL.md +64 -24
- package/skills/effect-observability/SKILL.md +61 -15
- package/skills/effect-parallelization/SKILL.md +24 -7
- package/skills/effect-path/SKILL.md +8 -2
- package/skills/effect-platform-abstraction/SKILL.md +88 -66
- package/skills/effect-platform-layers/SKILL.md +68 -67
- package/skills/effect-pubsub-event-bus/SKILL.md +56 -60
- package/skills/effect-react-composition/SKILL.md +19 -6
- package/skills/effect-rpc-api/SKILL.md +24 -24
- package/skills/effect-rpc-client/SKILL.md +33 -28
- package/skills/effect-rpc-cluster/SKILL.md +122 -78
- package/skills/effect-rpc-server/SKILL.md +56 -20
- package/skills/effect-scheduling/SKILL.md +29 -1
- package/skills/effect-schema-composition/SKILL.md +31 -13
- package/skills/effect-schema-v4/SKILL.md +94 -10
- package/skills/effect-scope/SKILL.md +13 -5
- package/skills/effect-service-implementation/SKILL.md +1 -1
- package/skills/effect-socket/SKILL.md +52 -8
- package/skills/effect-sql/SKILL.md +67 -33
- package/skills/effect-stream/SKILL.md +50 -5
- package/skills/effect-testing/SKILL.md +91 -2
- package/skills/effect-workflow/SKILL.md +76 -39
- package/src/guidance.ts +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Effect 4.0.0-rc.116 → 4.0.0
|
|
2
|
+
|
|
3
|
+
## Provenance and release scope
|
|
4
|
+
|
|
5
|
+
- Previous dependency: `effect@4.0.0-rc.116`.
|
|
6
|
+
- Target: **`effect@4.0.0`, the first stable v4 release** and npm's `latest`
|
|
7
|
+
dist-tag, checked on 2026-10-02. The `rc` tag remains rc.118; it is not the target.
|
|
8
|
+
- npm publication: 2026-10-01 at 03:11:28 UTC. The GitHub release is explicitly
|
|
9
|
+
non-prerelease, published at 01:47:13 UTC. The release commit is dated September 30.
|
|
10
|
+
- Exact source: [`effect@4.0.0`](https://github.com/Effect-TS/effect/tree/effect%404.0.0),
|
|
11
|
+
commit `67ba4e46a11ccda0b6761578bfd22c04ae00167d`.
|
|
12
|
+
- [Official release](https://github.com/Effect-TS/effect/releases/tag/effect%404.0.0).
|
|
13
|
+
- [Complete source comparison](https://github.com/Effect-TS/effect/compare/effect%404.0.0-rc.116...effect%404.0.0).
|
|
14
|
+
- [Full upstream release notes](effect-4.0.0-changelog.md): **93 complete release
|
|
15
|
+
sections across 31 packages**, including dependency-only entries and the stable
|
|
16
|
+
release overview. All rc.117, rc.118, and 4.0.0 sections are included; rc.116 is
|
|
17
|
+
excluded. Only trailing whitespace is normalized.
|
|
18
|
+
- Previous audit: [rc.112 → rc.116](effect-4.0.0-rc.116.md).
|
|
19
|
+
|
|
20
|
+
The moving source reference was already ahead of the release. Contracts were
|
|
21
|
+
checked against the exact tag rather than importing subsequent main-branch fixes.
|
|
22
|
+
The stable release overview describes some features introduced before rc.116;
|
|
23
|
+
the individual entries and source comparison identify this migration's changes.
|
|
24
|
+
|
|
25
|
+
## Alignment decisions
|
|
26
|
+
|
|
27
|
+
| Area | Alignment |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| Packaging | Pin the dependency and lockfile to 4.0.0. Remove the `unstable` import segment throughout live examples, module augmentations, source references, and test fixtures. Use `effect/http-api`, root `Arbitrary`, and format-specific binary-text encoding modules. |
|
|
30
|
+
| Stability | Stable package publication does not stabilize every API. `@stability unstable` APIs may break in minor releases; unmarked APIs follow semver. Directly consumed Effect-family packages use the same version. |
|
|
31
|
+
| Schema | Rename range/string checks; document Unicode units and JSON Schema approximations, persisted check IDs, single/type-only brands, representation round trips, and current issue constructors. |
|
|
32
|
+
| Configuration and CLI | Document dependent configuration, whole-group defaults, fallback-result semantics, and negative-number lexing. |
|
|
33
|
+
| Lifetimes and concurrency | Align partition ordering, closeable scopes, cache waiter ownership, zero-TTL preloading, runtime disposal, queue terminal batches, PubSub completion, races, and bounded repetition. |
|
|
34
|
+
| HTTP and RPC | Isolate entrypoint routers, place routes/protocols in the served app, share stateful services at the parent boundary, expose slot-specific parsing, preserve WebSocket exits, and distinguish missed pongs from connection-open retries. |
|
|
35
|
+
| SQL and workflow | Review savepoint release, failed COMMIT behavior, PostgreSQL session retirement and row codecs, workflow execution-ID derivation, reset retries, and conditional reply clearing. |
|
|
36
|
+
| AI and MCP | Align Toolkit service/error requirements, Decision probability options, streaming deltas, provider compatibility, object-root schemas, and version-specific MCP results/errors. |
|
|
37
|
+
| Reactive state and telemetry | Review registry ownership, SWR refresh timing, concurrent atom calls, batching, SSR hydration, and OTLP response draining/flush lifetimes. |
|
|
38
|
+
| Platform examples | Correct stale byte-size, error, scoped-resource, service-wiring, and process-output examples while retaining the platform abstraction boundaries. |
|
|
39
|
+
| Pattern feedback | Update positive guidance and fixtures to current APIs. Correct construction, decoding, randomness, ordering, defect-handling, service-definition, and HTTP retry examples. Keep the existing detector inventory. |
|
|
40
|
+
|
|
41
|
+
## Complete audit inventory
|
|
42
|
+
|
|
43
|
+
Five independently assigned audits covered all **53 skill directories (54 Markdown
|
|
44
|
+
files)**. The integration review covered all **45 pattern definitions**, all
|
|
45
|
+
**four guidance documents**, the runtime's Effect references, dependency wiring,
|
|
46
|
+
and detector fixtures. The historical essays remain conceptual material; current
|
|
47
|
+
baseline and API policy live in the two active guidance documents.
|
|
48
|
+
|
|
49
|
+
The following table accounts for every skill directory (`effect-` prefix omitted).
|
|
50
|
+
Files with unchanged contracts were reviewed without cosmetic edits.
|
|
51
|
+
|
|
52
|
+
| Skill directories | Review focus |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| schema-v4, schema-composition, domain-modeling, domain-predicates, pattern-matching, optics, typeclass-design, graph | Schema contracts, brands, predicates, transformations, representations, orders, and unchanged graph/optic APIs. |
|
|
55
|
+
| config, cli, testing | Config source/fallback behavior, argument lexing, native Arbitrary, Vitest fixtures, and effectful TestSchema assertions. |
|
|
56
|
+
| http-api, http-client, http-server, rpc-api, rpc-client, rpc-server, rpc-cluster, sql, workflow, socket | Router ownership, transport/protocol contracts, parsing, network address representation, transaction/session safety, and durable state compatibility. |
|
|
57
|
+
| ai-chat, ai-language-model, ai-prompt, ai-provider, ai-streaming, ai-tool, mcp-server | Model/tool requirements, provider translations, response histories, streaming, and MCP protocol versions. |
|
|
58
|
+
| atom-rpc, atom-state, react-composition, observability, wide-events | Reactive lifecycle, React peers, registry/hydration, exporters, and wide-event guidance including its accompanying article. |
|
|
59
|
+
| cache, batching, layer-design, scope, service-implementation, context-witness, managed-runtime, incremental-migration | Acquisition/interruption, memoization, dependency capture, runtime ownership, and boundary integration. |
|
|
60
|
+
| fiber, parallelization, concurrency-testing, pubsub-event-bus, stream, scheduling, error-handling | Partition order, cancellation/cleanup, queue completion, scheduling, typed failures, and defects. |
|
|
61
|
+
| filesystem, path, platform-abstraction, platform-layers, command-executor | File and process services, portable paths, errors, byte sizes, test doubles, layer precedence, and output draining. |
|
|
62
|
+
|
|
63
|
+
## Compatibility considerations
|
|
64
|
+
|
|
65
|
+
- This plugin directly depends only on `effect` from the Effect family. Companion
|
|
66
|
+
libraries in examples are reviewed against 4.0.0 source; they are not added as
|
|
67
|
+
plugin runtime dependencies.
|
|
68
|
+
- TypeScript 5.9 or newer is required. The repository already satisfies that
|
|
69
|
+
minimum. `@effect/vitest` and `@effect/doctest` require Vitest 5; this repository
|
|
70
|
+
uses plain Vitest without those adapters, so its runner need not change.
|
|
71
|
+
- `@effect/atom-react` requires React 19. Deno adapters require Deno 2.8.3 or newer.
|
|
72
|
+
- Removed module paths are not aliases. Update module augmentations and tooling
|
|
73
|
+
references along with source imports. HTTP API runtime identities and its
|
|
74
|
+
reserved streaming failure event use `http-api` too.
|
|
75
|
+
- Schema check IDs in persisted representations changed with the check names;
|
|
76
|
+
type-only brands do not survive representation serialization. Reapply nominal
|
|
77
|
+
brands after rebuilding, and migrate stored check IDs where applicable.
|
|
78
|
+
- Workflow execution IDs now hash length-prefixed tags/idempotency keys. Existing
|
|
79
|
+
persisted runs and deferred tokens need an explicit compatibility decision;
|
|
80
|
+
unchanged business idempotency strings do not imply unchanged execution IDs.
|
|
81
|
+
- Hash values can change. Do not treat Effect hash output as a stable persisted
|
|
82
|
+
identifier or wire format.
|
|
83
|
+
|
|
84
|
+
## Verification scope
|
|
85
|
+
|
|
86
|
+
The documentation test checks core Effect import entrypoints and named exports
|
|
87
|
+
in every TypeScript/TSX fence across skills, patterns, and guidance. It also
|
|
88
|
+
compiles each explicitly marked, independent example with strict TypeScript
|
|
89
|
+
options against the installed stable release. Illustrative fragments may rely
|
|
90
|
+
on surrounding declarations or intentionally show unsupported code; they are
|
|
91
|
+
not all standalone programs.
|
|
92
|
+
|
|
93
|
+
Release-note completeness is checked against every package changelog at the
|
|
94
|
+
target tag. Source-diff and removed-export scans complement the semantic audits.
|
|
95
|
+
The detector tests retain bidirectional pattern inventory and README catalog
|
|
96
|
+
coverage. Companion-package examples are source-reviewed rather than tested
|
|
97
|
+
against live providers, databases, or OS transports.
|
|
98
|
+
|
|
99
|
+
Final integration verification on 2026-10-02:
|
|
100
|
+
|
|
101
|
+
- `bun run check` — passed formatting, lint, and project typechecking.
|
|
102
|
+
- `bun run test` — **912 tests passed across 53 test files**.
|
|
103
|
+
- Documentation compilation — **90 marked examples and 1,096 core Effect import
|
|
104
|
+
declarations** checked against installed `effect@4.0.0`.
|
|
105
|
+
- `git diff --check` — passed.
|
|
106
|
+
- Release-note comparison — all **93 section bodies across 31 packages** match
|
|
107
|
+
the exact tag, after trailing-whitespace normalization.
|
|
108
|
+
- Live-content scans — no removed `effect/unstable/*` or `effect/httpapi` paths,
|
|
109
|
+
rc.116 baseline declarations, or renamed Schema check calls remain. Historical
|
|
110
|
+
audits and verbatim upstream changelogs retain their original references.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
This document defines the working model behind Effect-first code using the Effect v4 ecosystem.
|
|
4
4
|
|
|
5
|
-
Bundled baseline: **Effect 4.0.0
|
|
5
|
+
Bundled baseline: **Effect 4.0.0 (stable)**. Check the consuming project's installed
|
|
6
6
|
version and inspect the matching upstream release tag before using newer APIs.
|
|
7
|
-
The release changelog and migration audit are in `docs/effect-4.0.0
|
|
7
|
+
The release changelog and migration audit are in `docs/effect-4.0.0.md`.
|
|
8
8
|
Source paths below are relative to the Effect source reference; locate symbols
|
|
9
9
|
by name rather than relying on line numbers or a particular tool name.
|
|
10
10
|
|
|
@@ -46,6 +46,8 @@ At boundaries:
|
|
|
46
46
|
|
|
47
47
|
## Laws and Conventions
|
|
48
48
|
|
|
49
|
+
Keep types sound: no `any`, type assertions, `@ts-ignore`, or non-null assertions.
|
|
50
|
+
|
|
49
51
|
### EF-1: Errors are data, not side effects
|
|
50
52
|
|
|
51
53
|
- If logic can fail, return `Effect.Effect<A, E, R>` with a typed error `E`.
|
|
@@ -103,8 +105,9 @@ const toDisplayName = (rawName: string | null | undefined) =>
|
|
|
103
105
|
|
|
104
106
|
- Unknown or external data must be decoded at the boundary.
|
|
105
107
|
- Prefer `Schema.decodeUnknownEffect` for effectful paths and `Schema.decodeUnknownSync` only where sync failure handling is explicit.
|
|
106
|
-
- Never use `JSON.parse` / `JSON.stringify`; use schema JSON codecs (`Schema.fromJsonString`, `Schema.decodeUnknown*`, `Schema.encode*`). For unknown JSON, use `Schema.fromJsonString(Schema.Unknown)`.
|
|
107
108
|
- Prefer `Schema.Class` over `Schema.Struct` for all decoded shapes — including HTTP response bodies, API payloads, and ephemeral wire formats, not just domain models. Named `Schema.Class` types enable `instanceof` discrimination (e.g., `Schema.Union([SuccessResponse, ErrorResponse])` then `if (parsed instanceof ErrorResponse)`), which is compile-time safe.
|
|
109
|
+
- Use `Schema.Class` for shapes discriminated via `instanceof` or participating in a `Schema.Union`.
|
|
110
|
+
- Prefer schema constructors over plain `type` / `interface` for property-based domain shapes; derive types from schemas rather than maintaining parallel models. Keep plain types for shapes schema cannot represent cleanly, such as complex type-level transforms, utility types, and overload-only surfaces.
|
|
108
111
|
- Do not name schemas with a `Schema` suffix; schema constants should be named after the domain type.
|
|
109
112
|
- For non-class schemas, export type aliases with the same identifier name as the schema value.
|
|
110
113
|
|
|
@@ -138,11 +141,13 @@ export const decodeCreateTaskInput =
|
|
|
138
141
|
- `import * as Eq from "effect/Equal"`
|
|
139
142
|
- `import * as Bool from "effect/Boolean"`
|
|
140
143
|
- Reserve root imports from `"effect"` for core combinators/types such as `Effect`, `Match`, `pipe`, and `flow`.
|
|
141
|
-
- Keep unstable
|
|
144
|
+
- Import area modules from `effect/<area>` (for example `effect/http-api` and `effect/ai`). Keep APIs marked `@stability unstable` deliberate and local; they may break in minor releases. Use the same release version for `effect` and all directly used `@effect/*` packages.
|
|
142
145
|
|
|
143
146
|
### EF-5: Effect modules over native collection helpers
|
|
144
147
|
|
|
145
148
|
- Use `Arr`, `R`, `Str`, `Eq`, `HashMap`, `HashSet`, `MutableHashMap`, `MutableHashSet`.
|
|
149
|
+
- Sort with `Arr.sort(values, order)` and explicit `effect/Order` values (`Order.String`, `Order.Number`, `Order.mapInput`, etc.), never native array `.sort()`.
|
|
150
|
+
- Partition/separate tuples put successes or predicate-passing values first: `[successes, failures]` / `[passes, fails]`. This applies to Array, Chunk, Effect, Record, Option's `partitionMap`, and Stream partitioning; do not assume a failures-first tuple.
|
|
146
151
|
- Avoid domain usage of native `Object`, `Map`, `Set`, `Date`, and direct native string helpers.
|
|
147
152
|
- Do not use imperative `for` / `for...of` loops in domain code. Use `Arr.map`, `Arr.filter`, `Arr.filterMap`, or `Arr.reduce` for pure transformations. For effectful iteration, use `Effect.forEach` (which also supports concurrency).
|
|
148
153
|
- When behavior is unchanged, prefer the tersest helper form: direct helper refs over trivial wrapper lambdas, `flow(...)` over passthrough `pipe(...)` callbacks, and shared thunk helpers when already in scope.
|
|
@@ -206,7 +211,6 @@ const summarizeAttempts = (attempts: ReadonlyArray<number>) =>
|
|
|
206
211
|
|
|
207
212
|
### EF-8: Services use explicit tags + `Layer`
|
|
208
213
|
|
|
209
|
-
- Service identity comes from a unique string key.
|
|
210
214
|
- Honor a current Effect service-tag style already standardized by the project; otherwise default to `Context.Service`.
|
|
211
215
|
- Service constructors are explicit and layered.
|
|
212
216
|
- Dependency wiring happens in Layer composition, not hidden global state.
|
|
@@ -276,14 +280,6 @@ export const TenantHeader = Tenant.annotate({
|
|
|
276
280
|
export type Tenant = typeof Tenant.Type;
|
|
277
281
|
```
|
|
278
282
|
|
|
279
|
-
### EF-12b: Schema-first internal domain building blocks
|
|
280
|
-
|
|
281
|
-
- If an intermediate domain concept is named, reused, matched on, or structurally validated, model it as a schema first instead of an ad-hoc boolean helper.
|
|
282
|
-
- Prefer built-in schema constructors/checks such as `Schema.NonEmptyString`, `Schema.NonEmptyArray`, `Schema.TupleWithRest`, `Schema.Union`, `Schema.isPattern`, and `Schema.isIncludes` before reaching for `Schema.makeFilter`.
|
|
283
|
-
- Derive domain guards with `Schema.is(SomeSchema)`.
|
|
284
|
-
- If an internal literal domain needs type guards, use `Schema.is(Schema.Literal(...))`. For exhaustive matching over literals, use `Match`. For annotation-bearing schema values, use `Schema.Literal(...).annotate({...})`.
|
|
285
|
-
- Prefer named intermediate schemas; export and document them when reusable or when they materially clarify the module’s domain model, otherwise keep them module-local.
|
|
286
|
-
|
|
287
283
|
### EF-12c: Reusable schema checks carry metadata
|
|
288
284
|
|
|
289
285
|
- Reusable `Schema.makeFilter`, `Schema.makeFilterGroup`, and reusable built-in check blocks must include `identifier`, `title`, and `description`.
|
|
@@ -504,6 +500,7 @@ You are not done if these fail:
|
|
|
504
500
|
|
|
505
501
|
### EF-21: Runtime execution stays at the boundary
|
|
506
502
|
|
|
503
|
+
- Runtime source uses Effect `FileSystem`, `Path`, and process services instead of `node:fs`, `node:path`, or `node:child_process`.
|
|
507
504
|
- Application entrypoints and tests may execute effects with `Effect.run*`.
|
|
508
505
|
- Library and domain exports should return `Effect` values.
|
|
509
506
|
- Keep runtime execution in one place so wiring, logging, and lifecycle behavior stay auditable.
|
|
@@ -565,7 +562,7 @@ const withConnection = <A, E, R>(
|
|
|
565
562
|
- Keep retry policy close to the failing effect.
|
|
566
563
|
- Retry only proven-idempotent operations at the narrowest boundary that can classify the failure.
|
|
567
564
|
- Let exhausted failures remain visible unless the boundary has a truthful fallback.
|
|
568
|
-
- Reference: `retry` in `packages/effect/src/Effect.ts` and the
|
|
565
|
+
- Reference: `retry` in `packages/effect/src/Effect.ts` and `ai-docs/src/06_schedule/10_schedules.ts` at the installed release tag.
|
|
569
566
|
|
|
570
567
|
Example:
|
|
571
568
|
|
|
@@ -739,30 +736,6 @@ const runIsolated = program.pipe(
|
|
|
739
736
|
);
|
|
740
737
|
```
|
|
741
738
|
|
|
742
|
-
### EF-33: Schema-first development for domain models
|
|
743
|
-
|
|
744
|
-
- If a data shape is decoded from external input, will be discriminated via `instanceof`, or participates in a `Schema.Union`, define it as `Schema.Class` first — regardless of whether it is a "domain model" or an ephemeral HTTP response shape.
|
|
745
|
-
- Prefer `Schema.Class` (or another schema constructor) over plain `type` / `interface` for property-based domain shapes.
|
|
746
|
-
- Derive runtime types from schema definitions instead of duplicating parallel `type` / `interface` models.
|
|
747
|
-
- Keep plain `type` / `interface` for cases schema cannot represent cleanly (complex type-level transforms, utility types, overload-only surfaces).
|
|
748
|
-
|
|
749
|
-
Example:
|
|
750
|
-
|
|
751
|
-
```ts
|
|
752
|
-
import * as Schema from 'effect/Schema';
|
|
753
|
-
|
|
754
|
-
// Prefer schema-first over plain interfaces for domain payloads.
|
|
755
|
-
export class CreateOrderInput extends Schema.Class<CreateOrderInput>(
|
|
756
|
-
'CreateOrderInput'
|
|
757
|
-
)(
|
|
758
|
-
{
|
|
759
|
-
orderId: Schema.String,
|
|
760
|
-
customerId: Schema.String
|
|
761
|
-
},
|
|
762
|
-
{ description: 'Input payload for creating an order.' }
|
|
763
|
-
) {}
|
|
764
|
-
```
|
|
765
|
-
|
|
766
739
|
### EF-34: Schema defaults over fallback object logic
|
|
767
740
|
|
|
768
741
|
- Put defaults in schema definitions, not in handler/service fallback object literals.
|
|
@@ -800,11 +773,12 @@ export class VersionSyncOptions extends Schema.Class<VersionSyncOptions>(
|
|
|
800
773
|
|
|
801
774
|
- If a guard validates domain strings/paths/tags, define a branded schema and use `Schema.is(...)`.
|
|
802
775
|
- If a domain constraint is named, reused, matched on, or structurally validated, model it as a schema first rather than a forest of ad-hoc predicate helpers.
|
|
803
|
-
- Prefer built-in schema constructors/checks before `Schema.makeFilter`.
|
|
776
|
+
- Prefer built-in schema constructors/checks such as `Schema.NonEmptyString`, `Schema.NonEmptyArray`, `Schema.TupleWithRest`, `Schema.Union`, `Schema.isPattern`, and `Schema.isIncluding` before `Schema.makeFilter`.
|
|
804
777
|
- Keep guard intent and reusable check intent in schema annotations and check metadata.
|
|
805
778
|
- For internal literal domains, use `Schema.is(Schema.Literal(...))` for type guards, `Match` for exhaustive matching, and `Schema.Literal(...).annotate({...})` for annotated schema values.
|
|
806
|
-
- Prefer named intermediate schemas; export them
|
|
779
|
+
- Prefer named intermediate schemas; export and document them when reusable or when they materially clarify the module's domain model, otherwise keep them module-local.
|
|
807
780
|
- Propagate branded schema types through the persistence layer (e.g., ORM column types: `text().$type<AccessToken>()`) to enforce compile-time safety across the entire stack and prevent parameter-swapping bugs.
|
|
781
|
+
- `Schema.brand` is type-only and takes one concrete identifier; apply it repeatedly to compose brands. Put runtime invariants in checks. Brand identifiers are not stored in AST annotations or preserved by `SchemaRepresentation`; reapply brands after reconstruction when the TypeScript type is needed.
|
|
808
782
|
|
|
809
783
|
Example:
|
|
810
784
|
|
|
@@ -818,7 +792,7 @@ import * as Str from 'effect/String';
|
|
|
818
792
|
type TopicKind = 'plain' | 'scoped';
|
|
819
793
|
|
|
820
794
|
const ContainsScopeSeparator = Schema.String.check(
|
|
821
|
-
Schema.
|
|
795
|
+
Schema.isIncluding(':', {
|
|
822
796
|
identifier: 'ContainsScopeSeparatorCheck',
|
|
823
797
|
title: 'Contains Scope Separator',
|
|
824
798
|
description: 'A string that contains `:`.',
|
|
@@ -955,25 +929,6 @@ const NativePathToPosixPath = Schema.String.pipe(
|
|
|
955
929
|
);
|
|
956
930
|
```
|
|
957
931
|
|
|
958
|
-
### EF-38: Never use native array sort in Effect-first code
|
|
959
|
-
|
|
960
|
-
- Use `Arr.sort(values, order)` from `effect/Array`.
|
|
961
|
-
- Define ordering with `effect/Order` (`Order.String`, `Order.Number`, `Order.mapInput`, etc.).
|
|
962
|
-
- Do not call native `.sort()` directly on arrays.
|
|
963
|
-
|
|
964
|
-
Example:
|
|
965
|
-
|
|
966
|
-
```ts
|
|
967
|
-
import { Order } from 'effect';
|
|
968
|
-
import * as Arr from 'effect/Array';
|
|
969
|
-
|
|
970
|
-
const byName = Order.mapInput(
|
|
971
|
-
Order.String,
|
|
972
|
-
(item: { readonly name: string }) => item.name
|
|
973
|
-
);
|
|
974
|
-
const sorted = Arr.sort(items, byName);
|
|
975
|
-
```
|
|
976
|
-
|
|
977
932
|
### EF-39: Avoid ad-hoc `String(...)` coercion for domain comparisons
|
|
978
933
|
|
|
979
934
|
- When unknown/scalar data must normalize to domain strings, model the conversion with schema transformations.
|
|
@@ -1022,248 +977,3 @@ const [cachedConfig, invalidate] =
|
|
|
1022
977
|
);
|
|
1023
978
|
// Later: yield* invalidate to force reload on next access
|
|
1024
979
|
```
|
|
1025
|
-
|
|
1026
|
-
## Copy-Paste Templates
|
|
1027
|
-
|
|
1028
|
-
### Template: Tagged error
|
|
1029
|
-
|
|
1030
|
-
```ts
|
|
1031
|
-
import * as Schema from 'effect/Schema';
|
|
1032
|
-
|
|
1033
|
-
class DomainError extends Schema.TaggedError<DomainError>()(
|
|
1034
|
-
'DomainError',
|
|
1035
|
-
{
|
|
1036
|
-
message: Schema.String
|
|
1037
|
-
},
|
|
1038
|
-
{ description: 'Domain failure' }
|
|
1039
|
-
) {}
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
### Template: Safe nullable boundary conversion
|
|
1043
|
-
|
|
1044
|
-
```ts
|
|
1045
|
-
import { pipe } from 'effect';
|
|
1046
|
-
import * as Option from 'effect/Option';
|
|
1047
|
-
|
|
1048
|
-
const fromNullableName = (name: string | null | undefined) =>
|
|
1049
|
-
pipe(
|
|
1050
|
-
Option.fromNullishOr(name),
|
|
1051
|
-
Option.filter((value) => value.length > 0)
|
|
1052
|
-
);
|
|
1053
|
-
```
|
|
1054
|
-
|
|
1055
|
-
### Template: Decode unknown at API edge
|
|
1056
|
-
|
|
1057
|
-
```ts
|
|
1058
|
-
import * as Schema from 'effect/Schema';
|
|
1059
|
-
|
|
1060
|
-
export class Payload extends Schema.Class<Payload>('Payload')({
|
|
1061
|
-
query: Schema.String
|
|
1062
|
-
}) {}
|
|
1063
|
-
|
|
1064
|
-
const decodePayload = Schema.decodeUnknownEffect(Payload);
|
|
1065
|
-
```
|
|
1066
|
-
|
|
1067
|
-
### Template: Schema naming + type alias (no `Schema` suffix)
|
|
1068
|
-
|
|
1069
|
-
```ts
|
|
1070
|
-
import * as Schema from 'effect/Schema';
|
|
1071
|
-
|
|
1072
|
-
export const OrderId = Schema.String;
|
|
1073
|
-
export type OrderId = typeof OrderId.Type;
|
|
1074
|
-
```
|
|
1075
|
-
|
|
1076
|
-
### Template: Schema-first replacement for interface
|
|
1077
|
-
|
|
1078
|
-
```ts
|
|
1079
|
-
import * as Schema from 'effect/Schema';
|
|
1080
|
-
|
|
1081
|
-
export class UserProfile extends Schema.Class<UserProfile>('UserProfile')(
|
|
1082
|
-
{
|
|
1083
|
-
id: Schema.String,
|
|
1084
|
-
displayName: Schema.String
|
|
1085
|
-
},
|
|
1086
|
-
{ description: 'User profile model used in domain workflows.' }
|
|
1087
|
-
) {}
|
|
1088
|
-
```
|
|
1089
|
-
|
|
1090
|
-
### Template: Match over switch
|
|
1091
|
-
|
|
1092
|
-
```ts
|
|
1093
|
-
import { Match } from 'effect';
|
|
1094
|
-
import * as Arr from 'effect/Array';
|
|
1095
|
-
|
|
1096
|
-
type Phase = 'draft' | 'running' | 'done';
|
|
1097
|
-
|
|
1098
|
-
const phaseLabel = (phase: Phase) =>
|
|
1099
|
-
Match.value(phase).pipe(
|
|
1100
|
-
Match.when('draft', () => 'draft'),
|
|
1101
|
-
Match.when('running', () => 'running'),
|
|
1102
|
-
Match.when('done', () => 'done'),
|
|
1103
|
-
Match.exhaustive
|
|
1104
|
-
);
|
|
1105
|
-
|
|
1106
|
-
const summarize = (items: ReadonlyArray<string>) =>
|
|
1107
|
-
Arr.match(items, {
|
|
1108
|
-
onEmpty: () => 'none',
|
|
1109
|
-
onNonEmpty: (values) => `count:${Arr.length(values)}`
|
|
1110
|
-
});
|
|
1111
|
-
```
|
|
1112
|
-
|
|
1113
|
-
### Template: Effect-returning function constructor
|
|
1114
|
-
|
|
1115
|
-
```ts
|
|
1116
|
-
import { Effect } from 'effect';
|
|
1117
|
-
|
|
1118
|
-
export const runTask = Effect.fn('Task.run')(function* (taskId: string) {
|
|
1119
|
-
yield* Effect.logInfo('run task', taskId);
|
|
1120
|
-
return taskId;
|
|
1121
|
-
});
|
|
1122
|
-
```
|
|
1123
|
-
|
|
1124
|
-
### Template: Option schema from nullish/optional
|
|
1125
|
-
|
|
1126
|
-
```ts
|
|
1127
|
-
import * as Schema from 'effect/Schema';
|
|
1128
|
-
|
|
1129
|
-
export class Input extends Schema.Class<Input>('Input')({
|
|
1130
|
-
maybeName: Schema.OptionFromNullishOr(Schema.String),
|
|
1131
|
-
maybeEmail: Schema.OptionFromOptionalKey(Schema.String)
|
|
1132
|
-
}) {}
|
|
1133
|
-
```
|
|
1134
|
-
|
|
1135
|
-
### Template: Dual helper (data-first + data-last)
|
|
1136
|
-
|
|
1137
|
-
```ts
|
|
1138
|
-
import { dual } from 'effect/Function';
|
|
1139
|
-
|
|
1140
|
-
export const rename: {
|
|
1141
|
-
(
|
|
1142
|
-
to: string
|
|
1143
|
-
): (self: { readonly name: string }) => { readonly name: string };
|
|
1144
|
-
(self: { readonly name: string }, to: string): { readonly name: string };
|
|
1145
|
-
} = dual(2, (self, to) => ({ ...self, name: to }));
|
|
1146
|
-
```
|
|
1147
|
-
|
|
1148
|
-
### Template: JSON boundary without native JSON APIs
|
|
1149
|
-
|
|
1150
|
-
```ts
|
|
1151
|
-
import * as Schema from 'effect/Schema';
|
|
1152
|
-
|
|
1153
|
-
export class Payload extends Schema.Class<Payload>('Payload')({
|
|
1154
|
-
query: Schema.String
|
|
1155
|
-
}) {}
|
|
1156
|
-
|
|
1157
|
-
const PayloadJson = Schema.fromJsonString(Payload);
|
|
1158
|
-
|
|
1159
|
-
export const decodePayloadJson = Schema.decodeUnknownEffect(PayloadJson);
|
|
1160
|
-
export const encodePayloadJson = Schema.encodeUnknownEffect(PayloadJson);
|
|
1161
|
-
```
|
|
1162
|
-
|
|
1163
|
-
### Template: Runtime boundary execution
|
|
1164
|
-
|
|
1165
|
-
```ts
|
|
1166
|
-
import { Effect } from 'effect';
|
|
1167
|
-
|
|
1168
|
-
export const buildReport = Effect.fn('Report.build')(function* () {
|
|
1169
|
-
return 'ok';
|
|
1170
|
-
});
|
|
1171
|
-
|
|
1172
|
-
// runtime boundary only
|
|
1173
|
-
// Effect.runPromise(buildReport())
|
|
1174
|
-
```
|
|
1175
|
-
|
|
1176
|
-
### Template: Scoped resource helper
|
|
1177
|
-
|
|
1178
|
-
```ts
|
|
1179
|
-
import { Effect } from 'effect';
|
|
1180
|
-
|
|
1181
|
-
export const withResource = <A, E, R>(
|
|
1182
|
-
use: (resource: Resource) => Effect.Effect<A, E, R>
|
|
1183
|
-
) => Effect.acquireUseRelease(acquireResource, use, releaseResource);
|
|
1184
|
-
```
|
|
1185
|
-
|
|
1186
|
-
### Template: Retry + timeout
|
|
1187
|
-
|
|
1188
|
-
```ts
|
|
1189
|
-
import { Duration, Effect, Schedule } from 'effect';
|
|
1190
|
-
|
|
1191
|
-
export const resilientTask = task.pipe(
|
|
1192
|
-
Effect.retry(Schedule.recurs(3)),
|
|
1193
|
-
Effect.timeoutOption(Duration.seconds(5))
|
|
1194
|
-
);
|
|
1195
|
-
```
|
|
1196
|
-
|
|
1197
|
-
### Template: Config + redacted secret
|
|
1198
|
-
|
|
1199
|
-
```ts
|
|
1200
|
-
import { Config, Effect } from 'effect';
|
|
1201
|
-
|
|
1202
|
-
export const loadConfig = Effect.fn('Config.load')(function* () {
|
|
1203
|
-
const port = yield* Config.Int('PORT');
|
|
1204
|
-
const apiKey = yield* Config.Redacted('API_KEY');
|
|
1205
|
-
return { port, apiKey };
|
|
1206
|
-
});
|
|
1207
|
-
```
|
|
1208
|
-
|
|
1209
|
-
### Template: Isolated layer provide
|
|
1210
|
-
|
|
1211
|
-
```ts
|
|
1212
|
-
import { Effect, Layer } from 'effect';
|
|
1213
|
-
|
|
1214
|
-
export const runIsolated = program.pipe(
|
|
1215
|
-
Effect.provide(Layer.fresh(AppLayer), { local: true })
|
|
1216
|
-
);
|
|
1217
|
-
```
|
|
1218
|
-
|
|
1219
|
-
## LLM Review Checklist
|
|
1220
|
-
|
|
1221
|
-
Use this before submitting code:
|
|
1222
|
-
|
|
1223
|
-
1. No `any`, no type assertions, no `@ts-ignore`, no non-null assertions.
|
|
1224
|
-
2. No untyped error throwing in domain logic.
|
|
1225
|
-
3. Nullish converted to `Option` at boundaries.
|
|
1226
|
-
4. Unknown input decoded with `Schema`.
|
|
1227
|
-
5. Canonical namespace imports (`Option`, `Schema`, `Arr`, `P`, `R`, etc.) present and used.
|
|
1228
|
-
6. No native `Object/Map/Set/Date/String` helpers in domain logic.
|
|
1229
|
-
7. Branching logic is exhaustive where appropriate (`Match.exhaustive`, schema `.match`, and `Arr.match` for array emptiness).
|
|
1230
|
-
8. No new schema constants end with `Schema`.
|
|
1231
|
-
9. For non-class schemas, new schema constants expose `export type X = typeof X.Type`.
|
|
1232
|
-
10. Schema annotations are used only where they materially improve docs, errors, or introspection.
|
|
1233
|
-
11. `Effect`-returning reusable functions are created with `Effect.fn`/`Effect.fnUntraced`.
|
|
1234
|
-
12. Critical flows include logs/spans/metrics instrumentation.
|
|
1235
|
-
13. Durations/time windows use `Duration` values.
|
|
1236
|
-
14. Nullish schema fields use `Schema.OptionFrom*` helpers when representing absence as `Option`.
|
|
1237
|
-
15. Exported helper combinators support dual API via `dual`.
|
|
1238
|
-
16. No `JSON.parse` / `JSON.stringify` in Effect-first domain paths.
|
|
1239
|
-
17. Prefer `Schema.Class` over `Schema.Struct` for all decoded shapes (domain models, HTTP responses, API payloads).
|
|
1240
|
-
18. Required verification commands are green.
|
|
1241
|
-
19. `Effect.run*` appears only in runtime boundaries (entrypoint/test harness).
|
|
1242
|
-
20. Promise-based APIs are lifted with `Effect.tryPromise`.
|
|
1243
|
-
21. Acquired resources use `Effect.acquireUseRelease` or `Effect.scoped`.
|
|
1244
|
-
22. Retries are declared with `Effect.retry` + `Schedule`.
|
|
1245
|
-
23. Timeouts use `Effect.timeoutOption` / `Effect.timeoutOrElse`.
|
|
1246
|
-
24. Forking intent is explicit (`forkChild` default; `forkDetach` justified).
|
|
1247
|
-
25. Large fan-out operations specify concurrency deliberately.
|
|
1248
|
-
26. Config values come from `Config` / `ConfigProvider`, not direct `process.env` in domain logic.
|
|
1249
|
-
27. Secrets are `Redacted` (`Config.Redacted` / `Redacted.make`) and not logged raw.
|
|
1250
|
-
28. Recovery uses `catchTag` / `catchFilter` for targeted cases.
|
|
1251
|
-
29. Expected failures use `Effect.fail`; defects are reserved for invariants and discarding irrelevant upstream error types via `orDie`.
|
|
1252
|
-
30. Isolation-sensitive layer provisioning uses `{ local: true }` or `Layer.fresh`.
|
|
1253
|
-
31. All decoded shapes (domain models, HTTP responses, API payloads) are schema-first with `Schema.Class`; plain `type` / `interface` is used only when schema is not a practical fit.
|
|
1254
|
-
32. Literal-string discriminant unions use `Schema.Union` + `Schema.toTaggedUnion`. For exhaustive matching over literals, use `Match`. For type guards, use `Schema.is(Schema.Literal(...))`.
|
|
1255
|
-
33. Schema defaults use `Schema.withConstructorDefault` / `Schema.withDecodingDefault*`, not ad-hoc fallback objects in handlers/services.
|
|
1256
|
-
34. Named or reused domain constraints are modeled as schemas first; built-in schema constructors/checks are preferred before `Schema.makeFilter`.
|
|
1257
|
-
35. Guard helpers for domain strings/paths/tags come from branded schemas with `Schema.is(...)`, not ad-hoc `regex.test(...)` predicates.
|
|
1258
|
-
36. Reusable schema checks and filter groups carry `identifier`, `title`, and `description`.
|
|
1259
|
-
37. Intermediate schemas are exported only when reusable or materially clarifying; otherwise they stay module-local.
|
|
1260
|
-
38. Schema-modeled comparisons use `Schema.toEquivalence(...)` where practical.
|
|
1261
|
-
39. Deterministic format conversions use `Schema.decodeTo(..., SchemaTransformation.transform(...))`.
|
|
1262
|
-
40. Trivial helper wrapper lambdas are collapsed to direct helper refs where safe, and passthrough `pipe(...)` callbacks are expressed with `flow(...)`.
|
|
1263
|
-
41. Runtime source avoids `node:fs` / `node:path` / `node:child_process`; use Effect `FileSystem` / `Path` / process services.
|
|
1264
|
-
42. Runtime source avoids native `fetch`; HTTP boundaries use `effect/unstable/http` + platform layers (`BunHttpClient.layer`, etc.).
|
|
1265
|
-
43. Runtime sorting uses `Arr.sort` with explicit `Order`, not native `Array.prototype.sort`.
|
|
1266
|
-
44. Boolean branching prefers `Bool.match` over ad-hoc `if/else` when branching on booleans.
|
|
1267
|
-
45. HTTP request/response composition uses Effect HTTP modules (`HttpClientRequest`, `HttpClientResponse`, `Headers`, `UrlParams`, `HttpMethod`, `HttpBody`).
|
|
1268
|
-
46. Retried operations have proven idempotency, and exhausted failures remain visible unless a truthful fallback exists.
|
|
1269
|
-
47. Provider/network calls do not run inside authoritative database transactions.
|
|
@@ -1,35 +1,29 @@
|
|
|
1
1
|
# Agent Rules
|
|
2
2
|
|
|
3
|
-
The bundled guidance targets **Effect 4.0.0
|
|
4
|
-
version before applying an API:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
The bundled guidance targets **Effect 4.0.0 (stable)**. Read the consuming project's
|
|
4
|
+
version before applying an API: v3, v4 prereleases, and unreleased main can have
|
|
5
|
+
different contracts. Use the same version of `effect` and every directly used
|
|
6
|
+
`@effect/*` package; Effect packages are versioned and released together.
|
|
7
|
+
|
|
8
|
+
Import area modules from `effect/<area>`: for example `effect/http`,
|
|
9
|
+
`effect/http-api`, `effect/rpc`, `effect/ai`, and `effect/process`. Import
|
|
10
|
+
`Arbitrary` from `effect/Arbitrary`, and binary-text codecs from
|
|
11
|
+
`effect/encoding/{Base64,Base64Url,Hex,EncodingError}`. Check source stability
|
|
12
|
+
annotations: `@stability unstable` APIs can break in minor releases even though
|
|
13
|
+
their import paths have no `unstable` segment. APIs without that annotation
|
|
14
|
+
follow semver. TypeScript 5.9 or newer is required.
|
|
15
|
+
|
|
16
|
+
Before planning or writing Effect code, load the skills relevant to the APIs involved.
|
|
17
|
+
For other tasks, load a skill only when its guidance is needed to answer or complete the task.
|
|
9
18
|
|
|
10
19
|
When skills leave any ambiguity, or when you encounter unfamiliar APIs during implementation, read the OpenCode `effect` reference at `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/`. Treat this reference as the source of truth over `node_modules`, stale external docs, or memory.
|
|
11
20
|
|
|
12
21
|
Check the reference revision too. For this baseline, inspect the
|
|
13
|
-
`effect@4.0.0
|
|
14
|
-
effect@4.0.0
|
|
22
|
+
`effect@4.0.0` tag (for example with `git show
|
|
23
|
+
effect@4.0.0:packages/effect/src/Schema.ts`) when main has moved ahead.
|
|
15
24
|
Source symbols and signatures at that tag take precedence over stale prose or
|
|
16
25
|
line-number links. Public exports marked `@internal` in source are not application APIs.
|
|
17
26
|
|
|
18
|
-
## Skill routing
|
|
19
|
-
|
|
20
|
-
Load every branch that the task crosses:
|
|
21
|
-
|
|
22
|
-
- Schemas, brands, variants, optionality, or decoding: `effect-schema-v4`, `effect-schema-composition`, and `effect-domain-modeling`.
|
|
23
|
-
- Binary schema codecs and framed binary streams: `effect-schema-composition` and `effect-stream`; RPC serialization also needs `effect-rpc-client` / `effect-rpc-server`.
|
|
24
|
-
- Services, layers, runtime wiring, or scoped lifetimes: `effect-service-implementation`, `effect-layer-design`, `effect-scope`, and `effect-fiber`.
|
|
25
|
-
- Configuration or secrets: `effect-config`.
|
|
26
|
-
- Retry, repeat, polling, backoff, pacing, or recurrence: `effect-scheduling` plus the relevant error, HTTP, or testing skill.
|
|
27
|
-
- Memoization, keyed caches, or request batching: `effect-cache` and `effect-batching`.
|
|
28
|
-
- Retained keyed resources or pooled checkout: `effect-cache`, `effect-layer-design`, and `effect-scope`.
|
|
29
|
-
- Streams, queues, pubsubs, pagination, or backpressure: `effect-stream` and the relevant concurrency skill.
|
|
30
|
-
- Outgoing HTTP: `effect-http-client` plus the relevant platform-layer skill.
|
|
31
|
-
- Effect tests, virtual time, or concurrent synchronization: `effect-testing` and `effect-concurrency-testing`.
|
|
32
|
-
|
|
33
27
|
## Start here
|
|
34
28
|
|
|
35
29
|
- `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/LLMS.md` — generated task-oriented guide for Effect v4, with links to examples.
|
|
@@ -44,7 +38,7 @@ Load every branch that the task crosses:
|
|
|
44
38
|
- `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/packages/effect/MCP.md` — MCP server resources, prompts, tools, and transports.
|
|
45
39
|
- `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/packages/effect/OPTIC.md` — `Optic` guide for lenses, prisms, optionals, traversals, and schema isos.
|
|
46
40
|
- `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/packages/vitest/README.md` — `@effect/vitest` testing guide.
|
|
47
|
-
- `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/
|
|
41
|
+
- `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/ai-docs/src/06_schedule/10_schedules.ts` — runnable Schedule guide at the stable tag; read alongside `Schedule.ts` before designing retries, repeats, polling, backoff, jitter, timeouts, or recurrence limits. The stable tag has no `cookbooks/schedule.md`.
|
|
48
42
|
|
|
49
43
|
Do not use migration notes, Effect-repo contributor patterns, or in-repo specs as general application guidance. Read those only when the task is explicitly about migrating old Effect code or contributing to the Effect repository itself.
|
|
50
44
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://www.schemastore.org/package.json",
|
|
3
3
|
"name": "opencode-effect-enforcer",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "OpenCode V2 plugin for Effect v4 skills, guidance, and pattern enforcement",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"opencode",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@ast-grep/napi": "^0.42.3",
|
|
47
47
|
"@opencode/plugin": "2.0.8",
|
|
48
48
|
"diff": "^9.0.0",
|
|
49
|
-
"effect": "4.0.0
|
|
49
|
+
"effect": "4.0.0",
|
|
50
50
|
"picomatch": "^4.0.3",
|
|
51
51
|
"yaml": "^2.8.1"
|
|
52
52
|
},
|
package/patterns/avoid-any.md
CHANGED
|
@@ -30,8 +30,8 @@ generics :: ∀ a. Constraint a ⇒ a → F a
|
|
|
30
30
|
bad :: Unknown → T
|
|
31
31
|
bad x = x `as` T -- trust me bro
|
|
32
32
|
|
|
33
|
-
good :: Unknown →
|
|
34
|
-
good x = Schema.
|
|
33
|
+
good :: Unknown → Effect T SchemaError
|
|
34
|
+
good x = Schema.decodeUnknownEffect schemaT x -- decode and retain the proof
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Using `as any` bypasses type checking entirely. The `as unknown as T` pattern is equivalent—casting through `unknown` still erases type information. Note: `as const` is acceptable—it narrows to literal types without erasing type safety.
|
|
@@ -30,8 +30,8 @@ encodeJson :: Schema a → a → String
|
|
|
30
30
|
bad :: String → IO User
|
|
31
31
|
bad json = JSON.parse json -- returns Any, throws on invalid
|
|
32
32
|
|
|
33
|
-
good :: String →
|
|
34
|
-
good json = Schema.
|
|
33
|
+
good :: String → Effect User SchemaError
|
|
34
|
+
good json = Schema.decodeUnknownEffect(Schema.fromJsonString(User)) json
|
|
35
35
|
|
|
36
36
|
unknownJson = Schema.fromJsonString(Schema.Unknown)
|
|
37
37
|
|
|
@@ -41,11 +41,11 @@ data User = Schema.Class "User"
|
|
|
41
41
|
, name :: Schema.String
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
decode :: String →
|
|
45
|
-
decode = Schema.
|
|
44
|
+
decode :: String → Effect User SchemaError
|
|
45
|
+
decode = Schema.decodeUnknownEffect(Schema.fromJsonString(User))
|
|
46
46
|
|
|
47
|
-
encode :: User → String
|
|
48
|
-
encode = Schema.
|
|
47
|
+
encode :: User → Effect String SchemaError
|
|
48
|
+
encode = Schema.encodeEffect(Schema.fromJsonString(User))
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
`JSON.parse` returns `any` and throws on invalid input. `Schema.fromJsonString(...)` provides typed, validated JSON parsing and encoding. Use `Schema.fromJsonString(Schema.Unknown)` when the JSON shape is intentionally unknown; `Schema.UnknownFromJsonString` is internal in current Effect v4. Direct JSON methods remain reasonable at narrow logging/debugging boundaries.
|