@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,671 @@
1
+ # Schema Guide
2
+
3
+ This guide covers Schema APIs and the application modeling policies built on
4
+ them.
5
+
6
+ Key source files:
7
+
8
+ - `packages/effect/src/Schema.ts`
9
+ - `packages/effect/src/SchemaTransformation.ts`
10
+ - `packages/effect/src/SchemaGetter.ts`
11
+ - `packages/effect/src/SchemaIssue.ts`
12
+ - `packages/effect/src/JsonSchema.ts`
13
+
14
+ Representative repo usage:
15
+
16
+ - `packages/tools/ai-codegen/src/Config.ts`
17
+ - `packages/platform-node/test/fixtures/rpc-schemas.ts`
18
+ - `packages/platform-browser/test/IndexedDbQueryBuilder.test.ts`
19
+ - `packages/tools/openapi-generator/`
20
+
21
+ ## Mental Model
22
+
23
+ Schema is the standard way to:
24
+
25
+ - define data shapes
26
+ - validate unknown input
27
+ - encode typed values back to serialized form
28
+ - transform between encoded and decoded representations
29
+ - attach metadata and constraints
30
+
31
+ The repo uses Schema pervasively for:
32
+
33
+ - protocol payloads
34
+ - configuration
35
+ - HTTP and RPC contracts
36
+ - database row decoding
37
+ - error types
38
+ - derived tooling such as JSON Schema and arbitrary generation
39
+
40
+ ## Preferred Rule
41
+
42
+ Prefer Schema-based types whenever data crosses a boundary or should be validated, transformed, documented, or encoded.
43
+
44
+ Typical boundaries:
45
+
46
+ - HTTP requests and responses
47
+ - RPC payloads
48
+ - database rows
49
+ - config files
50
+ - worker messages
51
+ - persisted data
52
+ - domain errors
53
+
54
+ ## Application Model Ownership
55
+
56
+ Make Schema the source of truth for application data. Export the schema value
57
+ and derive its decoded TypeScript type from `.Type` under the same name.
58
+
59
+ ```ts
60
+ export const ArtifactId = Schema.NonEmptyString.pipe(
61
+ Schema.brand("@acme/ArtifactId")
62
+ )
63
+ export type ArtifactId = typeof ArtifactId.Type
64
+
65
+ export const GenerateInput = Schema.Struct({
66
+ artifactId: ArtifactId,
67
+ prompt: Schema.String
68
+ })
69
+ export type GenerateInput = typeof GenerateInput.Type
70
+ ```
71
+
72
+ Use schema classes for named reusable models when their validated construction,
73
+ methods, or identity are useful. Use `Schema.Struct` for ordinary record-shaped
74
+ contracts and inline fragments. Do not maintain a parallel interface or
75
+ handwritten structural alias for fields already owned by a schema.
76
+
77
+ Export schemas for every data-bearing service input and result, including
78
+ intermediate application results that have not yet crossed a network boundary.
79
+ Service interfaces should refer to the schema-derived types so the schema
80
+ remains reusable for fixtures, persistence, and future transports.
81
+
82
+ Interfaces remain appropriate for runtime capabilities containing functions,
83
+ resources, and behavior rather than serializable application data.
84
+
85
+ Give each semantically distinct identifier its own branded schema and reuse it
86
+ for every corresponding field and parameter. During a change, inventory every
87
+ added or modified `id`, `*Id`, and `*Ids` field; each should resolve to its
88
+ semantic brand rather than a plain primitive.
89
+
90
+ Decode external `unknown` input once at the earliest boundary that owns it.
91
+ Pass the decoded type through internal services, and encode with the same
92
+ schema when writing an external representation. Use `.Encoded` only in code
93
+ that explicitly handles the encoded form.
94
+
95
+ ## What A Schema Actually Is
96
+
97
+ A schema is not just a static shape.
98
+
99
+ It is a contract between:
100
+
101
+ - the decoded in-memory value you want to work with
102
+ - the encoded representation that comes from or goes to some boundary
103
+
104
+ This is the most important thing many implementations get wrong.
105
+
106
+ Do not think of Schema as “a typed struct definition.”
107
+ Think of it as:
108
+
109
+ - validation
110
+ - decoding
111
+ - encoding
112
+ - transformation
113
+ - metadata
114
+ - reuse across boundaries
115
+
116
+ Because of that, schemas should not be duplicated unless there is a real semantic difference.
117
+
118
+ If two schemas describe the same logical model but differ only because one boundary encodes a field differently, prefer one schema with transformations instead of two parallel schemas.
119
+
120
+ ## Avoid Duplicating Schemas
121
+
122
+ Do not create multiple parallel schemas for the same logical entity unless they truly represent different models.
123
+
124
+ Bad pattern:
125
+
126
+ ```ts
127
+ const Todo = Schema.Struct({
128
+ id: Schema.Number,
129
+ title: Schema.String,
130
+ completed: Schema.Boolean
131
+ })
132
+
133
+ const TodoSql = Schema.Struct({
134
+ id: Schema.Number,
135
+ title: Schema.String,
136
+ completed: Schema.BooleanFromBit
137
+ })
138
+ ```
139
+
140
+ This is usually a sign that transformations are not being used properly.
141
+
142
+ If the model is still “Todo”, do not define a second schema just because one boundary stores `completed` as a bit.
143
+
144
+ Prefer deriving or transforming the representation instead.
145
+
146
+ Why duplication is bad:
147
+
148
+ - the same model is now maintained in multiple places
149
+ - fields drift over time
150
+ - boundary logic gets copied instead of centralized
151
+ - refactors become error-prone
152
+
153
+ Only duplicate schemas when there is a real semantic difference, for example:
154
+
155
+ - a creation payload really is a different model from a persisted entity
156
+ - a public API contract intentionally differs from an internal domain model
157
+ - a projection or partial view is intentionally a different type
158
+
159
+ If the difference is only encoding, use a transformation.
160
+
161
+ ## Prefer `Class` Variants Over `Struct` Variants When Possible
162
+
163
+ When a schema represents a named domain model, reusable payload, or long-lived API shape, prefer `Schema.Class`, `Schema.TaggedClass`, or `Schema.TaggedErrorClass` over a bare `Schema.Struct`.
164
+
165
+ Prefer:
166
+
167
+ ```ts
168
+ import { Schema } from "effect"
169
+
170
+ export class User extends Schema.Class<User>("User")({
171
+ id: Schema.String,
172
+ name: Schema.String
173
+ }) {}
174
+ ```
175
+
176
+ Over:
177
+
178
+ ```ts
179
+ import { Schema } from "effect"
180
+
181
+ export const User = Schema.Struct({
182
+ id: Schema.String,
183
+ name: Schema.String
184
+ })
185
+ ```
186
+
187
+ Why `Class` variants are usually better:
188
+
189
+ - the schema has a stable, named identity
190
+ - reusable models are easier to recognize in code and traces
191
+ - constructors and validation are packaged together
192
+ - extension patterns are clearer
193
+ - named schemas read better in contracts and tooling output
194
+
195
+ Use `Struct` when:
196
+
197
+ - the shape is local and anonymous
198
+ - it is a small inline request or response shape
199
+ - introducing a class would add unnecessary ceremony
200
+ - the schema is primarily a one-off composition fragment
201
+
202
+ Good rule of thumb:
203
+
204
+ - reusable named model: `Class`
205
+ - reusable tagged union member: `TaggedClass`
206
+ - reusable error payload: `TaggedErrorClass`
207
+ - small inline object shape: `Struct`
208
+
209
+ ## One Logical Model, Multiple Representations
210
+
211
+ The right Schema mindset is:
212
+
213
+ - one logical model
214
+ - multiple encoded forms when needed
215
+ - transformations connecting them
216
+
217
+ For example, a `Todo` may be:
218
+
219
+ - a boolean in memory
220
+ - a bit in SQL
221
+ - a string in some external API
222
+
223
+ That does not automatically mean you need three separate top-level schemas.
224
+
225
+ Prefer:
226
+
227
+ - one main schema for the logical model
228
+ - transformed field schemas or transformed object schemas for boundary-specific encoding
229
+ - derived request/result schemas when the shape is actually different
230
+
231
+ ## Common Schema Building Blocks
232
+
233
+ Common primitives and collections used throughout the repo:
234
+
235
+ - `Schema.String`
236
+ - `Schema.Number`
237
+ - `Schema.Boolean`
238
+ - `Schema.BigInt`
239
+ - `Schema.Array(...)`
240
+ - `Schema.Record(key, value)`
241
+ - `Schema.Tuple([...])`
242
+ - `Schema.Struct({...})`
243
+ - `Schema.Union([...])`
244
+
245
+ Example:
246
+
247
+ ```ts
248
+ const Todo = Schema.Struct({
249
+ id: Schema.Number,
250
+ title: Schema.String,
251
+ completed: Schema.Boolean
252
+ })
253
+ ```
254
+
255
+ ## `Class`, `TaggedClass`, and `TaggedErrorClass`
256
+
257
+ ### `Schema.Class`
258
+
259
+ Use for named reusable schema-backed models.
260
+
261
+ ```ts
262
+ class Product extends Schema.Class<Product>("Product")({
263
+ id: Schema.String,
264
+ price: Schema.Number
265
+ }) {}
266
+ ```
267
+
268
+ ### Constructor Rule
269
+
270
+ When constructing schema classes, prefer `X.make(...)` over `new X(...)`.
271
+
272
+ Prefer:
273
+
274
+ ```ts
275
+ const todo = Todo.make({
276
+ id: 1,
277
+ title: "write docs",
278
+ completed: false
279
+ })
280
+ ```
281
+
282
+ Over:
283
+
284
+ ```ts
285
+ const todo = new Todo({
286
+ id: 1,
287
+ title: "write docs",
288
+ completed: false
289
+ })
290
+ ```
291
+
292
+ Why:
293
+
294
+ - it is the intended schema-class construction style
295
+ - it makes schema-backed construction explicit
296
+ - it keeps the codebase consistent
297
+ - it reads better across `Class`, `TaggedClass`, and `TaggedErrorClass`
298
+
299
+ Use this rule consistently for:
300
+
301
+ - `Schema.Class`
302
+ - `Schema.TaggedClass`
303
+ - `Schema.TaggedErrorClass`
304
+
305
+ ### `Schema.TaggedClass`
306
+
307
+ Use for members of tagged unions.
308
+
309
+ ```ts
310
+ class Circle extends Schema.TaggedClass<Circle>()("Circle", {
311
+ radius: Schema.Number
312
+ }) {}
313
+
314
+ class Rectangle extends Schema.TaggedClass<Rectangle>()("Rectangle", {
315
+ width: Schema.Number,
316
+ height: Schema.Number
317
+ }) {}
318
+ ```
319
+
320
+ ### `Schema.TaggedErrorClass`
321
+
322
+ Use for schema-backed typed errors.
323
+
324
+ ```ts
325
+ class NotFound extends Schema.TaggedErrorClass<NotFound>()("NotFound", {
326
+ id: Schema.String
327
+ }) {}
328
+ ```
329
+
330
+ ## Optional Fields
331
+
332
+ Be precise about optionality.
333
+
334
+ Important rule from the canonical docs:
335
+
336
+ - `Schema.optional(schema)` means `T | undefined`
337
+ - `Schema.optionalKey(schema)` means an exact optional property in a struct
338
+
339
+ Prefer `optionalKey` for object fields.
340
+
341
+ Prefer:
342
+
343
+ ```ts
344
+ const Query = Schema.Struct({
345
+ search: Schema.optionalKey(Schema.String)
346
+ })
347
+ ```
348
+
349
+ Use `optional` when the value itself should be `A | undefined`, not just an omitted field.
350
+
351
+ ## Unions
352
+
353
+ Use `Schema.Union([...])` for ordinary unions.
354
+
355
+ ```ts
356
+ const Id = Schema.Union([
357
+ Schema.String,
358
+ Schema.Number
359
+ ])
360
+ ```
361
+
362
+ Prefer tagged unions for domain variants.
363
+
364
+ ```ts
365
+ class Created extends Schema.TaggedClass<Created>()("Created", {
366
+ id: Schema.String
367
+ }) {}
368
+
369
+ class Deleted extends Schema.TaggedClass<Deleted>()("Deleted", {
370
+ id: Schema.String
371
+ }) {}
372
+
373
+ const TodoEvent = Schema.Union([Created, Deleted])
374
+ ```
375
+
376
+ Why:
377
+
378
+ - decoding and branching are clearer
379
+ - `_tag`-based matching aligns with Effect code style
380
+
381
+ ## Recursive Schemas
382
+
383
+ Use `Schema.suspend` for recursive schemas.
384
+
385
+ ```ts
386
+ type Tree = {
387
+ readonly name: string
388
+ readonly children: ReadonlyArray<Tree>
389
+ }
390
+
391
+ const Tree: Schema.Schema<Tree> = Schema.Struct({
392
+ name: Schema.String,
393
+ children: Schema.Array(Schema.suspend((): Schema.Schema<Tree> => Tree))
394
+ })
395
+ ```
396
+
397
+ Use it whenever a schema refers to itself, directly or indirectly.
398
+
399
+ Without `suspend`, recursive definitions will not work correctly.
400
+
401
+ ## Transformations
402
+
403
+ Transformations are one of the most important Schema features.
404
+
405
+ Use them when decoded and encoded shapes differ.
406
+
407
+ This is the main tool that avoids needless schema duplication.
408
+
409
+ If your instinct is “I need another schema because this boundary encodes the same value differently”, stop and first ask whether this should be one schema with a transformation instead.
410
+
411
+ ### `Schema.decodeTo`
412
+
413
+ Use `decodeTo` when you want one schema to decode into another schema's type.
414
+
415
+ ```ts
416
+ const TrimmedString = Schema.String.pipe(
417
+ Schema.decodeTo(Schema.String, {
418
+ decode: (value) => value.trim(),
419
+ encode: (value) => value
420
+ })
421
+ )
422
+ ```
423
+
424
+ The canonical docs explicitly note that `decodeTo` is curried and should be used with `pipe`.
425
+
426
+ ### `Schema.encodeTo`
427
+
428
+ Use `encodeTo` when the reverse direction reads more clearly.
429
+
430
+ ### `SchemaTransformation.transformOrFail`
431
+
432
+ Use `transformOrFail` when the transformation itself is effectful or may fail.
433
+
434
+ ```ts
435
+ import * as Effect from "effect/Effect"
436
+ import * as Schema from "effect/Schema"
437
+ import * as SchemaTransformation from "effect/SchemaTransformation"
438
+
439
+ const VerifiedString = Schema.String.pipe(
440
+ Schema.decodeTo(
441
+ Schema.String,
442
+ SchemaTransformation.transformOrFail({
443
+ decode: (value) => Effect.succeed(value.trim()),
444
+ encode: (value) => Effect.succeed(value)
445
+ })
446
+ )
447
+ )
448
+ ```
449
+
450
+ Use this when:
451
+
452
+ - validation depends on services or effects
453
+ - decoding can fail with structured issues
454
+ - encoding also needs logic beyond identity
455
+
456
+ ## Field-Level Transformations
457
+
458
+ Very often, the right answer is not a second object schema but a transformed field schema.
459
+
460
+ Example shape:
461
+
462
+ ```ts
463
+ const Completed = Schema.BooleanFromBit
464
+
465
+ const Todo = Schema.Struct({
466
+ id: Schema.Number,
467
+ title: Schema.String,
468
+ completed: Completed
469
+ })
470
+ ```
471
+
472
+ In this pattern:
473
+
474
+ - the logical model still has `completed: boolean`
475
+ - the encoded SQL-facing representation can still be a bit
476
+ - the transformation lives at the field where it belongs
477
+
478
+ This is usually better than defining `Todo` and `TodoSql` as separate object schemas.
479
+
480
+ ## Object-Level Transformations
481
+
482
+ Use object-level transformations when the whole object encoding differs, not just one field.
483
+
484
+ Good use cases:
485
+
486
+ - external keys differ from internal keys
487
+ - several fields need coordinated transformation
488
+ - the encoded shape is a structurally different representation of the same model
489
+
490
+ Still prefer a single logical schema plus a transformation pipeline over maintaining multiple duplicated top-level schemas.
491
+
492
+ ## Rename Keys
493
+
494
+ Schema supports key renaming through struct transformations.
495
+
496
+ The canonical `Schema.ts` implements key renaming by mapping fields and using
497
+ decode/encode transformations with renamed key maps.
498
+
499
+ Use key renaming when:
500
+
501
+ - external payload keys differ from internal keys
502
+ - you want stable internal names while honoring external contract names
503
+
504
+ Preferred pattern:
505
+
506
+ - keep the internal decoded shape idiomatic
507
+ - use schema-level transformation or field-mapping to adapt external keys
508
+
509
+ This is another example of avoiding duplication. If the only difference is key naming, do not define a second schema just to rename fields manually later.
510
+
511
+ In practice, use struct field mapping helpers and transformation composition rather than manual post-parse object rewriting.
512
+
513
+ ## Opaque And Branded Types
514
+
515
+ Use opaque or branded schemas when a value should stay distinct from its structural base type.
516
+
517
+ ### `Schema.brand`
518
+
519
+ Use `brand` for refined nominal distinctions.
520
+
521
+ ```ts
522
+ const UserId = Schema.String.pipe(
523
+ Schema.brand("UserId")
524
+ )
525
+ ```
526
+
527
+ This is useful for:
528
+
529
+ - IDs
530
+ - validated domain scalars
531
+ - preventing accidental interchange of same-shaped values
532
+
533
+ ### `Schema.Opaque`
534
+
535
+ Use `Opaque` when you want an opaque schema-backed type with the same structure as its underlying schema.
536
+
537
+ This is especially useful when the type should remain distinct at the type level without changing its runtime shape.
538
+
539
+ ## Picking, Omitting, Partial Shapes, And Mutability
540
+
541
+ Common struct operations include:
542
+
543
+ - `pick`
544
+ - `omit`
545
+ - `partial`
546
+ - `mutable`
547
+
548
+ Use them to derive variations instead of redefining near-identical schemas manually.
549
+
550
+ Good examples:
551
+
552
+ - request subset from a domain model
553
+ - patch/update payloads
554
+ - mutable representations for specific adapters
555
+
556
+ Prefer deriving from one source schema rather than maintaining parallel copies.
557
+
558
+ This is the second major tool for avoiding duplication:
559
+
560
+ - use transformations when encoded and decoded representations differ
561
+ - use derivation when one schema is a subset, superset, or variation of another
562
+
563
+ ## Constraints And Validation
564
+
565
+ Use schema checks and filters for validation.
566
+
567
+ Examples from the module docs include:
568
+
569
+ - `isMinLength`
570
+ - `isGreaterThan`
571
+ - `isPattern`
572
+ - `isUUID`
573
+
574
+ Attach them with `.check(...)`.
575
+
576
+ Use this when:
577
+
578
+ - the validation is intrinsic to the schema
579
+ - the rule belongs to the data contract
580
+
581
+ For business-rule validation that depends on services or current state, prefer effectful logic outside the schema or use effectful transformations.
582
+
583
+ ## Decoding And Encoding
584
+
585
+ Common operations:
586
+
587
+ - `Schema.decodeUnknownSync`
588
+ - `Schema.decodeUnknownEffect`
589
+ - `Schema.decodeUnknownExit`
590
+ - `Schema.encodeUnknownSync`
591
+ - `Schema.encodeUnknownEffect`
592
+
593
+ Preferred rule:
594
+
595
+ - use `decodeUnknownEffect` and `encodeUnknownEffect` in Effect code
596
+ - avoid throwing sync decode APIs in application flows unless you are intentionally at a sync boundary
597
+
598
+ Good pattern:
599
+
600
+ ```ts
601
+ const decodeUser = Schema.decodeUnknownEffect(User)
602
+ ```
603
+
604
+ ## Schema Metadata And Derived Tooling
605
+
606
+ Schema is also used for:
607
+
608
+ - annotations and documentation metadata
609
+ - JSON Schema generation
610
+ - arbitrary generation for tests
611
+ - derived equivalence
612
+
613
+ Useful operations from the module docs:
614
+
615
+ - `.annotate(...)`
616
+ - `Schema.toJsonSchemaDocument(...)`
617
+ - `Schema.toArbitrary(...)`
618
+ - `Schema.toEquivalence(...)`
619
+
620
+ Use annotations when the schema participates in:
621
+
622
+ - API docs
623
+ - codegen
624
+ - contract generation
625
+
626
+ ## Common Repo Patterns
627
+
628
+ Patterns visible in the canonical source:
629
+
630
+ - `Schema.Class` for named reusable contract types
631
+ - `Schema.Struct` for inline shapes and anonymous fragments
632
+ - `Schema.Union` for alternative payloads
633
+ - `Schema.optionalKey` for request/query/body optional fields
634
+ - `Schema.suspend` for recursive generated schemas
635
+ - `Schema.decodeTo` and `transformOrFail` for non-trivial decode/encode logic
636
+ - `Schema.TaggedErrorClass` for typed error payloads
637
+
638
+ ## Best Practices
639
+
640
+ 1. Prefer `Class` variants over plain `Struct` for named reusable schemas.
641
+ 2. Prefer tagged variants for unions and errors.
642
+ 3. Prefer `optionalKey` for optional object properties.
643
+ 4. Do not duplicate schemas unless there is a real semantic difference.
644
+ 5. Prefer schema-level transformations over ad hoc post-parse object rewriting.
645
+ 6. Prefer deriving schema variants with `pick`, `omit`, `partial`, and `mutable` instead of duplicating definitions.
646
+ 7. Prefer field-level transformations when only a field encoding differs.
647
+ 8. Prefer branded or opaque types for important domain identifiers.
648
+ 9. Prefer `decodeUnknownEffect` in application code.
649
+ 10. Keep internal decoded shapes idiomatic and use schema transforms for external representation differences.
650
+ 11. Give every data-bearing service input and result an exported schema.
651
+ 12. Audit changed identifier fields for the correct semantic brand.
652
+ 13. Decode external data once at its earliest owning boundary.
653
+
654
+ ## Anti-Patterns
655
+
656
+ - using plain `Struct` for every reusable domain model even when `Class` would give a clearer named type
657
+ - duplicating whole schemas when only one field encoding differs
658
+ - creating `Foo` and `FooSql` schemas for the same logical model when a transformation would do
659
+ - using `optional` when you actually want an optional key
660
+ - duplicating near-identical schemas instead of deriving variants
661
+ - rewriting keys manually after decode instead of using schema transformations
662
+ - hand-validating external data after decode when the constraint belongs in the schema
663
+ - exposing unvalidated external payloads deep into business logic
664
+
665
+ ## Good Repo Examples To Study
666
+
667
+ - `packages/tools/ai-codegen/src/Config.ts`
668
+ - `packages/platform-node/test/fixtures/rpc-schemas.ts`
669
+ - `packages/platform-browser/test/IndexedDbQueryBuilder.test.ts`
670
+ - `packages/tools/openapi-generator/src/JsonSchemaGenerator.ts`
671
+ - `packages/effect/src/Schema.ts`