opencode-effect-enforcer 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +278 -0
- package/guidance/effect-first-development.md +1247 -0
- package/guidance/post__effect-and-the-near-inexpressible-majesty-of-layers.md +490 -0
- package/guidance/post__parse-dont-validate.md +109 -0
- package/guidance/progressive-disclosure-guidance.md +38 -0
- package/package.json +63 -0
- package/patterns/avoid-any.md +37 -0
- package/patterns/avoid-data-tagged-error.md +34 -0
- package/patterns/avoid-direct-json.md +51 -0
- package/patterns/avoid-direct-tag-checks.md +54 -0
- package/patterns/avoid-expect-in-if.md +52 -0
- package/patterns/avoid-mutable-state.md +70 -0
- package/patterns/avoid-native-fetch.md +61 -0
- package/patterns/avoid-node-imports.md +86 -0
- package/patterns/avoid-non-null-assertion.md +44 -0
- package/patterns/avoid-object-type.md +46 -0
- package/patterns/avoid-option-getorthrow.md +39 -0
- package/patterns/avoid-platform-coupling.md +43 -0
- package/patterns/avoid-process-env.md +43 -0
- package/patterns/avoid-react-hooks.md +73 -0
- package/patterns/avoid-schema-suffix.md +45 -0
- package/patterns/avoid-sync-fs.md +68 -0
- package/patterns/avoid-try-catch.md +47 -0
- package/patterns/avoid-ts-ignore.md +38 -0
- package/patterns/avoid-untagged-errors.md +67 -0
- package/patterns/avoid-yield-ref.md +46 -0
- package/patterns/casting-awareness.md +46 -0
- package/patterns/context-tag-extends.md +84 -0
- package/patterns/effect-catchall-default.md +61 -0
- package/patterns/effect-promise-vs-trypromise.md +47 -0
- package/patterns/effect-run-in-body.md +58 -0
- package/patterns/imperative-loops.md +76 -0
- package/patterns/prefer-arr-sort.md +52 -0
- package/patterns/prefer-duration-values.md +56 -0
- package/patterns/prefer-effect-fn.md +161 -0
- package/patterns/prefer-match-over-switch.md +48 -0
- package/patterns/prefer-option-over-null.md +56 -0
- package/patterns/prefer-redacted-config.md +70 -0
- package/patterns/prefer-schema-class.md +54 -0
- package/patterns/require-effect-concurrency.md +83 -0
- package/patterns/stream-large-files.md +63 -0
- package/patterns/throw-in-effect-gen.md +62 -0
- package/patterns/use-clock-service.md +45 -0
- package/patterns/use-command-executor-service.md +54 -0
- package/patterns/use-console-service.md +54 -0
- package/patterns/use-filesystem-service.md +59 -0
- package/patterns/use-http-client-service.md +77 -0
- package/patterns/use-path-service.md +53 -0
- package/patterns/use-random-service.md +45 -0
- package/patterns/use-temp-file-scoped.md +66 -0
- package/patterns/vm-in-wrong-file.md +51 -0
- package/patterns/yield-in-for-loop.md +61 -0
- package/skills/effect-ai-chat/SKILL.md +472 -0
- package/skills/effect-ai-language-model/SKILL.md +652 -0
- package/skills/effect-ai-prompt/SKILL.md +752 -0
- package/skills/effect-ai-provider/SKILL.md +668 -0
- package/skills/effect-ai-streaming/SKILL.md +418 -0
- package/skills/effect-ai-tool/SKILL.md +1132 -0
- package/skills/effect-atom-rpc/SKILL.md +488 -0
- package/skills/effect-atom-state/SKILL.md +640 -0
- package/skills/effect-batching/SKILL.md +614 -0
- package/skills/effect-cache/SKILL.md +570 -0
- package/skills/effect-cli/SKILL.md +523 -0
- package/skills/effect-command-executor/SKILL.md +675 -0
- package/skills/effect-concurrency-testing/SKILL.md +612 -0
- package/skills/effect-config/SKILL.md +580 -0
- package/skills/effect-context-witness/SKILL.md +274 -0
- package/skills/effect-domain-modeling/SKILL.md +1212 -0
- package/skills/effect-domain-predicates/SKILL.md +867 -0
- package/skills/effect-error-handling/SKILL.md +1581 -0
- package/skills/effect-fiber/SKILL.md +731 -0
- package/skills/effect-filesystem/SKILL.md +624 -0
- package/skills/effect-graph/SKILL.md +571 -0
- package/skills/effect-http-api/SKILL.md +1760 -0
- package/skills/effect-http-client/SKILL.md +989 -0
- package/skills/effect-http-server/SKILL.md +920 -0
- package/skills/effect-incremental-migration/SKILL.md +362 -0
- package/skills/effect-layer-design/SKILL.md +642 -0
- package/skills/effect-managed-runtime/SKILL.md +395 -0
- package/skills/effect-mcp-server/SKILL.md +608 -0
- package/skills/effect-observability/SKILL.md +719 -0
- package/skills/effect-optics/SKILL.md +554 -0
- package/skills/effect-parallelization/SKILL.md +668 -0
- package/skills/effect-path/SKILL.md +296 -0
- package/skills/effect-pattern-matching/SKILL.md +914 -0
- package/skills/effect-platform-abstraction/SKILL.md +1175 -0
- package/skills/effect-platform-layers/SKILL.md +514 -0
- package/skills/effect-pubsub-event-bus/SKILL.md +384 -0
- package/skills/effect-react-composition/SKILL.md +986 -0
- package/skills/effect-react-vm/SKILL.md +675 -0
- package/skills/effect-rpc-api/SKILL.md +624 -0
- package/skills/effect-rpc-client/SKILL.md +666 -0
- package/skills/effect-rpc-cluster/SKILL.md +1623 -0
- package/skills/effect-rpc-server/SKILL.md +767 -0
- package/skills/effect-scheduling/SKILL.md +124 -0
- package/skills/effect-schema-composition/SKILL.md +975 -0
- package/skills/effect-schema-v4/SKILL.md +691 -0
- package/skills/effect-scope/SKILL.md +682 -0
- package/skills/effect-service-implementation/SKILL.md +656 -0
- package/skills/effect-socket/SKILL.md +703 -0
- package/skills/effect-sql/SKILL.md +781 -0
- package/skills/effect-stream/SKILL.md +765 -0
- package/skills/effect-testing/SKILL.md +1331 -0
- package/skills/effect-typeclass-design/SKILL.md +161 -0
- package/skills/effect-wide-events/Article.md +66 -0
- package/skills/effect-wide-events/SKILL.md +95 -0
- package/skills/effect-workflow/SKILL.md +810 -0
- package/src/agent-policy.ts +22 -0
- package/src/enforcer.ts +104 -0
- package/src/frontmatter.ts +34 -0
- package/src/guidance.ts +66 -0
- package/src/index.ts +38 -0
- package/src/pattern-catalog.ts +115 -0
- package/src/pattern-matcher.ts +178 -0
- package/src/pattern.ts +97 -0
- package/src/skills.ts +29 -0
- package/src/write-projection.ts +66 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: effect-scheduling
|
|
3
|
+
description: Design Effect v4 retries, repeats, polling, pacing, backoff, jitter, rate-limit-aware delays, and timeouts with Schedule. Use when replacing manual sleep loops or defining recurrence and failure policy.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are an Effect TypeScript expert specializing in `Schedule`, retry, repeat, polling, and time policy.
|
|
7
|
+
|
|
8
|
+
## Source Of Truth
|
|
9
|
+
|
|
10
|
+
Verify APIs against `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/packages/effect/src/Schedule.ts` and `Effect.ts`. In Effect v4, `Schedule.concat` is current, `Schedule.tapInput` is absent, and `Schedule.tap` receives full metadata.
|
|
11
|
+
|
|
12
|
+
## Semantics
|
|
13
|
+
|
|
14
|
+
- `Effect.retry` reruns typed failures. It does not retry defects or interruption.
|
|
15
|
+
- `Effect.repeat` reruns successes. A typed failure stops repetition unless the pass handles it first.
|
|
16
|
+
- The source effect runs once before the schedule is stepped.
|
|
17
|
+
- `Schedule.recurs(3)` permits three recurrences after the initial evaluation: at most four evaluations total.
|
|
18
|
+
- Schedules can require services and fail; schedule errors join the resulting effect's error channel.
|
|
19
|
+
- Retry only the narrowest idempotent operation. Never retry non-idempotent writes unless an idempotency key, transaction, or equivalent guarantee makes replay safe.
|
|
20
|
+
|
|
21
|
+
## Policy Chooser
|
|
22
|
+
|
|
23
|
+
- Counter only: `Schedule.recurs(n)`.
|
|
24
|
+
- Delay after each completed run: `Schedule.spaced(duration)`.
|
|
25
|
+
- Cadence aligned to time boundaries: `Schedule.fixed(interval)`; slow work may make the next run immediate, and missed ticks are not replayed.
|
|
26
|
+
- Backoff: `Schedule.exponential(base)` or `Schedule.fibonacci(base)`.
|
|
27
|
+
- Desynchronize callers: pipe through `Schedule.jittered`.
|
|
28
|
+
- Bound an existing delay schedule: `Schedule.upTo({ times, duration })`.
|
|
29
|
+
- Run one schedule after another: `Schedule.concat(first, second)`; use `concatResult` when phase identity matters.
|
|
30
|
+
- Stop from full metadata: `Schedule.while(predicate)`; a type-guard predicate narrows both schedule input and output.
|
|
31
|
+
- Observe decisions: `Schedule.tap(({ attempt, input, output, duration, elapsed }) => ...)`.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const retryPolicy = Schedule.exponential('100 millis').pipe(
|
|
35
|
+
Schedule.jittered,
|
|
36
|
+
Schedule.upTo({ times: 5 }),
|
|
37
|
+
Schedule.tap(({ attempt, input, duration, elapsed }) =>
|
|
38
|
+
Effect.logWarning('request retry').pipe(
|
|
39
|
+
Effect.annotateLogs({ attempt, error: input, duration, elapsed })
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const result = request.pipe(
|
|
45
|
+
Effect.retryOrElse(retryPolicy, (error, scheduleOutput) =>
|
|
46
|
+
Effect.logError('request retries exhausted', error).pipe(
|
|
47
|
+
Effect.zipRight(fallback(error, scheduleOutput))
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`retryOrElse` receives the final typed error and the schedule's terminal output. A fallback must be truthful; otherwise let the final failure remain visible.
|
|
54
|
+
|
|
55
|
+
`Schedule.while` supports refinement predicates in both data-first and data-last forms. The resulting schedule carries the narrowed `metadata.input` and `metadata.output` types:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
declare const mixed: Schedule.Schedule<number | string, Date | boolean>;
|
|
59
|
+
|
|
60
|
+
const numericDates = mixed.pipe(
|
|
61
|
+
Schedule.while(
|
|
62
|
+
(metadata): metadata is Schedule.Metadata<number, Date> =>
|
|
63
|
+
typeof metadata.output === 'number' && metadata.input instanceof Date
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
// Schedule.Schedule<number, Date>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Polling And Item Failure Policy
|
|
70
|
+
|
|
71
|
+
Use `Effect.repeat(pass, Schedule.spaced(...))` for a worker that emits no meaningful values. Use `Stream.fromEffectSchedule` when each result is part of a stream pipeline.
|
|
72
|
+
|
|
73
|
+
Decide failures at the correct granularity:
|
|
74
|
+
|
|
75
|
+
- Pass failure should stop the worker: leave it typed and let the owner monitor the fiber.
|
|
76
|
+
- Expected pass failure should be logged and polling continue: handle that typed error inside `pass`, then repeat the successful handled pass.
|
|
77
|
+
- One bad item should not stop a batch: catch expected typed errors around each item only when skip/retry-later is actual product policy.
|
|
78
|
+
- Defects should reach supervision. Interruption is normal shutdown and must not be converted into a retryable error.
|
|
79
|
+
|
|
80
|
+
Long-lived polling fibers need explicit ownership. `forkScoped` ties lifetime to a scope but does not restart or propagate child failure automatically; monitor/join or supervise according to runtime policy.
|
|
81
|
+
|
|
82
|
+
## Rate-Limit-Aware Retry
|
|
83
|
+
|
|
84
|
+
Use `Schedule.modifyDelay` to select the greater of computed backoff and a typed provider retry delay. The callback is effectful and receives metadata; `Schedule.passthrough` is unnecessary when only `metadata.input` is needed.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const providerPolicy = Schedule.exponential('200 millis').pipe(
|
|
88
|
+
Schedule.jittered,
|
|
89
|
+
Schedule.upTo({ times: 5 }),
|
|
90
|
+
Schedule.modifyDelay(({ input, duration }) =>
|
|
91
|
+
Effect.succeed(
|
|
92
|
+
Option.match(input.retryAfter, {
|
|
93
|
+
onNone: () => duration,
|
|
94
|
+
onSome: (retryAfter) => Duration.max(duration, retryAfter)
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Apply this to typed rate-limit/transient failures only. Do not retry authentication, validation, quota exhaustion, or permanent not-found failures without an explicit reason.
|
|
102
|
+
|
|
103
|
+
## Timeouts And Delays
|
|
104
|
+
|
|
105
|
+
- `Effect.timeout(duration)` interrupts the source on expiry and fails with `Cause.TimeoutError`.
|
|
106
|
+
- `Effect.timeoutOption(duration)` represents only timeout as `Option.none` while preserving source failures.
|
|
107
|
+
- `Effect.timeoutOrElse({ duration, orElse })` runs a typed fallback after interrupting the source.
|
|
108
|
+
- `Effect.delay(duration)` delays the start of one effect.
|
|
109
|
+
- `Effect.sleep(duration)` is appropriate when sleeping itself is workflow behavior.
|
|
110
|
+
- Do not build recurring work with manual sleep loops; compose `repeat`/`retry` with `Schedule`.
|
|
111
|
+
- Test time policies with `TestClock`, not real sleeps.
|
|
112
|
+
|
|
113
|
+
## Checklist
|
|
114
|
+
|
|
115
|
+
- Retry and repeat semantics are not confused.
|
|
116
|
+
- Initial attempt plus recurrence count is documented.
|
|
117
|
+
- Backoff is bounded and jittered where many callers can synchronize.
|
|
118
|
+
- Retried work is idempotent.
|
|
119
|
+
- Poll and per-item failure policies are explicit.
|
|
120
|
+
- Interruption remains cancellation.
|
|
121
|
+
- Exhaustion is observable or has a truthful fallback.
|
|
122
|
+
- Rate-limit metadata influences delay through `modifyDelay`.
|
|
123
|
+
- Refinement predicates passed to `Schedule.while` preserve their narrowed input and output types.
|
|
124
|
+
- No removed `Schedule.tapInput` or old sequential-composition name is used.
|