@martinffx/atelier 1.4.0 → 3.0.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 (93) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +95 -220
  3. package/agents/architect.md +6 -6
  4. package/agents/oracle.md +1 -1
  5. package/agents/{recon.md → sentinel.md} +3 -3
  6. package/dist/atelier.js +167 -26
  7. package/package.json +2 -1
  8. package/skills/atelier-orchestrator/SKILL.md +180 -0
  9. package/skills/atelier-setup/SKILL.md +80 -0
  10. package/skills/code-review/SKILL.md +2 -2
  11. package/skills/code-review/references/reviewers.md +3 -3
  12. package/skills/code-review/references/rq.md +9 -9
  13. package/skills/code-subagents/SKILL.md +44 -75
  14. package/skills/code-subagents/references/batch-reviewer-prompt.md +44 -0
  15. package/skills/code-subagents/references/implementor-prompt.md +10 -11
  16. package/skills/spec-brainstorm/SKILL.md +21 -16
  17. package/skills/spec-finish/SKILL.md +14 -14
  18. package/skills/spec-implement/SKILL.md +42 -42
  19. package/skills/spec-plan/SKILL.md +72 -26
  20. package/skills/spec-plan/references/plan_template.md +77 -0
  21. package/skills/code-subagents/references/code-quality-reviewer-prompt.md +0 -48
  22. package/skills/code-subagents/references/spec-reviewer-prompt.md +0 -62
  23. package/skills/python-architecture/SKILL.md +0 -148
  24. package/skills/python-architecture/references/data-modeling.md +0 -558
  25. package/skills/python-architecture/references/ddd.md +0 -569
  26. package/skills/python-architecture/references/functional-core.md +0 -337
  27. package/skills/python-build-tools/SKILL.md +0 -153
  28. package/skills/python-build-tools/references/basedpyright.md +0 -321
  29. package/skills/python-build-tools/references/ruff.md +0 -225
  30. package/skills/python-build-tools/references/uv.md +0 -257
  31. package/skills/python-fastapi/SKILL.md +0 -184
  32. package/skills/python-fastapi/references/api-design.md +0 -563
  33. package/skills/python-fastapi/references/dependencies.md +0 -479
  34. package/skills/python-fastapi/references/middleware.md +0 -475
  35. package/skills/python-fastapi/references/validation.md +0 -497
  36. package/skills/python-modern-python/SKILL.md +0 -201
  37. package/skills/python-modern-python/references/async.md +0 -76
  38. package/skills/python-modern-python/references/pattern-matching.md +0 -89
  39. package/skills/python-modern-python/references/typing.md +0 -96
  40. package/skills/python-monorepo/SKILL.md +0 -146
  41. package/skills/python-monorepo/references/docker.md +0 -515
  42. package/skills/python-monorepo/references/namespace-packages.md +0 -426
  43. package/skills/python-monorepo/references/workspace-config.md +0 -341
  44. package/skills/python-sqlalchemy/SKILL.md +0 -119
  45. package/skills/python-sqlalchemy/references/async.md +0 -51
  46. package/skills/python-sqlalchemy/references/models.md +0 -67
  47. package/skills/python-sqlalchemy/references/queries.md +0 -57
  48. package/skills/python-temporal/SKILL.md +0 -128
  49. package/skills/python-temporal/references/patterns.md +0 -87
  50. package/skills/python-temporal/references/testing.md +0 -54
  51. package/skills/python-testing/SKILL.md +0 -161
  52. package/skills/python-testing/references/boundaries.md +0 -449
  53. package/skills/python-testing/references/mocking.md +0 -449
  54. package/skills/python-testing/references/pytest.md +0 -524
  55. package/skills/spec-orchestrator/SKILL.md +0 -145
  56. package/skills/typescript-api-design/SKILL.md +0 -396
  57. package/skills/typescript-api-design/references/error-responses.md +0 -469
  58. package/skills/typescript-build-tools/SKILL.md +0 -447
  59. package/skills/typescript-build-tools/references/biome.md +0 -436
  60. package/skills/typescript-build-tools/references/bun.md +0 -432
  61. package/skills/typescript-build-tools/references/turborepo.md +0 -426
  62. package/skills/typescript-build-tools/references/vitest.md +0 -518
  63. package/skills/typescript-drizzle-orm/SKILL.md +0 -398
  64. package/skills/typescript-drizzle-orm/references/cloudflare.md +0 -392
  65. package/skills/typescript-drizzle-orm/references/entity-pattern.md +0 -412
  66. package/skills/typescript-drizzle-orm/references/postgresql.md +0 -191
  67. package/skills/typescript-drizzle-orm/references/repository-pattern.md +0 -479
  68. package/skills/typescript-drizzle-orm/references/sqlite.md +0 -291
  69. package/skills/typescript-dynamodb-toolbox/SKILL.md +0 -370
  70. package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +0 -574
  71. package/skills/typescript-dynamodb-toolbox/references/error-handling.md +0 -495
  72. package/skills/typescript-dynamodb-toolbox/references/modeling.md +0 -332
  73. package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +0 -631
  74. package/skills/typescript-dynamodb-toolbox/references/testing.md +0 -718
  75. package/skills/typescript-dynamodb-toolbox/references/transactions.md +0 -423
  76. package/skills/typescript-effect-ts/SKILL.md +0 -341
  77. package/skills/typescript-effect-ts/references/core.md +0 -412
  78. package/skills/typescript-effect-ts/references/error-handling.md +0 -468
  79. package/skills/typescript-effect-ts/references/resources.md +0 -476
  80. package/skills/typescript-effect-ts/references/schema.md +0 -536
  81. package/skills/typescript-effect-ts/references/services.md +0 -461
  82. package/skills/typescript-fastify/SKILL.md +0 -405
  83. package/skills/typescript-fastify/references/plugins.md +0 -329
  84. package/skills/typescript-fastify/references/typeid.md +0 -315
  85. package/skills/typescript-functional-patterns/SKILL.md +0 -402
  86. package/skills/typescript-functional-patterns/references/adts.md +0 -497
  87. package/skills/typescript-functional-patterns/references/branded-types.md +0 -542
  88. package/skills/typescript-functional-patterns/references/migration-guide.md +0 -695
  89. package/skills/typescript-functional-patterns/references/option-result.md +0 -585
  90. package/skills/typescript-testing/SKILL.md +0 -353
  91. package/skills/typescript-testing/references/mocking.md +0 -675
  92. package/skills/typescript-testing/references/msw.md +0 -852
  93. package/skills/typescript-testing/references/snapshot-testing.md +0 -774
@@ -1,468 +0,0 @@
1
- # Error Handling in Effect
2
-
3
- Effect provides comprehensive error handling with typed errors tracked in the type system. Errors are values, not exceptions, enabling exhaustive handling and composition.
4
-
5
- ## Error Types
6
-
7
- ### Expected Errors (E channel)
8
-
9
- Typed errors that are part of the function contract:
10
-
11
- ```typescript
12
- import { Effect } from "effect"
13
-
14
- class UserNotFound extends Error {
15
- readonly _tag = "UserNotFound"
16
- constructor(readonly userId: string) {
17
- super(`User not found: ${userId}`)
18
- }
19
- }
20
-
21
- class ValidationError extends Error {
22
- readonly _tag = "ValidationError"
23
- constructor(readonly field: string, readonly message: string) {
24
- super(`${field}: ${message}`)
25
- }
26
- }
27
-
28
- // Error type is tracked: Effect<User, UserNotFound | ValidationError, never>
29
- const getUser = (id: string): Effect.Effect<User, UserNotFound | ValidationError> =>
30
- Effect.gen(function* () {
31
- if (!isValidId(id)) {
32
- return yield* Effect.fail(new ValidationError("id", "Invalid format"))
33
- }
34
- const user = yield* fetchFromDb(id)
35
- if (!user) {
36
- return yield* Effect.fail(new UserNotFound(id))
37
- }
38
- return user
39
- })
40
- ```
41
-
42
- ### Defects (Unexpected Errors)
43
-
44
- Untyped errors for bugs and invariant violations:
45
-
46
- ```typescript
47
- import { Effect } from "effect"
48
-
49
- // Die creates a defect - not tracked in E type
50
- const assertPositive = (n: number) =>
51
- n > 0
52
- ? Effect.succeed(n)
53
- : Effect.die(`Expected positive number, got ${n}`)
54
-
55
- // Defects propagate but aren't in the error type
56
- const program: Effect.Effect<number> = assertPositive(-1)
57
- // Type says never fails, but will throw at runtime
58
- ```
59
-
60
- ### Tagged Error Pattern
61
-
62
- Always use `_tag` for discriminated union handling:
63
-
64
- ```typescript
65
- // Define error classes with _tag
66
- class NetworkError extends Error {
67
- readonly _tag = "NetworkError" as const
68
- constructor(readonly cause: unknown) {
69
- super("Network error")
70
- }
71
- }
72
-
73
- class TimeoutError extends Error {
74
- readonly _tag = "TimeoutError" as const
75
- constructor(readonly ms: number) {
76
- super(`Timeout after ${ms}ms`)
77
- }
78
- }
79
-
80
- class ParseError extends Error {
81
- readonly _tag = "ParseError" as const
82
- constructor(readonly input: string) {
83
- super(`Failed to parse: ${input}`)
84
- }
85
- }
86
-
87
- type FetchError = NetworkError | TimeoutError | ParseError
88
- ```
89
-
90
- ## Handling Errors
91
-
92
- ### catchAll - Handle All Errors
93
-
94
- ```typescript
95
- import { Effect } from "effect"
96
-
97
- const getUser = (id: string): Effect.Effect<User, UserNotFound> => // ...
98
-
99
- // Handle any error, must return same success type
100
- const recovered = getUser("123").pipe(
101
- Effect.catchAll((error) =>
102
- Effect.succeed({ id: "anonymous", name: "Guest" })
103
- )
104
- )
105
- // Effect<User, never, never> - error type becomes never
106
- ```
107
-
108
- ### catchTag - Handle Specific Error by Tag
109
-
110
- ```typescript
111
- import { Effect } from "effect"
112
-
113
- const fetchData = (): Effect.Effect<Data, NetworkError | ParseError> => // ...
114
-
115
- // Handle only NetworkError
116
- const withRetry = fetchData().pipe(
117
- Effect.catchTag("NetworkError", (error) =>
118
- Effect.sync(() => console.log("Retrying...")).pipe(
119
- Effect.flatMap(() => fetchData())
120
- )
121
- )
122
- )
123
- // Effect<Data, ParseError, never> - NetworkError handled
124
-
125
- // Handle multiple specific errors
126
- const handled = fetchData().pipe(
127
- Effect.catchTags({
128
- NetworkError: (e) => Effect.succeed(defaultData),
129
- ParseError: (e) => Effect.succeed(emptyData)
130
- })
131
- )
132
- // Effect<Data, never, never>
133
- ```
134
-
135
- ### catchSome - Handle Conditionally
136
-
137
- ```typescript
138
- import { Effect, Option } from "effect"
139
-
140
- const selective = effect.pipe(
141
- Effect.catchSome((error) => {
142
- if (error._tag === "Retryable") {
143
- return Option.some(retryEffect)
144
- }
145
- return Option.none() // Don't handle, propagate
146
- })
147
- )
148
- ```
149
-
150
- ### orElse - Fallback Effect
151
-
152
- ```typescript
153
- import { Effect } from "effect"
154
-
155
- // If first fails, try second
156
- const withFallback = primarySource().pipe(
157
- Effect.orElse(() => fallbackSource())
158
- )
159
-
160
- // Provide default on failure
161
- const withDefault = effect.pipe(
162
- Effect.orElseSucceed(() => defaultValue)
163
- )
164
-
165
- // Fail with different error
166
- const remapped = effect.pipe(
167
- Effect.orElseFail(() => new DifferentError())
168
- )
169
- ```
170
-
171
- ### mapError - Transform Error
172
-
173
- ```typescript
174
- import { Effect } from "effect"
175
-
176
- // Transform error type
177
- const wrapped = lowLevelOperation().pipe(
178
- Effect.mapError((error) => new HighLevelError(error))
179
- )
180
-
181
- // Add context to errors
182
- const withContext = dbQuery(sql).pipe(
183
- Effect.mapError((error) => ({
184
- ...error,
185
- query: sql,
186
- timestamp: Date.now()
187
- }))
188
- )
189
- ```
190
-
191
- ## Either and Exit
192
-
193
- ### Using Either
194
-
195
- ```typescript
196
- import { Effect, Either } from "effect"
197
-
198
- // Convert Effect to Either (errors become Left)
199
- const asEither = effect.pipe(Effect.either)
200
- // Effect<Either<E, A>, never, R>
201
-
202
- // Handle without short-circuiting
203
- const program = Effect.gen(function* () {
204
- const result = yield* Effect.either(mayFailEffect)
205
-
206
- if (Either.isLeft(result)) {
207
- console.log("Failed:", result.left)
208
- return defaultValue
209
- }
210
-
211
- return result.right
212
- })
213
- ```
214
-
215
- ### Using Exit
216
-
217
- ```typescript
218
- import { Effect, Exit } from "effect"
219
-
220
- // Get full exit status
221
- const exit = await Effect.runPromiseExit(effect)
222
-
223
- if (Exit.isSuccess(exit)) {
224
- console.log("Success:", exit.value)
225
- } else {
226
- // Cause contains full error information
227
- console.log("Failure:", Cause.pretty(exit.cause))
228
- }
229
- ```
230
-
231
- ## Cause - Rich Error Information
232
-
233
- Effect tracks not just errors but their full cause:
234
-
235
- ```typescript
236
- import { Effect, Cause } from "effect"
237
-
238
- // Cause types:
239
- // - Fail: Expected error in E channel
240
- // - Die: Unexpected defect
241
- // - Interrupt: Fiber was interrupted
242
- // - Sequential: Multiple causes in sequence
243
- // - Parallel: Multiple causes in parallel
244
-
245
- const program = effect.pipe(
246
- Effect.catchAllCause((cause) => {
247
- // Full cause tree available
248
- const failures = Cause.failures(cause) // E[]
249
- const defects = Cause.defects(cause) // unknown[]
250
-
251
- // Pretty print
252
- console.error(Cause.pretty(cause))
253
-
254
- return Effect.succeed(fallback)
255
- })
256
- )
257
- ```
258
-
259
- ## Retry Patterns
260
-
261
- ### Basic Retry
262
-
263
- ```typescript
264
- import { Effect, Schedule } from "effect"
265
-
266
- // Retry up to 3 times
267
- const retried = effect.pipe(
268
- Effect.retry(Schedule.recurs(3))
269
- )
270
-
271
- // Retry with delay
272
- const retriedWithDelay = effect.pipe(
273
- Effect.retry(
274
- Schedule.recurs(3).pipe(
275
- Schedule.addDelay(() => "1 second")
276
- )
277
- )
278
- )
279
- ```
280
-
281
- ### Exponential Backoff
282
-
283
- ```typescript
284
- import { Effect, Schedule } from "effect"
285
-
286
- const exponentialRetry = effect.pipe(
287
- Effect.retry(
288
- Schedule.exponential("100 millis").pipe(
289
- Schedule.jittered, // Add randomness
290
- Schedule.compose(Schedule.recurs(5)), // Max 5 retries
291
- Schedule.whileOutput(delay => delay < "30 seconds") // Cap delay
292
- )
293
- )
294
- )
295
- ```
296
-
297
- ### Conditional Retry
298
-
299
- ```typescript
300
- import { Effect, Schedule } from "effect"
301
-
302
- // Only retry specific errors
303
- const selectiveRetry = effect.pipe(
304
- Effect.retry({
305
- schedule: Schedule.recurs(3),
306
- while: (error) => error._tag === "Retryable"
307
- })
308
- )
309
-
310
- // Retry until condition
311
- const retryUntilSuccess = effect.pipe(
312
- Effect.retry({
313
- schedule: Schedule.spaced("1 second"),
314
- until: (error) => error._tag === "PermanentFailure"
315
- })
316
- )
317
- ```
318
-
319
- ## Timeouts
320
-
321
- ```typescript
322
- import { Effect, Duration } from "effect"
323
-
324
- // Fail with TimeoutException after duration
325
- const withTimeout = effect.pipe(
326
- Effect.timeout(Duration.seconds(30))
327
- )
328
- // Effect<A, E | TimeoutException, R>
329
-
330
- // Return Option.none on timeout
331
- const withTimeoutOption = effect.pipe(
332
- Effect.timeoutOption(Duration.seconds(30))
333
- )
334
- // Effect<Option<A>, E, R>
335
-
336
- // Provide fallback on timeout
337
- const withTimeoutFallback = effect.pipe(
338
- Effect.timeoutFail({
339
- duration: Duration.seconds(30),
340
- onTimeout: () => new CustomTimeoutError()
341
- })
342
- )
343
- ```
344
-
345
- ## Error Accumulation
346
-
347
- ### Collect All Errors
348
-
349
- ```typescript
350
- import { Effect } from "effect"
351
-
352
- const validations = [
353
- validateEmail(email),
354
- validatePassword(password),
355
- validateAge(age)
356
- ]
357
-
358
- // Stop at first error (default)
359
- const failFast = Effect.all(validations)
360
-
361
- // Collect all errors
362
- const allErrors = Effect.all(validations, { mode: "validate" })
363
- // Effect<[A, B, C], [E, E, E], R>
364
- ```
365
-
366
- ### Custom Error Accumulation
367
-
368
- ```typescript
369
- import { Effect, Either } from "effect"
370
-
371
- const validateForm = Effect.gen(function* () {
372
- const results = yield* Effect.all([
373
- Effect.either(validateEmail(email)),
374
- Effect.either(validatePassword(password)),
375
- Effect.either(validateAge(age))
376
- ])
377
-
378
- const errors = results
379
- .filter(Either.isLeft)
380
- .map(r => r.left)
381
-
382
- if (errors.length > 0) {
383
- return yield* Effect.fail(new ValidationErrors(errors))
384
- }
385
-
386
- return {
387
- email: (results[0] as Either.Right<string>).right,
388
- password: (results[1] as Either.Right<string>).right,
389
- age: (results[2] as Either.Right<number>).right
390
- }
391
- })
392
- ```
393
-
394
- ## Best Practices
395
-
396
- ### 1. Use Tagged Error Classes
397
-
398
- ```typescript
399
- // Good - tagged for catchTag
400
- class UserNotFound extends Error {
401
- readonly _tag = "UserNotFound" as const
402
- constructor(readonly userId: string) {
403
- super(`User not found: ${userId}`)
404
- }
405
- }
406
-
407
- // Avoid - generic errors
408
- const bad = Effect.fail(new Error("User not found"))
409
- ```
410
-
411
- ### 2. Be Specific with Error Types
412
-
413
- ```typescript
414
- // Good - specific error types
415
- type GetUserError = UserNotFound | DatabaseError | ValidationError
416
-
417
- const getUser = (id: string): Effect.Effect<User, GetUserError> => // ...
418
-
419
- // Avoid - union with string/Error
420
- const bad = (id: string): Effect.Effect<User, string | Error> => // ...
421
- ```
422
-
423
- ### 3. Handle Errors at Appropriate Level
424
-
425
- ```typescript
426
- // Low-level: expose specific errors
427
- const queryDb = (sql: string): Effect.Effect<Row[], DbError> => // ...
428
-
429
- // Mid-level: wrap and add context
430
- const getUser = (id: string): Effect.Effect<User, UserError> =>
431
- queryDb(`SELECT * FROM users WHERE id = '${id}'`).pipe(
432
- Effect.mapError(e => new UserError("Failed to fetch user", e))
433
- )
434
-
435
- // High-level: handle or provide fallback
436
- const getUserOrGuest = (id: string): Effect.Effect<User> =>
437
- getUser(id).pipe(
438
- Effect.catchAll(() => Effect.succeed(guestUser))
439
- )
440
- ```
441
-
442
- ### 4. Use Cause for Debugging
443
-
444
- ```typescript
445
- const program = effect.pipe(
446
- Effect.catchAllCause((cause) => {
447
- // Log full cause for debugging
448
- console.error(Cause.pretty(cause))
449
-
450
- // But expose clean error to caller
451
- return Effect.fail(new ServiceUnavailable())
452
- })
453
- )
454
- ```
455
-
456
- ### 5. Don't Swallow Defects
457
-
458
- ```typescript
459
- // Good - handle expected errors, let defects propagate
460
- const safe = effect.pipe(
461
- Effect.catchAll((error) => Effect.succeed(fallback))
462
- )
463
-
464
- // Careful - this catches defects too
465
- const dangerous = effect.pipe(
466
- Effect.catchAllCause((cause) => Effect.succeed(fallback))
467
- )
468
- ```