@jarrodmedrano/claude-skills 1.0.3 → 1.0.4

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 (26) hide show
  1. package/.claude/skills/bevy/SKILL.md +406 -0
  2. package/.claude/skills/bevy/references/bevy_specific_tips.md +385 -0
  3. package/.claude/skills/bevy/references/common_pitfalls.md +217 -0
  4. package/.claude/skills/bevy/references/ecs_patterns.md +277 -0
  5. package/.claude/skills/bevy/references/project_structure.md +116 -0
  6. package/.claude/skills/bevy/references/ui_development.md +147 -0
  7. package/.claude/skills/domain-driven-design/SKILL.md +459 -0
  8. package/.claude/skills/domain-driven-design/references/ddd_foundations_and_patterns.md +664 -0
  9. package/.claude/skills/domain-driven-design/references/rich_hickey_principles.md +406 -0
  10. package/.claude/skills/domain-driven-design/references/visualization_examples.md +790 -0
  11. package/.claude/skills/domain-driven-design/references/wlaschin_patterns.md +639 -0
  12. package/.claude/skills/godot/SKILL.md +728 -0
  13. package/.claude/skills/godot/assets/templates/attribute_template.gd +109 -0
  14. package/.claude/skills/godot/assets/templates/component_template.gd +76 -0
  15. package/.claude/skills/godot/assets/templates/interaction_template.gd +108 -0
  16. package/.claude/skills/godot/assets/templates/item_resource.tres +11 -0
  17. package/.claude/skills/godot/assets/templates/spell_resource.tres +20 -0
  18. package/.claude/skills/godot/references/architecture-patterns.md +608 -0
  19. package/.claude/skills/godot/references/common-pitfalls.md +518 -0
  20. package/.claude/skills/godot/references/file-formats.md +491 -0
  21. package/.claude/skills/godot/references/godot4-physics-api.md +302 -0
  22. package/.claude/skills/godot/scripts/validate_tres.py +145 -0
  23. package/.claude/skills/godot/scripts/validate_tscn.py +170 -0
  24. package/.claude/skills/react-three-fiber/SKILL.md +2055 -0
  25. package/.claude/skills/react-three-fiber/scripts/build-scene.ts +171 -0
  26. package/package.json +1 -1
@@ -0,0 +1,639 @@
1
+ # Scott Wlaschin's Type-Driven Design Patterns
2
+
3
+ This reference compiles patterns and principles from Scott Wlaschin's work on domain modeling, functional architecture, and type-driven design.
4
+
5
+ ## Core Philosophy
6
+
7
+ **"Make Illegal States Unrepresentable"**
8
+
9
+ Use the type system to eliminate entire categories of bugs at compile time. If a state shouldn't exist in your domain, make it impossible to construct.
10
+
11
+ ## Domain Modeling Made Functional
12
+
13
+ ### Understanding the Domain
14
+
15
+ **Key Questions:**
16
+ 1. What are the inputs and outputs?
17
+ 2. What can happen? (scenarios, workflows)
18
+ 3. What can go wrong? (errors, edge cases)
19
+ 4. What are the business rules and constraints?
20
+ 5. What are the invariants that must always hold?
21
+
22
+ **Process:**
23
+ 1. Talk to domain experts using their language
24
+ 2. Document workflows as transformations
25
+ 3. Identify the things (nouns) and actions (verbs)
26
+ 4. Model the lifecycle and state transitions
27
+ 5. Capture rules and constraints as types
28
+
29
+ ### Workflows as Pipelines
30
+
31
+ Model business workflows as data transformation pipelines:
32
+
33
+ ```
34
+ Input → Validate → Execute Business Logic → Persist → Output
35
+ ```
36
+
37
+ Each step:
38
+ - Takes data as input
39
+ - Performs a transformation
40
+ - Produces data as output
41
+ - May fail (use Result types)
42
+
43
+ **Benefits:**
44
+ - Clear separation of concerns
45
+ - Easy to test each step
46
+ - Easy to compose steps
47
+ - Makes the happy path obvious
48
+
49
+ ### Example: Order Placement Workflow
50
+
51
+ ```
52
+ UnvalidatedOrder
53
+ → ValidateOrder
54
+ → ValidatedOrder
55
+ → PriceOrder
56
+ → PricedOrder
57
+ → PlaceOrder
58
+ → PlacedOrderEvent
59
+ ```
60
+
61
+ Each arrow is a function. Each type in between represents a distinct state with its own invariants.
62
+
63
+ ## Type-Driven Design
64
+
65
+ ### Making Illegal States Unrepresentable
66
+
67
+ **Problem:** Optional fields that create invalid combinations
68
+
69
+ **Bad:**
70
+ ```typescript
71
+ type Order = {
72
+ id: string;
73
+ // Both can be null, or both can be set - illegal!
74
+ approvedAt: Date | null;
75
+ rejectedAt: Date | null;
76
+ }
77
+ ```
78
+
79
+ **Good:**
80
+ ```typescript
81
+ type Order = {
82
+ id: OrderId;
83
+ status:
84
+ | { type: "Pending" }
85
+ | { type: "Approved"; approvedAt: Date }
86
+ | { type: "Rejected"; rejectedAt: Date };
87
+ }
88
+ ```
89
+
90
+ Now impossible to be both approved and rejected, or to have dates without corresponding status.
91
+
92
+ ### Constrained Types
93
+
94
+ Create types that can only hold valid values:
95
+
96
+ **Bad:** Primitive obsession
97
+ ```typescript
98
+ function createUser(email: string, age: number) { ... }
99
+ // Can pass invalid values: createUser("not-an-email", -5)
100
+ ```
101
+
102
+ **Good:** Constrained types
103
+ ```typescript
104
+ type EmailAddress = EmailAddress & { __brand: "EmailAddress" };
105
+ type Age = Age & { __brand: "Age" };
106
+
107
+ function createEmailAddress(s: string): Result<EmailAddress, ValidationError> {
108
+ if (isValidEmail(s)) return Ok(s as EmailAddress);
109
+ return Err({ error: "Invalid email format" });
110
+ }
111
+
112
+ function createAge(n: number): Result<Age, ValidationError> {
113
+ if (n >= 0 && n <= 150) return Ok(n as Age);
114
+ return Err({ error: "Age must be between 0 and 150" });
115
+ }
116
+
117
+ function createUser(email: EmailAddress, age: Age) { ... }
118
+ // Can only pass validated values!
119
+ ```
120
+
121
+ ### Single Case Unions (Wrapper Types)
122
+
123
+ Use wrapper types to give semantic meaning to primitives:
124
+
125
+ ```typescript
126
+ type CustomerId = { readonly value: string };
127
+ type ProductId = { readonly value: string };
128
+ type OrderId = { readonly value: string };
129
+
130
+ // Now cannot confuse these:
131
+ function getCustomer(id: CustomerId): Customer { ... }
132
+ // getCustomer(productId) // Type error!
133
+ ```
134
+
135
+ **Benefits:**
136
+ - Type safety
137
+ - Self-documenting code
138
+ - Cannot confuse similar primitives
139
+ - Compiler catches errors
140
+
141
+ ### Exhaustive Pattern Matching
142
+
143
+ Use discriminated unions and let the compiler ensure you handle all cases:
144
+
145
+ ```typescript
146
+ type PaymentMethod =
147
+ | { type: "CreditCard"; cardNumber: string; cvv: string }
148
+ | { type: "PayPal"; email: EmailAddress }
149
+ | { type: "BankTransfer"; accountNumber: string; routingNumber: string };
150
+
151
+ function processPayment(method: PaymentMethod): Result<Receipt, PaymentError> {
152
+ switch (method.type) {
153
+ case "CreditCard":
154
+ return processCreditCard(method.cardNumber, method.cvv);
155
+ case "PayPal":
156
+ return processPayPal(method.email);
157
+ case "BankTransfer":
158
+ return processBankTransfer(method.accountNumber, method.routingNumber);
159
+ // If we add a new payment method, compiler will error here
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### States and Transitions
165
+
166
+ Model entity states explicitly, not as flags:
167
+
168
+ **Bad:**
169
+ ```typescript
170
+ type Order = {
171
+ isPaid: boolean;
172
+ isShipped: boolean;
173
+ isCancelled: boolean;
174
+ // Can be paid and cancelled? Shipped but not paid?
175
+ }
176
+ ```
177
+
178
+ **Good:**
179
+ ```typescript
180
+ type Order =
181
+ | { state: "Unpaid"; items: OrderLine[] }
182
+ | { state: "Paid"; items: OrderLine[]; paidAt: Date; paymentMethod: PaymentMethod }
183
+ | { state: "Shipped"; items: OrderLine[]; paidAt: Date; shippedAt: Date; trackingNumber: string }
184
+ | { state: "Cancelled"; reason: string };
185
+ ```
186
+
187
+ Now impossible to be in multiple states or to be shipped without being paid.
188
+
189
+ ## Railway-Oriented Programming
190
+
191
+ ### The Problem
192
+
193
+ Functions that can fail complicate the happy path:
194
+
195
+ ```typescript
196
+ function placeOrder(unvalidatedOrder: UnvalidatedOrder) {
197
+ const validatedOrder = validateOrder(unvalidatedOrder);
198
+ if (validatedOrder.isError) return validatedOrder.error;
199
+
200
+ const pricedOrder = priceOrder(validatedOrder.value);
201
+ if (pricedOrder.isError) return pricedOrder.error;
202
+
203
+ const placedOrder = saveOrder(pricedOrder.value);
204
+ if (placedOrder.isError) return placedOrder.error;
205
+
206
+ return placedOrder.value;
207
+ }
208
+ ```
209
+
210
+ **Problem:** Error handling obscures the happy path.
211
+
212
+ ### Result Type
213
+
214
+ Model success and failure explicitly:
215
+
216
+ ```typescript
217
+ type Result<T, E> =
218
+ | { ok: true; value: T }
219
+ | { ok: false; error: E };
220
+ ```
221
+
222
+ ### Chaining with bind/flatMap
223
+
224
+ Chain operations that return Results:
225
+
226
+ ```typescript
227
+ function placeOrder(unvalidatedOrder: UnvalidatedOrder): Result<PlacedOrder, OrderError> {
228
+ return validateOrder(unvalidatedOrder)
229
+ .flatMap(priceOrder)
230
+ .flatMap(saveOrder);
231
+ }
232
+ ```
233
+
234
+ **The Railway Metaphor:**
235
+ - Two tracks: Success and Failure
236
+ - Functions switch from success to failure track on error
237
+ - Once on failure track, stay on failure track
238
+ - Clear separation: happy path is just composition
239
+
240
+ ### Combining Results
241
+
242
+ When you need multiple independent validations:
243
+
244
+ ```typescript
245
+ function createUser(
246
+ emailStr: string,
247
+ ageNum: number,
248
+ nameStr: string
249
+ ): Result<User, ValidationErrors> {
250
+ const email = createEmail(emailStr);
251
+ const age = createAge(ageNum);
252
+ const name = createName(nameStr);
253
+
254
+ // Collect all errors, not just first
255
+ return combineResults([email, age, name], (email, age, name) => ({
256
+ email,
257
+ age,
258
+ name
259
+ }));
260
+ }
261
+ ```
262
+
263
+ ### Handling Errors at Boundaries
264
+
265
+ Keep the domain pure; handle effects at edges:
266
+
267
+ **Inside domain (pure):**
268
+ ```typescript
269
+ function validateOrder(order: UnvalidatedOrder): Result<ValidatedOrder, ValidationError> {
270
+ // Pure validation logic, no IO
271
+ }
272
+ ```
273
+
274
+ **At boundary (effects):**
275
+ ```typescript
276
+ async function handleOrderRequest(req: Request): Promise<Response> {
277
+ const result = validateOrder(req.body)
278
+ .flatMap(priceOrder)
279
+ .flatMap(saveOrder); // IO happens here
280
+
281
+ if (result.ok) {
282
+ return { status: 200, body: result.value };
283
+ } else {
284
+ return { status: 400, body: { error: result.error } };
285
+ }
286
+ }
287
+ ```
288
+
289
+ ## Designing with Types
290
+
291
+ ### Start with the Types
292
+
293
+ Before writing any implementation:
294
+
295
+ 1. **Define the types** that represent domain concepts
296
+ 2. **Define the function signatures** (inputs and outputs)
297
+ 3. **Implement the functions** (fill in the logic)
298
+
299
+ **Benefits:**
300
+ - Types guide implementation
301
+ - Types serve as documentation
302
+ - Types catch mismatches early
303
+ - Refactoring is safer
304
+
305
+ ### Example: Designing a Discount System
306
+
307
+ **Step 1: Define domain types**
308
+ ```typescript
309
+ type Product = { id: ProductId; price: Money; category: Category };
310
+ type Customer = { id: CustomerId; membershipLevel: MembershipLevel };
311
+ type Category = "Electronics" | "Clothing" | "Books";
312
+ type MembershipLevel = "Bronze" | "Silver" | "Gold";
313
+
314
+ type DiscountRule =
315
+ | { type: "Percentage"; percentage: number }
316
+ | { type: "FixedAmount"; amount: Money }
317
+ | { type: "BuyXGetY"; buyQuantity: number; getQuantity: number };
318
+
319
+ type Discount = {
320
+ rule: DiscountRule;
321
+ applicableTo: Category[];
322
+ minimumMembershipLevel: MembershipLevel | null;
323
+ };
324
+ ```
325
+
326
+ **Step 2: Define function signatures**
327
+ ```typescript
328
+ function calculateDiscount(
329
+ product: Product,
330
+ quantity: number,
331
+ customer: Customer,
332
+ discounts: Discount[]
333
+ ): Money;
334
+
335
+ function findApplicableDiscounts(
336
+ product: Product,
337
+ customer: Customer,
338
+ discounts: Discount[]
339
+ ): Discount[];
340
+
341
+ function applyDiscount(
342
+ price: Money,
343
+ quantity: number,
344
+ discount: Discount
345
+ ): Money;
346
+ ```
347
+
348
+ **Step 3: Implement** (signatures guide what's needed)
349
+
350
+ ### Use Types to Model Business Rules
351
+
352
+ Encode business rules in types:
353
+
354
+ **Rule:** "An order must have at least one item"
355
+
356
+ ```typescript
357
+ type NonEmptyList<T> = {
358
+ head: T;
359
+ tail: T[];
360
+ };
361
+
362
+ type Order = {
363
+ id: OrderId;
364
+ items: NonEmptyList<OrderLine>; // Cannot be empty!
365
+ };
366
+ ```
367
+
368
+ **Rule:** "Refunds require a reason if amount exceeds $100"
369
+
370
+ ```typescript
371
+ type Refund =
372
+ | { amount: Money; reason: null } // reason not needed
373
+ | { amount: Money; reason: string }; // reason required
374
+
375
+ // Factory function enforces rule:
376
+ function createRefund(amount: Money, reason: string | null): Refund {
377
+ if (amount.value > 100 && reason === null) {
378
+ throw new Error("Refund over $100 requires a reason");
379
+ }
380
+ return { amount, reason };
381
+ }
382
+ ```
383
+
384
+ ## Functional Architecture Patterns
385
+
386
+ ### Onion/Hexagonal Architecture
387
+
388
+ **Layers (inside-out):**
389
+ 1. **Domain** - Pure business logic, no dependencies
390
+ 2. **Application** - Workflows, orchestration, still pure
391
+ 3. **Infrastructure** - IO, databases, external services
392
+
393
+ **Dependency Rule:** Outer layers depend on inner layers, never reverse.
394
+
395
+ **Domain Layer:**
396
+ - Pure functions
397
+ - Domain types
398
+ - Business rules
399
+ - No IO, no frameworks
400
+
401
+ **Application Layer:**
402
+ - Composes domain functions into workflows
403
+ - Still mostly pure
404
+ - Defines interfaces (ports) for infrastructure
405
+
406
+ **Infrastructure Layer:**
407
+ - Implements ports (adapters)
408
+ - Handles IO (database, HTTP, file system)
409
+ - Deals with frameworks and libraries
410
+
411
+ ### Dependency Injection via Function Parameters
412
+
413
+ Pass dependencies as function parameters:
414
+
415
+ ```typescript
416
+ // Domain function defines what it needs
417
+ function placeOrder(
418
+ validateAddress: (address: Address) => Result<ValidatedAddress, ValidationError>,
419
+ checkInventory: (productId: ProductId) => Promise<boolean>,
420
+ saveOrder: (order: Order) => Promise<Result<void, DbError>>,
421
+ order: UnvalidatedOrder
422
+ ): Promise<Result<OrderPlaced, OrderError>> {
423
+ // Implementation uses provided functions
424
+ }
425
+
426
+ // At composition root, provide implementations:
427
+ const result = await placeOrder(
428
+ addressValidator.validate,
429
+ inventory.check,
430
+ orderRepository.save,
431
+ incomingOrder
432
+ );
433
+ ```
434
+
435
+ **Benefits:**
436
+ - No hidden dependencies
437
+ - Easy to test (pass mock functions)
438
+ - Explicit about requirements
439
+ - No magic or DI container
440
+
441
+ ### Command/Query Separation
442
+
443
+ **Commands:** Change state, return void or Result<void, Error>
444
+ - `placeOrder(order): Result<void, OrderError>`
445
+ - `cancelSubscription(id): Result<void, CancellationError>`
446
+ - `updateProfile(profile): Result<void, ValidationError>`
447
+
448
+ **Queries:** Read state, return data, never change state
449
+ - `getOrder(id): Option<Order>`
450
+ - `findCustomersByEmail(email): Customer[]`
451
+ - `getTotalRevenue(): Money`
452
+
453
+ **Benefits:**
454
+ - Clear separation of reads and writes
455
+ - Easier to optimize (cache queries)
456
+ - Easier to reason about (commands have effects, queries don't)
457
+
458
+ ### Event Sourcing Pattern
459
+
460
+ Instead of storing current state, store sequence of events:
461
+
462
+ ```typescript
463
+ type OrderEvent =
464
+ | { type: "OrderPlaced"; orderId: OrderId; items: OrderLine[]; at: Timestamp }
465
+ | { type: "OrderPaid"; orderId: OrderId; amount: Money; at: Timestamp }
466
+ | { type: "OrderShipped"; orderId: OrderId; trackingNumber: string; at: Timestamp }
467
+ | { type: "OrderCancelled"; orderId: OrderId; reason: string; at: Timestamp };
468
+
469
+ function applyEvent(state: Order | null, event: OrderEvent): Order {
470
+ switch (event.type) {
471
+ case "OrderPlaced":
472
+ return { id: event.orderId, items: event.items, status: "Placed" };
473
+ case "OrderPaid":
474
+ return { ...state!, status: "Paid" };
475
+ case "OrderShipped":
476
+ return { ...state!, status: "Shipped", trackingNumber: event.trackingNumber };
477
+ case "OrderCancelled":
478
+ return { ...state!, status: "Cancelled", reason: event.reason };
479
+ }
480
+ }
481
+
482
+ function reconstruct(events: OrderEvent[]): Order {
483
+ return events.reduce(applyEvent, null)!;
484
+ }
485
+ ```
486
+
487
+ **Benefits:**
488
+ - Complete history
489
+ - Audit trail
490
+ - Can replay to any point in time
491
+ - Events are facts (immutable)
492
+
493
+ ## Practical Patterns
494
+
495
+ ### Option Type for Missing Values
496
+
497
+ Don't use null/undefined for domain concepts:
498
+
499
+ ```typescript
500
+ type Option<T> = { some: true; value: T } | { some: false };
501
+
502
+ function findCustomer(id: CustomerId): Option<Customer> {
503
+ // ...
504
+ }
505
+
506
+ // Forces handling:
507
+ const result = findCustomer(customerId);
508
+ if (result.some) {
509
+ console.log(result.value.name);
510
+ } else {
511
+ console.log("Customer not found");
512
+ }
513
+ ```
514
+
515
+ ### Newtype Pattern for Type Safety
516
+
517
+ Create distinct types from same underlying representation:
518
+
519
+ ```typescript
520
+ type UserId = string & { __brand: "UserId" };
521
+ type ProductId = string & { __brand: "ProductId" };
522
+ type OrderId = string & { __brand: "OrderId" };
523
+
524
+ // Can't mix up IDs:
525
+ function getUser(id: UserId): User { ... }
526
+ const productId: ProductId = ...;
527
+ // getUser(productId); // Type error!
528
+ ```
529
+
530
+ ### Builder Pattern for Complex Construction
531
+
532
+ For complex objects with many fields:
533
+
534
+ ```typescript
535
+ class OrderBuilder {
536
+ private order: Partial<Order> = {};
537
+
538
+ withCustomer(id: CustomerId): this {
539
+ this.order.customerId = id;
540
+ return this;
541
+ }
542
+
543
+ addItem(item: OrderLine): this {
544
+ this.order.items = [...(this.order.items || []), item];
545
+ return this;
546
+ }
547
+
548
+ build(): Result<Order, ValidationError> {
549
+ if (!this.order.customerId) return Err({ error: "Customer required" });
550
+ if (!this.order.items?.length) return Err({ error: "Items required" });
551
+ // ... validate all required fields present
552
+ return Ok(this.order as Order);
553
+ }
554
+ }
555
+
556
+ const result = new OrderBuilder()
557
+ .withCustomer(customerId)
558
+ .addItem(item1)
559
+ .addItem(item2)
560
+ .build();
561
+ ```
562
+
563
+ ### Active Pattern / Parser Pattern
564
+
565
+ Transform external data into domain types:
566
+
567
+ ```typescript
568
+ function parseOrderRequest(json: unknown): Result<UnvalidatedOrder, ParseError> {
569
+ // Parse and validate structure
570
+ if (!isObject(json)) return Err({ error: "Expected object" });
571
+ if (!hasProperty(json, "items")) return Err({ error: "Missing items" });
572
+ // ... more parsing
573
+ return Ok({ items: json.items, customerId: json.customerId });
574
+ }
575
+
576
+ function handleRequest(req: Request): Response {
577
+ return parseOrderRequest(req.body)
578
+ .flatMap(validateOrder)
579
+ .flatMap(placeOrder)
580
+ .match(
581
+ success => ({ status: 200, body: success }),
582
+ error => ({ status: 400, body: { error } })
583
+ );
584
+ }
585
+ ```
586
+
587
+ ## Domain Modeling Recipes
588
+
589
+ ### Recipe: Modeling a Workflow
590
+
591
+ 1. **Name the workflow** using ubiquitous language
592
+ 2. **Define the input** (unvalidated/raw data)
593
+ 3. **Define the output** (result of successful execution)
594
+ 4. **Define errors** (what can go wrong)
595
+ 5. **Break into steps** (validation, execution, persistence)
596
+ 6. **Type each intermediate state**
597
+ 7. **Implement as pipeline**
598
+
599
+ ### Recipe: Modeling State Transitions
600
+
601
+ 1. **List all possible states**
602
+ 2. **For each state, determine what data is available**
603
+ 3. **Create a discriminated union type**
604
+ 4. **Define transition functions** (State → Event → Result<State, Error>)
605
+ 5. **Ensure illegal transitions are unrepresentable**
606
+
607
+ ### Recipe: Modeling Optional Fields
608
+
609
+ 1. **Ask: Is this really optional in all cases?**
610
+ 2. **If optional in all cases:** Use Option type
611
+ 3. **If required in some states, optional in others:** Use discriminated union with separate states
612
+ 4. **Never** use null/undefined for domain optionality
613
+
614
+ ### Recipe: Modeling Business Rules
615
+
616
+ 1. **Express rule in English**
617
+ 2. **Identify what makes the rule satisfied**
618
+ 3. **Use types to enforce:** Constrained types, discriminated unions, or validation functions
619
+ 4. **Make it impossible to violate:** Better in types than in runtime checks
620
+
621
+ ## Key Takeaways
622
+
623
+ 1. **Use types to make illegal states unrepresentable**
624
+ 2. **Model workflows as pipelines of data transformations**
625
+ 3. **Use Result types for operations that can fail**
626
+ 4. **Keep domain pure, handle effects at boundaries**
627
+ 5. **Design with types first, implementation second**
628
+ 6. **Use wrapper types to add semantic meaning**
629
+ 7. **Separate commands (writes) from queries (reads)**
630
+ 8. **Model events as immutable facts**
631
+ 9. **Use Option type instead of null/undefined**
632
+ 10. **Let the compiler be your friend - exhaustive pattern matching**
633
+
634
+ When domain modeling, continuously ask:
635
+ - What illegal states can I eliminate?
636
+ - What can go wrong here?
637
+ - Is this type signature telling the truth?
638
+ - Can I make this constraint explicit in the type?
639
+ - What's the simplest type that captures this domain concept?