@martinffx/atelier 2.0.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 (81) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +94 -253
  3. package/package.json +1 -1
  4. package/skills/{spec-orchestrator → atelier-orchestrator}/SKILL.md +3 -3
  5. package/skills/atelier-setup/SKILL.md +80 -0
  6. package/skills/code-review/references/reviewers.md +3 -3
  7. package/skills/code-review/references/rq.md +6 -6
  8. package/skills/spec-brainstorm/SKILL.md +5 -5
  9. package/skills/spec-implement/SKILL.md +2 -3
  10. package/skills/spec-plan/SKILL.md +21 -29
  11. package/skills/spec-plan/references/plan_template.md +77 -0
  12. package/skills/python-architecture/SKILL.md +0 -148
  13. package/skills/python-architecture/references/data-modeling.md +0 -558
  14. package/skills/python-architecture/references/ddd.md +0 -569
  15. package/skills/python-architecture/references/functional-core.md +0 -337
  16. package/skills/python-build-tools/SKILL.md +0 -153
  17. package/skills/python-build-tools/references/basedpyright.md +0 -321
  18. package/skills/python-build-tools/references/ruff.md +0 -225
  19. package/skills/python-build-tools/references/uv.md +0 -257
  20. package/skills/python-fastapi/SKILL.md +0 -184
  21. package/skills/python-fastapi/references/api-design.md +0 -563
  22. package/skills/python-fastapi/references/dependencies.md +0 -479
  23. package/skills/python-fastapi/references/middleware.md +0 -475
  24. package/skills/python-fastapi/references/validation.md +0 -497
  25. package/skills/python-modern-python/SKILL.md +0 -201
  26. package/skills/python-modern-python/references/async.md +0 -76
  27. package/skills/python-modern-python/references/pattern-matching.md +0 -89
  28. package/skills/python-modern-python/references/typing.md +0 -96
  29. package/skills/python-monorepo/SKILL.md +0 -146
  30. package/skills/python-monorepo/references/docker.md +0 -515
  31. package/skills/python-monorepo/references/namespace-packages.md +0 -426
  32. package/skills/python-monorepo/references/workspace-config.md +0 -341
  33. package/skills/python-sqlalchemy/SKILL.md +0 -119
  34. package/skills/python-sqlalchemy/references/async.md +0 -51
  35. package/skills/python-sqlalchemy/references/models.md +0 -67
  36. package/skills/python-sqlalchemy/references/queries.md +0 -57
  37. package/skills/python-temporal/SKILL.md +0 -128
  38. package/skills/python-temporal/references/patterns.md +0 -87
  39. package/skills/python-temporal/references/testing.md +0 -54
  40. package/skills/python-testing/SKILL.md +0 -161
  41. package/skills/python-testing/references/boundaries.md +0 -449
  42. package/skills/python-testing/references/mocking.md +0 -449
  43. package/skills/python-testing/references/pytest.md +0 -524
  44. package/skills/typescript-api-design/SKILL.md +0 -396
  45. package/skills/typescript-api-design/references/error-responses.md +0 -469
  46. package/skills/typescript-build-tools/SKILL.md +0 -447
  47. package/skills/typescript-build-tools/references/biome.md +0 -436
  48. package/skills/typescript-build-tools/references/bun.md +0 -432
  49. package/skills/typescript-build-tools/references/turborepo.md +0 -426
  50. package/skills/typescript-build-tools/references/vitest.md +0 -518
  51. package/skills/typescript-drizzle-orm/SKILL.md +0 -398
  52. package/skills/typescript-drizzle-orm/references/cloudflare.md +0 -392
  53. package/skills/typescript-drizzle-orm/references/entity-pattern.md +0 -412
  54. package/skills/typescript-drizzle-orm/references/postgresql.md +0 -191
  55. package/skills/typescript-drizzle-orm/references/repository-pattern.md +0 -479
  56. package/skills/typescript-drizzle-orm/references/sqlite.md +0 -291
  57. package/skills/typescript-dynamodb-toolbox/SKILL.md +0 -370
  58. package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +0 -574
  59. package/skills/typescript-dynamodb-toolbox/references/error-handling.md +0 -495
  60. package/skills/typescript-dynamodb-toolbox/references/modeling.md +0 -332
  61. package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +0 -631
  62. package/skills/typescript-dynamodb-toolbox/references/testing.md +0 -718
  63. package/skills/typescript-dynamodb-toolbox/references/transactions.md +0 -423
  64. package/skills/typescript-effect-ts/SKILL.md +0 -341
  65. package/skills/typescript-effect-ts/references/core.md +0 -412
  66. package/skills/typescript-effect-ts/references/error-handling.md +0 -468
  67. package/skills/typescript-effect-ts/references/resources.md +0 -476
  68. package/skills/typescript-effect-ts/references/schema.md +0 -536
  69. package/skills/typescript-effect-ts/references/services.md +0 -461
  70. package/skills/typescript-fastify/SKILL.md +0 -405
  71. package/skills/typescript-fastify/references/plugins.md +0 -329
  72. package/skills/typescript-fastify/references/typeid.md +0 -315
  73. package/skills/typescript-functional-patterns/SKILL.md +0 -402
  74. package/skills/typescript-functional-patterns/references/adts.md +0 -497
  75. package/skills/typescript-functional-patterns/references/branded-types.md +0 -542
  76. package/skills/typescript-functional-patterns/references/migration-guide.md +0 -695
  77. package/skills/typescript-functional-patterns/references/option-result.md +0 -585
  78. package/skills/typescript-testing/SKILL.md +0 -353
  79. package/skills/typescript-testing/references/mocking.md +0 -675
  80. package/skills/typescript-testing/references/msw.md +0 -852
  81. package/skills/typescript-testing/references/snapshot-testing.md +0 -774
@@ -1,495 +0,0 @@
1
- # Error Handling Patterns
2
-
3
- Comprehensive error handling for DynamoDB operations with domain error mapping.
4
-
5
- ## DynamoDB Error Types
6
-
7
- ### ConditionalCheckFailedException
8
-
9
- Thrown when a conditional expression evaluates to false.
10
-
11
- **Common Causes:**
12
- - Create operation: Item already exists (`exists: false` failed)
13
- - Update operation: Item doesn't exist (`exists: true` failed)
14
- - Custom conditions: Business rule validation failed
15
-
16
- **From AWS SDK:**
17
- ```typescript
18
- import { ConditionalCheckFailedException } from '@aws-sdk/client-dynamodb'
19
- ```
20
-
21
- ### DynamoDBToolboxError
22
-
23
- Thrown by dynamodb-toolbox for schema validation errors.
24
-
25
- **Common Causes:**
26
- - Required field missing
27
- - Type mismatch (string vs number)
28
- - Validation function returned false
29
- - Invalid attribute name
30
-
31
- **Properties:**
32
- - `path`: Field path that failed validation
33
- - `message`: Error description
34
-
35
- ### TransactionCanceledException
36
-
37
- Thrown when a transaction fails.
38
-
39
- **Key Property:**
40
- - `CancellationReasons`: Array explaining why each operation failed
41
-
42
- ## Domain Error Classes
43
-
44
- Define domain-specific errors that clients can handle meaningfully.
45
-
46
- ```typescript
47
- // src/shared/errors.ts
48
-
49
- export class DuplicateEntityError extends Error {
50
- public readonly entityType: string
51
- public readonly entityKey: string
52
-
53
- constructor(entityType: string, entityKey: string) {
54
- super(`${entityType} '${entityKey}' already exists`)
55
- this.name = "DuplicateEntityError"
56
- this.entityType = entityType
57
- this.entityKey = entityKey
58
- }
59
- }
60
-
61
- export class EntityNotFoundError extends Error {
62
- public readonly entityType: string
63
- public readonly entityKey: string
64
-
65
- constructor(entityType: string, entityKey: string) {
66
- super(`${entityType} '${entityKey}' not found`)
67
- this.name = "EntityNotFoundError"
68
- this.entityType = entityType
69
- this.entityKey = entityKey
70
- }
71
- }
72
-
73
- export class ValidationError extends Error {
74
- public readonly field: string
75
-
76
- constructor(field: string, message: string) {
77
- super(`Validation failed for '${field}': ${message}`)
78
- this.name = "ValidationError"
79
- this.field = field
80
- }
81
- }
82
- ```
83
-
84
- ## Create Operation Error Handling
85
-
86
- **Context:** Creating a new entity with duplicate prevention
87
-
88
- ```typescript
89
- import { PutItemCommand } from 'dynamodb-toolbox'
90
- import { ConditionalCheckFailedException } from '@aws-sdk/client-dynamodb'
91
- import { DynamoDBToolboxError } from 'dynamodb-toolbox'
92
-
93
- async create(user: UserEntity): Promise<UserEntity> {
94
- try {
95
- const result = await this.entity
96
- .build(PutItemCommand)
97
- .item(user.toRecord())
98
- .options({
99
- condition: { attr: "PK", exists: false } // Prevent duplicates
100
- })
101
- .send()
102
-
103
- return UserEntity.fromRecord(result.ToolboxItem)
104
- } catch (error: unknown) {
105
- // Pattern: ConditionalCheckFailed → DuplicateEntityError
106
- if (error instanceof ConditionalCheckFailedException) {
107
- throw new DuplicateEntityError("UserEntity", user.username)
108
- }
109
-
110
- // Pattern: Schema validation → ValidationError
111
- if (error instanceof DynamoDBToolboxError) {
112
- throw new ValidationError(
113
- error.path ?? "entity",
114
- error.message
115
- )
116
- }
117
-
118
- // Unknown errors: re-throw
119
- throw error
120
- }
121
- }
122
- ```
123
-
124
- **Key Points:**
125
- - `ConditionalCheckFailedException` → Item already exists
126
- - Map to `DuplicateEntityError` with entity type and key
127
- - Client can catch and show user-friendly message: "Username 'alice' already taken"
128
-
129
- ## Update Operation Error Handling
130
-
131
- **Context:** Updating an existing entity with existence check
132
-
133
- ```typescript
134
- async update(user: UserEntity): Promise<UserEntity> {
135
- try {
136
- const result = await this.entity
137
- .build(PutItemCommand)
138
- .item(user.toRecord())
139
- .options({
140
- condition: { attr: "PK", exists: true } // Must exist
141
- })
142
- .send()
143
-
144
- return UserEntity.fromRecord(result.ToolboxItem)
145
- } catch (error: unknown) {
146
- // Pattern: ConditionalCheckFailed → EntityNotFoundError
147
- if (error instanceof ConditionalCheckFailedException) {
148
- throw new EntityNotFoundError("UserEntity", user.username)
149
- }
150
-
151
- if (error instanceof DynamoDBToolboxError) {
152
- throw new ValidationError(error.path ?? "entity", error.message)
153
- }
154
-
155
- throw error
156
- }
157
- }
158
- ```
159
-
160
- **Same Exception, Different Meaning:**
161
- - Create: `ConditionalCheckFailed` → "already exists"
162
- - Update: `ConditionalCheckFailed` → "not found"
163
-
164
- Context determines the domain error mapping.
165
-
166
- ## Transaction Error Handling
167
-
168
- **Context:** Multi-entity transaction (create issue, check repo exists)
169
-
170
- ```typescript
171
- import { TransactionCanceledException } from '@aws-sdk/client-dynamodb'
172
-
173
- async createIssue(issue: IssueEntity): Promise<IssueEntity> {
174
- try {
175
- // Transaction: PutTransaction + ConditionCheck
176
- await execute(putIssueTransaction, repoCheckTransaction)
177
-
178
- return await this.get(/* ... */)
179
- } catch (error: unknown) {
180
- handleTransactionError(error, {
181
- entityType: "IssueEntity",
182
- entityKey: issue.getEntityKey(),
183
- parentEntityType: "RepositoryEntity",
184
- parentEntityKey: issue.getParentEntityKey(),
185
- operationName: "issue",
186
- })
187
- }
188
- }
189
- ```
190
-
191
- ### Transaction Error Handler
192
-
193
- ```typescript
194
- function handleTransactionError(
195
- error: unknown,
196
- options: {
197
- entityType: string
198
- entityKey: string
199
- parentEntityType: string
200
- parentEntityKey: string
201
- operationName: string
202
- }
203
- ): never {
204
- if (error instanceof TransactionCanceledException) {
205
- const reasons = error.CancellationReasons || []
206
-
207
- // Validate we have expected number of cancellation reasons
208
- if (reasons.length < 2) {
209
- throw new ValidationError(
210
- "transaction",
211
- `Transaction failed with unexpected cancellation reason count: ${reasons.length}`
212
- )
213
- }
214
-
215
- // Index 0: The entity create/put operation
216
- if (reasons[0]?.Code === "ConditionalCheckFailed") {
217
- throw new DuplicateEntityError(options.entityType, options.entityKey)
218
- }
219
-
220
- // Index 1: The parent existence check
221
- if (reasons[1]?.Code === "ConditionalCheckFailed") {
222
- throw new EntityNotFoundError(
223
- options.parentEntityType,
224
- options.parentEntityKey
225
- )
226
- }
227
-
228
- // Unknown transaction failure
229
- throw new ValidationError(
230
- options.operationName,
231
- `Failed to create ${options.operationName} due to transaction conflict`
232
- )
233
- }
234
-
235
- if (error instanceof DynamoDBToolboxError) {
236
- throw new ValidationError(error.path ?? "entity", error.message)
237
- }
238
-
239
- throw error
240
- }
241
- ```
242
-
243
- **CancellationReasons Structure:**
244
- ```typescript
245
- [
246
- { Code: "ConditionalCheckFailed" }, // Index 0: PutTransaction failed
247
- { Code: "ConditionalCheckFailed" }, // Index 1: ConditionCheck failed
248
- ]
249
- ```
250
-
251
- **Mapping:**
252
- - `reasons[0]` failed → Entity already exists (DuplicateEntityError)
253
- - `reasons[1]` failed → Parent doesn't exist (EntityNotFoundError)
254
-
255
- **Order Matters:** CancellationReasons array matches the order operations were passed to `execute()`.
256
-
257
- ## Three-Way Transaction Error Handling
258
-
259
- **Context:** Create star with user + repo validation
260
-
261
- ```typescript
262
- async createStar(star: StarEntity): Promise<StarEntity> {
263
- try {
264
- // Transaction: PutTransaction + UserCheck + RepoCheck
265
- await execute(putStarTransaction, userCheck, repoCheck)
266
-
267
- return await this.get(/* ... */)
268
- } catch (error: unknown) {
269
- handleThreeWayTransactionError(error, star)
270
- }
271
- }
272
-
273
- function handleThreeWayTransactionError(
274
- error: unknown,
275
- star: StarEntity
276
- ): never {
277
- if (error instanceof TransactionCanceledException) {
278
- const reasons = error.CancellationReasons || []
279
-
280
- if (reasons.length < 3) {
281
- throw new ValidationError(
282
- "transaction",
283
- `Expected 3 cancellation reasons, got ${reasons.length}`
284
- )
285
- }
286
-
287
- // Index 0: Star creation
288
- if (reasons[0]?.Code === "ConditionalCheckFailed") {
289
- throw new DuplicateEntityError(
290
- "StarEntity",
291
- star.getEntityKey()
292
- )
293
- }
294
-
295
- // Index 1: User existence check
296
- if (reasons[1]?.Code === "ConditionalCheckFailed") {
297
- throw new EntityNotFoundError("UserEntity", star.username)
298
- }
299
-
300
- // Index 2: Repo existence check
301
- if (reasons[2]?.Code === "ConditionalCheckFailed") {
302
- throw new EntityNotFoundError(
303
- "RepositoryEntity",
304
- `${star.repoOwner}/${star.repoName}`
305
- )
306
- }
307
-
308
- throw new ValidationError(
309
- "star",
310
- "Failed to create star due to transaction conflict"
311
- )
312
- }
313
-
314
- throw error
315
- }
316
- ```
317
-
318
- ## Reusable Error Handlers
319
-
320
- Create utility functions for common patterns:
321
-
322
- ```typescript
323
- // src/repos/utils.ts
324
-
325
- export function handleCreateError(
326
- error: unknown,
327
- entityType: string,
328
- entityKey: string
329
- ): never {
330
- if (error instanceof ConditionalCheckFailedException) {
331
- throw new DuplicateEntityError(entityType, entityKey)
332
- }
333
- if (error instanceof DynamoDBToolboxError) {
334
- throw new ValidationError(error.path ?? "entity", error.message)
335
- }
336
- throw error
337
- }
338
-
339
- export function handleUpdateError(
340
- error: unknown,
341
- entityType: string,
342
- entityKey: string
343
- ): never {
344
- if (error instanceof ConditionalCheckFailedException) {
345
- throw new EntityNotFoundError(entityType, entityKey)
346
- }
347
- if (error instanceof DynamoDBToolboxError) {
348
- throw new ValidationError(error.path ?? "entity", error.message)
349
- }
350
- throw error
351
- }
352
- ```
353
-
354
- **Usage:**
355
-
356
- ```typescript
357
- async create(user: UserEntity): Promise<UserEntity> {
358
- try {
359
- const result = await this.entity
360
- .build(PutItemCommand)
361
- .item(user.toRecord())
362
- .options({ condition: { attr: "PK", exists: false } })
363
- .send()
364
-
365
- return UserEntity.fromRecord(result.ToolboxItem)
366
- } catch (error: unknown) {
367
- handleCreateError(error, "UserEntity", user.username)
368
- }
369
- }
370
- ```
371
-
372
- ## Testing Error Handling
373
-
374
- ### Test Duplicate Creation
375
-
376
- ```typescript
377
- it("should throw DuplicateEntityError for duplicate username", async () => {
378
- const user1 = createUserEntity({ username: "alice" })
379
- const user2 = createUserEntity({ username: "alice" })
380
-
381
- await userRepo.create(user1)
382
-
383
- await expect(userRepo.create(user2)).rejects.toThrow(DuplicateEntityError)
384
- await expect(userRepo.create(user2)).rejects.toThrow("already exists")
385
- })
386
- ```
387
-
388
- ### Test Update Non-Existent
389
-
390
- ```typescript
391
- it("should throw EntityNotFoundError when updating non-existent user", async () => {
392
- const user = createUserEntity({ username: "nonexistent" })
393
-
394
- await expect(userRepo.update(user)).rejects.toThrow(EntityNotFoundError)
395
- await expect(userRepo.update(user)).rejects.toThrow("not found")
396
- })
397
- ```
398
-
399
- ### Test Transaction Validation
400
-
401
- ```typescript
402
- it("should throw EntityNotFoundError when creating issue for non-existent repo", async () => {
403
- const issue = IssueEntity.fromRequest({
404
- owner: "alice",
405
- repo_name: "nonexistent",
406
- title: "Test Issue",
407
- author: "bob",
408
- })
409
-
410
- await expect(issueRepo.create(issue)).rejects.toThrow(EntityNotFoundError)
411
- await expect(issueRepo.create(issue)).rejects.toThrow("RepositoryEntity")
412
- })
413
- ```
414
-
415
- ## Error Response Formatting (API Layer)
416
-
417
- Convert domain errors to HTTP responses:
418
-
419
- ```typescript
420
- // src/routes/users.ts (Fastify example)
421
-
422
- app.post("/users", async (request, reply) => {
423
- try {
424
- const user = UserEntity.fromRequest(request.body)
425
- const created = await userService.create(user)
426
- return reply.status(201).send(created.toResponse())
427
- } catch (error) {
428
- if (error instanceof DuplicateEntityError) {
429
- return reply.status(409).send({
430
- error: "Conflict",
431
- message: error.message,
432
- entityType: error.entityType,
433
- entityKey: error.entityKey,
434
- })
435
- }
436
-
437
- if (error instanceof ValidationError) {
438
- return reply.status(400).send({
439
- error: "Bad Request",
440
- message: error.message,
441
- field: error.field,
442
- })
443
- }
444
-
445
- if (error instanceof EntityNotFoundError) {
446
- return reply.status(404).send({
447
- error: "Not Found",
448
- message: error.message,
449
- })
450
- }
451
-
452
- // Unknown errors: 500
453
- console.error("Unexpected error:", error)
454
- return reply.status(500).send({
455
- error: "Internal Server Error",
456
- message: "An unexpected error occurred",
457
- })
458
- }
459
- })
460
- ```
461
-
462
- **HTTP Status Code Mapping:**
463
- - `DuplicateEntityError` → 409 Conflict
464
- - `EntityNotFoundError` → 404 Not Found
465
- - `ValidationError` → 400 Bad Request
466
- - Unknown → 500 Internal Server Error
467
-
468
- ## Best Practices
469
-
470
- ### ✓ Do
471
-
472
- - Always catch `ConditionalCheckFailedException` and map to domain errors
473
- - Include entity type and key in error messages
474
- - Inspect `CancellationReasons` array order for transactions
475
- - Log unexpected errors before re-throwing
476
- - Test error handling paths explicitly
477
-
478
- ### ✗ Don't
479
-
480
- - Don't expose DynamoDB error details to clients
481
- - Don't assume transaction error order - always check CancellationReasons
482
- - Don't ignore `DynamoDBToolboxError` - it indicates schema issues
483
- - Don't retry blindly on transaction errors (may have partial state)
484
-
485
- ## Error Handling Checklist
486
-
487
- For each repository operation:
488
-
489
- - [ ] **Create operations** - Catch `ConditionalCheckFailed` → `DuplicateEntityError`
490
- - [ ] **Update operations** - Catch `ConditionalCheckFailed` → `EntityNotFoundError`
491
- - [ ] **Transactions** - Parse `CancellationReasons` by index
492
- - [ ] **Schema validation** - Catch `DynamoDBToolboxError` → `ValidationError`
493
- - [ ] **Unknown errors** - Re-throw with logging
494
- - [ ] **API layer** - Map domain errors to HTTP status codes
495
- - [ ] **Tests** - Verify error handling for each failure scenario