@danieljvdm/dev-kit 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.
Files changed (47) hide show
  1. package/README.md +290 -0
  2. package/bin/dev-kit.mjs +3 -0
  3. package/dev-kit.example.jsonc +13 -0
  4. package/package.json +69 -0
  5. package/schema/dev-kit.schema.json +128 -0
  6. package/schema/skill-sources.schema.json +83 -0
  7. package/skill-sources.jsonc +55 -0
  8. package/skill-sources.lock.json +136 -0
  9. package/skills/dev-kit/SKILL.md +145 -0
  10. package/skills/dev-kit/agents/openai.yaml +4 -0
  11. package/skills/effect-ts/SKILL.md +242 -0
  12. package/skills/effect-ts/UPSTREAM.md +28 -0
  13. package/skills/effect-ts/agents/openai.yaml +5 -0
  14. package/skills/effect-ts/references/audit-services.md +144 -0
  15. package/skills/effect-ts/references/features.md +525 -0
  16. package/skills/effect-ts/references/guide-cli.md +106 -0
  17. package/skills/effect-ts/references/guide-effect.md +453 -0
  18. package/skills/effect-ts/references/guide-error-handling.md +574 -0
  19. package/skills/effect-ts/references/guide-http-boundaries.md +55 -0
  20. package/skills/effect-ts/references/guide-layers.md +1017 -0
  21. package/skills/effect-ts/references/guide-observability.md +771 -0
  22. package/skills/effect-ts/references/guide-retries.md +446 -0
  23. package/skills/effect-ts/references/guide-schedule.md +357 -0
  24. package/skills/effect-ts/references/guide-schema.md +671 -0
  25. package/skills/effect-ts/references/guide-sql.md +539 -0
  26. package/skills/effect-ts/references/guide-testing.md +534 -0
  27. package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +131 -0
  28. package/skills/effect-ts/references/version-and-source.md +87 -0
  29. package/src/bin/dev-kit.ts +372 -0
  30. package/src/catalog-manager.ts +345 -0
  31. package/src/catalog.ts +246 -0
  32. package/src/cli-ui.ts +110 -0
  33. package/src/effect-source.ts +325 -0
  34. package/src/effect-tsgo.ts +256 -0
  35. package/src/gitignore.ts +212 -0
  36. package/src/index.ts +98 -0
  37. package/src/manifest.ts +133 -0
  38. package/src/node-symbolic-link.ts +31 -0
  39. package/src/path-digest.ts +140 -0
  40. package/src/project-process-lock.ts +76 -0
  41. package/src/project-state.ts +67 -0
  42. package/src/skill-manager.ts +326 -0
  43. package/src/source-manifest.ts +51 -0
  44. package/src/sync.ts +900 -0
  45. package/src/tool-metadata.ts +3 -0
  46. package/src/typescript-package-name.ts +5 -0
  47. package/src/vendor.ts +848 -0
@@ -0,0 +1,446 @@
1
+ # Retries Guide
2
+
3
+ Use Effect test services and `TestClock` for retry timing tests. Do not add
4
+ wall-clock sleeps to make a retry test pass.
5
+
6
+ This guide is based on retry patterns and `ExecutionPlan` usage in the canonical Effect source.
7
+
8
+ Key source files:
9
+
10
+ - `packages/effect/src/Effect.ts`
11
+ - `packages/effect/src/Schedule.ts`
12
+ - `packages/effect/src/ExecutionPlan.ts`
13
+ - `packages/effect/test/Effect.test.ts`
14
+ - `packages/effect/test/ExecutionPlan.test.ts`
15
+
16
+ Representative repo usage:
17
+
18
+ - `packages/effect/src/unstable/workflow/Activity.ts`
19
+ - `packages/effect/src/unstable/workflow/WorkflowEngine.ts`
20
+ - `packages/effect/src/unstable/rpc/RpcClient.ts`
21
+ - `packages/effect/src/unstable/observability/OtlpExporter.ts`
22
+ - `packages/vitest/src/internal/internal.ts`
23
+
24
+ ## Mental Model
25
+
26
+ Retries in Effect are not just loops.
27
+
28
+ The repo uses three increasingly powerful levels:
29
+
30
+ 1. simple `Effect.retry` options for bounded or condition-based retries
31
+ 2. `Schedule` for timing-aware retry policies
32
+ 3. `ExecutionPlan` for fallback across different provided resources or layers
33
+
34
+ Choose the smallest model that correctly expresses the retry policy.
35
+
36
+ ## Preferred Rule
37
+
38
+ Prefer structured retry policies over ad hoc retry loops.
39
+
40
+ Use:
41
+
42
+ - simple `Effect.retry({ ... })` for straightforward conditions
43
+ - `Effect.retry(schedule)` when timing matters
44
+ - `ExecutionPlan` when retries should escalate across different layers or resources
45
+
46
+ Avoid:
47
+
48
+ - hand-written loops with mutable counters
49
+ - inline `catch` plus recursive retry logic
50
+ - resource fallback logic encoded as nested `catch` chains when `ExecutionPlan` is a better fit
51
+
52
+ ## `Effect.retry`
53
+
54
+ `Effect.retry` is the main retry operator.
55
+
56
+ The canonical tests show several important supported forms.
57
+
58
+ ## Retry On Success vs Failure
59
+
60
+ `Effect.retry` only retries failures.
61
+
62
+ If the effect succeeds, nothing is retried.
63
+
64
+ This is explicitly covered in the module tests.
65
+
66
+ ## Simple Retry Options
67
+
68
+ ### `{ times: n }`
69
+
70
+ Use this for the simplest bounded retry case.
71
+
72
+ ```ts
73
+ const retried = effect.pipe(
74
+ Effect.retry({ times: 3 })
75
+ )
76
+ ```
77
+
78
+ Use this when:
79
+
80
+ - timing does not matter
81
+ - you only need a fixed retry count
82
+
83
+ ### `{ until: predicate }`
84
+
85
+ Use `until` when retries should stop once the failure value satisfies a condition.
86
+
87
+ The module tests show:
88
+
89
+ - pure `until`
90
+ - effectful `until`
91
+ - that `until` is still evaluated at least once
92
+
93
+ Example:
94
+
95
+ ```ts
96
+ const retried = effect.pipe(
97
+ Effect.retry({ until: (error) => error._tag === "Done" })
98
+ )
99
+ ```
100
+
101
+ ### `{ while: predicate }`
102
+
103
+ Use `while` when retries should continue only while the failure value satisfies a condition.
104
+
105
+ The tests also show pure and effectful `while` variants.
106
+
107
+ Example:
108
+
109
+ ```ts
110
+ const retried = effect.pipe(
111
+ Effect.retry({ while: (error) => error._tag === "Retryable" })
112
+ )
113
+ ```
114
+
115
+ ## Retry With Schedule
116
+
117
+ Use a `Schedule` whenever timing matters.
118
+
119
+ ```ts
120
+ const retried = effect.pipe(
121
+ Effect.retry(Schedule.recurs(3))
122
+ )
123
+ ```
124
+
125
+ Or with the richer object form:
126
+
127
+ ```ts
128
+ const retried = effect.pipe(
129
+ Effect.retry({
130
+ schedule: Schedule.recurs(3),
131
+ while: (error) => error._tag === "Retryable"
132
+ })
133
+ )
134
+ ```
135
+
136
+ This is a very important repo pattern because it lets you combine:
137
+
138
+ - retry timing
139
+ - retry limits
140
+ - retry predicates
141
+
142
+ ## Current Schedule Metadata During Retries
143
+
144
+ The module tests show that retry execution updates `Schedule.CurrentMetadata`.
145
+
146
+ This means retry policies and retry-aware effects can inspect:
147
+
148
+ - attempt number
149
+ - elapsed time
150
+ - previous delay timing
151
+ - schedule output
152
+
153
+ Use this when:
154
+
155
+ - logging retry behavior
156
+ - building retry-aware diagnostics
157
+ - implementing advanced adaptive retry behavior
158
+
159
+ ## When To Use Simple Options Vs Schedule
160
+
161
+ Prefer simple options when:
162
+
163
+ - only the retry count matters
164
+ - retry timing does not matter
165
+ - the retry rule is just a condition on the error
166
+
167
+ Prefer a schedule when:
168
+
169
+ - retry timing matters
170
+ - backoff matters
171
+ - jitter matters
172
+ - the policy should evolve over time
173
+
174
+ ## Common Retry Schedules In The Repo
175
+
176
+ The canonical source repeatedly uses these patterns:
177
+
178
+ ### Fixed retry count
179
+
180
+ ```ts
181
+ Schedule.recurs(3)
182
+ ```
183
+
184
+ ### Exponential backoff
185
+
186
+ ```ts
187
+ Schedule.exponential("500 millis", 1.5)
188
+ ```
189
+
190
+ ### Exponential plus steady fallback spacing
191
+
192
+ ```ts
193
+ Schedule.exponential("500 millis", 1.5).pipe(
194
+ Schedule.upTo({ times: 4 }),
195
+ Schedule.andThen(Schedule.spaced("5 seconds")),
196
+ )
197
+ ```
198
+
199
+ This appears in production modules such as RPC and workflow code.
200
+
201
+ ### Error-sensitive delay policy
202
+
203
+ ```ts
204
+ Schedule.forever.pipe(
205
+ Schedule.setInputType<RetryableError>(),
206
+ Schedule.addDelay(({ input: error }) =>
207
+ Effect.succeed(error.retryAfter ?? "1 second")
208
+ ),
209
+ )
210
+ ```
211
+
212
+ The OTLP exporter uses this shape to derive delays from actual HTTP failure details such as rate limits.
213
+
214
+ ## Retry Only For Specific Failures
215
+
216
+ The workflow `Activity` module shows an important advanced pattern:
217
+
218
+ - sandbox the effect
219
+ - retry only when the `Cause` matches a specific retryable category
220
+ - fail or die differently once retries are exhausted
221
+
222
+ Example shape from the repo:
223
+
224
+ ```ts
225
+ effect.pipe(
226
+ Effect.sandbox,
227
+ Effect.retry(policy),
228
+ Effect.catch((cause) => {
229
+ if (!Cause.hasInterrupts(cause)) {
230
+ return Effect.failCause(cause)
231
+ }
232
+ return Effect.die("interrupted and retries exhausted")
233
+ })
234
+ )
235
+ ```
236
+
237
+ Use this when:
238
+
239
+ - retryability depends on the full cause, not just typed failures
240
+ - interrupt-specific retry behavior is required
241
+ - infrastructure policy is more nuanced than a simple tagged error rule
242
+
243
+ ## Retry Observability
244
+
245
+ Retry logic should be observable.
246
+
247
+ Good patterns:
248
+
249
+ - keep retries inside named `Effect.fn` operations
250
+ - use `Schedule.CurrentMetadata` for diagnostics when needed
251
+ - log or annotate retry attempts at meaningful boundaries
252
+ - prefer central retry policies over duplicating timing logic everywhere
253
+
254
+ Do not spread retry behavior across many small helpers where it becomes hard to see the operational policy.
255
+
256
+ ## `ExecutionPlan`
257
+
258
+ Use `ExecutionPlan` when retries should escalate across different provided resources or layers.
259
+
260
+ This is not just about retry timing. It is about retrying the same operation under different provided environments.
261
+
262
+ The core use case from `ExecutionPlan.ts` is:
263
+
264
+ - try one layer some number of times
265
+ - possibly with a schedule and conditions
266
+ - then fall back to another layer
267
+ - then possibly fall back again
268
+
269
+ ### What `ExecutionPlan` Solves
270
+
271
+ `ExecutionPlan` is the right tool when:
272
+
273
+ - the same effect should be retried against multiple alternative providers
274
+ - fallback should move across tiers, regions, models, or implementations
275
+ - retry policy includes both attempt counts and provider changes
276
+
277
+ Examples:
278
+
279
+ - fail over between multiple language model providers
280
+ - try one upstream cluster, then another
281
+ - fall back from a fast but unreliable service to a slower but more reliable one
282
+
283
+ ## `ExecutionPlan.make`
284
+
285
+ Use `ExecutionPlan.make(...)` to define ordered retry/fallback steps.
286
+
287
+ Each step can include:
288
+
289
+ - `provide`
290
+ - `attempts`
291
+ - `while`
292
+ - `schedule`
293
+
294
+ Example shape:
295
+
296
+ ```ts
297
+ const Plan = ExecutionPlan.make(
298
+ {
299
+ provide: FastLayer,
300
+ attempts: 2,
301
+ schedule: Schedule.spaced("3 seconds")
302
+ },
303
+ {
304
+ provide: SafeLayer,
305
+ attempts: 3,
306
+ schedule: Schedule.spaced("1 second")
307
+ },
308
+ {
309
+ provide: FinalFallbackLayer
310
+ }
311
+ )
312
+ ```
313
+
314
+ ### Step Semantics
315
+
316
+ For each step:
317
+
318
+ - `provide` is the context or layer to use
319
+ - `attempts` bounds how many times that step is tried
320
+ - `while` can stop retries for that step based on the input
321
+ - `schedule` defines the timing policy for retries within that step
322
+
323
+ If `attempts` is omitted, the step attempts once unless a schedule is involved in a way that causes further retries.
324
+
325
+ ## `Effect.withExecutionPlan` And `Stream.withExecutionPlan`
326
+
327
+ Use:
328
+
329
+ - `Effect.withExecutionPlan` for effects
330
+ - `Stream.withExecutionPlan` for streams
331
+
332
+ The canonical tests focus on `Stream.withExecutionPlan` and demonstrate:
333
+
334
+ - fallback from one provider to another
335
+ - fallback after partial stream failure
336
+ - the ability to prevent fallback on partial streams
337
+
338
+ This is a strong signal that `ExecutionPlan` is particularly useful for long-running or streaming integrations where failure can happen after partial success.
339
+
340
+ ## `ExecutionPlan.CurrentMetadata`
341
+
342
+ `ExecutionPlan` exposes metadata with:
343
+
344
+ - `attempt`
345
+ - `stepIndex`
346
+
347
+ This is useful for:
348
+
349
+ - diagnostics
350
+ - logging which fallback tier is being used
351
+ - understanding which plan step ultimately succeeded
352
+
353
+ ## `captureRequirements`
354
+
355
+ Every plan exposes a `captureRequirements` Effect that converts the plan into
356
+ one whose requirements are satisfied from the current context.
357
+
358
+ Use this when the plan should be frozen with the current environment before being applied later.
359
+
360
+ ```ts
361
+ const capturedPlan = yield* Plan.captureRequirements
362
+ ```
363
+
364
+ ## `ExecutionPlan.merge`
365
+
366
+ Use `ExecutionPlan.merge(...)` when you need to concatenate multiple plans into one ordered plan.
367
+
368
+ This is useful for assembling more complex fallback policies out of smaller ones.
369
+
370
+ ## When To Use `ExecutionPlan` Instead Of `Schedule`
371
+
372
+ Use `Schedule` when:
373
+
374
+ - only timing and retry conditions change
375
+ - the same environment/provider is used for every retry
376
+
377
+ Use `ExecutionPlan` when:
378
+
379
+ - the provider or layer should change across retry phases
380
+ - retries are tied to alternative resources, not just delays
381
+ - fallback is part of dependency provisioning strategy
382
+
383
+ ## Recommended Patterns
384
+
385
+ ### Pattern: simple bounded retry
386
+
387
+ ```ts
388
+ const retried = effect.pipe(
389
+ Effect.retry({ times: 3 })
390
+ )
391
+ ```
392
+
393
+ ### Pattern: retryable-error backoff
394
+
395
+ ```ts
396
+ const retryPolicy = Schedule.exponential("500 millis", 1.5).pipe(
397
+ Schedule.upTo({ times: 4 }),
398
+ Schedule.andThen(Schedule.spaced("5 seconds")),
399
+ )
400
+
401
+ const retried = effect.pipe(
402
+ Effect.retry({
403
+ schedule: retryPolicy,
404
+ while: (error) => error._tag === "Retryable"
405
+ })
406
+ )
407
+ ```
408
+
409
+ ### Pattern: fallback across providers
410
+
411
+ ```ts
412
+ const Plan = ExecutionPlan.make(
413
+ {
414
+ provide: PrimaryLayer,
415
+ attempts: 2,
416
+ schedule: Schedule.spaced("1 second")
417
+ },
418
+ {
419
+ provide: SecondaryLayer,
420
+ attempts: 3,
421
+ schedule: Schedule.exponential(500, 1.5)
422
+ },
423
+ {
424
+ provide: FinalFallbackLayer
425
+ }
426
+ )
427
+ ```
428
+
429
+ ## Anti-Patterns
430
+
431
+ - hand-writing retry recursion instead of using `Effect.retry`
432
+ - embedding sleep and counters directly in business logic
433
+ - using `ExecutionPlan` when a simple `Schedule` is enough
434
+ - encoding provider fallback as a maze of nested `catch` branches
435
+ - retrying indiscriminately without checking whether the failure is actually retryable
436
+
437
+ ## Good Repo Examples To Study
438
+
439
+ - `packages/effect/test/Effect.test.ts`
440
+ - `packages/effect/src/Schedule.ts`
441
+ - `packages/effect/src/ExecutionPlan.ts`
442
+ - `packages/effect/test/ExecutionPlan.test.ts`
443
+ - `packages/effect/src/unstable/workflow/Activity.ts`
444
+ - `packages/effect/src/unstable/workflow/WorkflowEngine.ts`
445
+ - `packages/effect/src/unstable/rpc/RpcClient.ts`
446
+ - `packages/effect/src/unstable/observability/OtlpExporter.ts`