engsys 1.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 (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/core/agents/aaron.md +152 -0
  4. package/core/agents/bert.md +115 -0
  5. package/core/agents/isabelle.md +136 -0
  6. package/core/agents/jody.md +150 -0
  7. package/core/agents/leith.md +111 -0
  8. package/core/agents/marcelo.md +282 -0
  9. package/core/agents/melvin.md +101 -0
  10. package/core/agents/nyx.md +152 -0
  11. package/core/agents/otto.md +168 -0
  12. package/core/agents/patricia.md +283 -0
  13. package/core/commands/design-audit-local.md +155 -0
  14. package/core/commands/design-audit.md +235 -0
  15. package/core/commands/design-critique.md +96 -0
  16. package/core/commands/file-issue.md +22 -0
  17. package/core/commands/generate-project.md +45 -0
  18. package/core/commands/implement-issue.md +37 -0
  19. package/core/commands/implement-project.md +40 -0
  20. package/core/commands/naturalize.md +61 -0
  21. package/core/commands/pre-push.md +29 -0
  22. package/core/commands/prep-review-collect.md +130 -0
  23. package/core/commands/prep-review-finalize.md +121 -0
  24. package/core/commands/prep-review-publish.md +113 -0
  25. package/core/commands/prep-review.md +65 -0
  26. package/core/commands/project-closeout.md +25 -0
  27. package/core/skills/agentic-eval/SKILL.md +195 -0
  28. package/core/skills/chrome-devtools/SKILL.md +97 -0
  29. package/core/skills/code-review/SKILL.md +26 -0
  30. package/core/skills/gh-cli/SKILL.md +2202 -0
  31. package/core/skills/git-commit/SKILL.md +124 -0
  32. package/core/skills/git-workflow-agents/SKILL.md +462 -0
  33. package/core/skills/git-workflow-agents/reference.md +220 -0
  34. package/core/skills/github-actions/SKILL.md +190 -0
  35. package/core/skills/github-issues/SKILL.md +154 -0
  36. package/core/skills/llm-structured-outputs/SKILL.md +323 -0
  37. package/core/skills/llm-structured-outputs/references/provider-details.md +392 -0
  38. package/core/skills/pre-push/SKILL.md +115 -0
  39. package/core/skills/refactor/SKILL.md +645 -0
  40. package/core/skills/web-design-reviewer/SKILL.md +371 -0
  41. package/core/skills/webapp-testing/SKILL.md +127 -0
  42. package/core/skills/webapp-testing/test-helper.js +56 -0
  43. package/core/templates/CLAUDE.md.tmpl +98 -0
  44. package/core/templates/adr-template.md +67 -0
  45. package/core/templates/gh-issue-templates/bug.md +39 -0
  46. package/core/templates/gh-issue-templates/content.md +42 -0
  47. package/core/templates/gh-issue-templates/enhancement.md +36 -0
  48. package/core/templates/gh-issue-templates/feature.md +39 -0
  49. package/core/templates/gh-issue-templates/infrastructure.md +41 -0
  50. package/core/templates/post-edit-reminders.sh.tmpl +19 -0
  51. package/core/templates/settings.json.tmpl +90 -0
  52. package/core/templates/settings.local.json.tmpl +3 -0
  53. package/core/workflows/agent-implementation-workflow.md +346 -0
  54. package/core/workflows/generate-project.md +258 -0
  55. package/core/workflows/implement-project-workflow.md +190 -0
  56. package/core/workflows/issue-tracking.md +89 -0
  57. package/core/workflows/project-closeout-ceremony.md +77 -0
  58. package/core/workflows/review-workflow.md +266 -0
  59. package/engsys.config.example.yaml +46 -0
  60. package/install +202 -0
  61. package/lessons-library/README.md +80 -0
  62. package/lessons-library/async-callbacks-verify-liveness.md +15 -0
  63. package/lessons-library/change-isnt-done-until-every-surface-updated.md +15 -0
  64. package/lessons-library/claim-then-act-for-irreversible-ops.md +16 -0
  65. package/lessons-library/co-commit-entangled-work.md +15 -0
  66. package/lessons-library/dependabot-triage-playbook.md +17 -0
  67. package/lessons-library/deploy-by-digest-and-verify-the-running-revision.md +15 -0
  68. package/lessons-library/enforce-your-guarantee-at-your-boundary.md +16 -0
  69. package/lessons-library/gate-changes-on-measurement-not-vibes.md +15 -0
  70. package/lessons-library/iac-first-no-console-changes.md +15 -0
  71. package/lessons-library/independent-objective-review-gate.md +15 -0
  72. package/lessons-library/keep-an-immutable-source-of-truth.md +15 -0
  73. package/lessons-library/long-agent-runs-checkpoint-not-poll.md +15 -0
  74. package/lessons-library/model-identity-with-stable-ids-and-provenance.md +15 -0
  75. package/lessons-library/operator-choices-are-first-class.md +15 -0
  76. package/lessons-library/prefer-tool-enforced-structured-output.md +15 -0
  77. package/lessons-library/prove-causation-before-acting.md +15 -0
  78. package/lessons-library/re-read-state-before-acting.md +14 -0
  79. package/lessons-library/read-layer-tolerates-unbackfilled-rows.md +15 -0
  80. package/lessons-library/shell-safety-pipefail-and-validate-before-teardown.md +14 -0
  81. package/lessons-library/shift-correctness-left-and-distrust-false-greens.md +15 -0
  82. package/lessons-library/stray-control-bytes-hide-changes.md +14 -0
  83. package/lessons-library/tests-can-assert-the-bug.md +15 -0
  84. package/lessons-library/verify-ground-truth-not-reports.md +15 -0
  85. package/lessons-library/worktrees-need-bootstrap-from-origin-main.md +15 -0
  86. package/lib/commands.js +356 -0
  87. package/lib/generate-team-avatars.mjs +251 -0
  88. package/lib/manifest.js +155 -0
  89. package/lib/render.js +135 -0
  90. package/lib/selftest.js +90 -0
  91. package/lib/util.js +89 -0
  92. package/lib/yaml.js +156 -0
  93. package/optional-agents/gary.md +86 -0
  94. package/optional-agents/jos.md +136 -0
  95. package/optional-agents/sandy.md +101 -0
  96. package/optional-agents/steve.md +161 -0
  97. package/package.json +43 -0
  98. package/stacks/cloud/aws/claude.fragment.md +17 -0
  99. package/stacks/cloud/aws/settings.fragment.json +39 -0
  100. package/stacks/cloud/aws/skills/aws-deployment-preflight/SKILL.md +165 -0
  101. package/stacks/cloud/aws/skills/cloud-architecture-aws/SKILL.md +265 -0
  102. package/stacks/cloud/azure/claude.fragment.md +17 -0
  103. package/stacks/cloud/azure/settings.fragment.json +45 -0
  104. package/stacks/cloud/azure/skills/azure-deployment-preflight/SKILL.md +175 -0
  105. package/stacks/cloud/azure/skills/cloud-architecture-azure/SKILL.md +211 -0
  106. package/stacks/cloud/cloudflare/claude.fragment.md +21 -0
  107. package/stacks/cloud/cloudflare/settings.fragment.json +31 -0
  108. package/stacks/cloud/cloudflare/skills/cloud-architecture-cloudflare/SKILL.md +294 -0
  109. package/stacks/cloud/cloudflare/skills/cloudflare-deployment-preflight/SKILL.md +175 -0
  110. package/stacks/cloud/gcp/claude.fragment.md +17 -0
  111. package/stacks/cloud/gcp/settings.fragment.json +40 -0
  112. package/stacks/cloud/gcp/skills/cloud-architecture-gcp/SKILL.md +208 -0
  113. package/stacks/cloud/gcp/skills/gcp-deployment-preflight/SKILL.md +137 -0
  114. package/stacks/db/mongo/skills/mongo-conventions/SKILL.md +96 -0
  115. package/stacks/db/prisma/claude.fragment.md +49 -0
  116. package/stacks/db/prisma/skills/docker-database-package-copy/SKILL.md +44 -0
  117. package/stacks/db/prisma/skills/prisma-conventions/SKILL.md +37 -0
  118. package/stacks/domain/mobile-growth/skills/apple-ads/SKILL.md +184 -0
  119. package/stacks/domain/mobile-growth/skills/apple-ads/references/benchmark-notes.md +47 -0
  120. package/stacks/domain/mobile-growth/skills/apple-ads/references/official-links.md +53 -0
  121. package/stacks/domain/mobile-growth/skills/google-play-growth/SKILL.md +197 -0
  122. package/stacks/domain/mobile-growth/skills/google-play-growth/references/benchmark-notes.md +47 -0
  123. package/stacks/domain/mobile-growth/skills/google-play-growth/references/official-links.md +45 -0
  124. package/stacks/iac/bicep/claude.fragment.md +14 -0
  125. package/stacks/iac/bicep/settings.fragment.json +20 -0
  126. package/stacks/iac/bicep/skills/iac-bicep/SKILL.md +113 -0
  127. package/stacks/iac/cdk/claude.fragment.md +14 -0
  128. package/stacks/iac/cdk/settings.fragment.json +23 -0
  129. package/stacks/iac/cdk/skills/iac-cdk/SKILL.md +104 -0
  130. package/stacks/iac/terraform/claude.fragment.md +13 -0
  131. package/stacks/iac/terraform/settings.fragment.json +25 -0
  132. package/stacks/iac/terraform/skills/iac-terraform/SKILL.md +93 -0
  133. package/stacks/iac/terraform/skills/terraform-conventions/SKILL.md +87 -0
  134. package/stacks/lang/kotlin/skills/android-testing/SKILL.md +263 -0
  135. package/stacks/lang/kotlin/skills/jetpack-compose/SKILL.md +264 -0
  136. package/stacks/lang/kotlin/skills/kotlin-coroutines/SKILL.md +329 -0
  137. package/stacks/lang/python/skills/python-conventions/SKILL.md +61 -0
  138. package/stacks/lang/shell/skills/shell-scripting/SKILL.md +110 -0
  139. package/stacks/lang/swift/skills/swift-concurrency/SKILL.md +423 -0
  140. package/stacks/lang/swift/skills/swift-concurrency/references/approachable-concurrency.md +80 -0
  141. package/stacks/lang/swift/skills/swift-concurrency/references/concurrency-patterns.md +233 -0
  142. package/stacks/lang/swift/skills/swift-concurrency/references/swiftui-concurrency.md +187 -0
  143. package/stacks/lang/swift/skills/swift-concurrency/references/synchronization-primitives.md +341 -0
  144. package/stacks/lang/swift/skills/swift-testing/SKILL.md +497 -0
  145. package/stacks/lang/swift/skills/swift-testing/references/testing-advanced.md +106 -0
  146. package/stacks/lang/swift/skills/swift-testing/references/testing-patterns.md +504 -0
  147. package/stacks/lang/swift/skills/swiftdata/SKILL.md +334 -0
  148. package/stacks/lang/swift/skills/swiftdata/references/core-data-coexistence.md +504 -0
  149. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-advanced.md +975 -0
  150. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-queries.md +675 -0
  151. package/stacks/lang/swift/skills/swiftui-patterns/SKILL.md +371 -0
  152. package/stacks/lang/swift/skills/swiftui-patterns/references/architecture-patterns.md +486 -0
  153. package/stacks/lang/swift/skills/swiftui-patterns/references/deprecated-migration.md +1097 -0
  154. package/stacks/lang/swift/skills/swiftui-patterns/references/design-polish.md +780 -0
  155. package/stacks/lang/swift/skills/swiftui-patterns/references/platform-and-sharing.md +696 -0
  156. package/stacks/lang/typescript/skills/typescript-conventions/SKILL.md +91 -0
  157. package/stacks/platform/android/claude.fragment.md +40 -0
  158. package/stacks/platform/android/hooks/pre-push-gradle.sh +70 -0
  159. package/stacks/platform/android/settings.fragment.json +13 -0
  160. package/stacks/platform/android/skills/android-build-conventions/SKILL.md +247 -0
  161. package/stacks/platform/ios/claude.fragment.md +24 -0
  162. package/stacks/platform/ios/hooks/pre-push-xcodebuild.sh +82 -0
  163. package/stacks/platform/ios/settings.fragment.json +21 -0
  164. package/stacks/platform/ios/skills/xcodebuildmcp-simulator-logs/SKILL.md +76 -0
  165. package/stacks/platform/web/skills/frontend-testing/SKILL.md +246 -0
  166. package/stacks/platform/web/skills/react-conventions/SKILL.md +261 -0
  167. package/stacks/platform/web/skills/web-platform-conventions/SKILL.md +55 -0
  168. package/stacks/tooling/issue-tracker-github/claude.fragment.md +10 -0
  169. package/stacks/tooling/issue-tracker-github/settings.fragment.json +24 -0
  170. package/stacks/tooling/issue-tracker-github/skills/issue-tracker-github/SKILL.md +278 -0
  171. package/stacks/tooling/issue-tracker-linear/claude.fragment.md +17 -0
  172. package/stacks/tooling/issue-tracker-linear/settings.fragment.json +9 -0
  173. package/stacks/tooling/issue-tracker-linear/skills/issue-tracker-linear/SKILL.md +183 -0
@@ -0,0 +1,334 @@
1
+ ---
2
+ name: swiftdata
3
+ description: "Implement, review, or improve data persistence using SwiftData. Use when defining @Model classes with @Attribute, @Relationship, @Transient, @Unique, or @Index; when querying with @Query, #Predicate, FetchDescriptor, or SortDescriptor; when configuring ModelContainer and ModelContext for SwiftUI or background work with @ModelActor; when planning schema migrations with VersionedSchema and SchemaMigrationPlan; when setting up CloudKit sync with ModelConfiguration; or when coexisting with or migrating from Core Data."
4
+ ---
5
+
6
+ # SwiftData
7
+
8
+ Persist, query, and manage structured data in iOS 26+ apps using SwiftData
9
+ with Swift 6.3.
10
+
11
+ ## Contents
12
+
13
+ - [Model Definition](#model-definition)
14
+ - [ModelContainer Setup](#modelcontainer-setup)
15
+ - [CRUD Operations](#crud-operations)
16
+ - [@Query in SwiftUI](#query-in-swiftui)
17
+ - [#Predicate](#predicate)
18
+ - [FetchDescriptor](#fetchdescriptor)
19
+ - [Schema Versioning and Migration](#schema-versioning-and-migration)
20
+ - [Concurrency (@ModelActor)](#concurrency-modelactor)
21
+ - [SwiftUI Integration](#swiftui-integration)
22
+ - [Common Mistakes](#common-mistakes)
23
+ - [Review Checklist](#review-checklist)
24
+ - [References](#references)
25
+
26
+ ## Model Definition
27
+
28
+ Apply `@Model` to a **class** (not struct). Generates `PersistentModel`, `Observable`, `Sendable`.
29
+
30
+ ```swift
31
+ @Model
32
+ class Trip {
33
+ var name: String
34
+ var destination: String
35
+ var startDate: Date
36
+ var endDate: Date
37
+ var isFavorite: Bool = false
38
+ @Attribute(.externalStorage) var imageData: Data?
39
+ @Relationship(deleteRule: .cascade, inverse: \LivingAccommodation.trip)
40
+ var accommodation: LivingAccommodation?
41
+ @Transient var isSelected: Bool = false // Always provide default
42
+
43
+ init(name: String, destination: String, startDate: Date, endDate: Date) {
44
+ self.name = name; self.destination = destination
45
+ self.startDate = startDate; self.endDate = endDate
46
+ }
47
+ }
48
+ ```
49
+
50
+ **@Attribute options**: `.externalStorage`, `.unique`, `.spotlight`, `.allowsCloudEncryption`, `.preserveValueOnDeletion` (iOS 18+), `.ephemeral`, `.transformable(by:)`. Rename: `@Attribute(originalName: "old_name")`.
51
+
52
+ **@Relationship**: `deleteRule:` `.cascade`/`.nullify`(default)/`.deny`/`.noAction`. Specify `inverse:` for reliable behavior. Unidirectional (iOS 18+): `inverse: nil`.
53
+
54
+ **#Unique (iOS 18+)**: `#Unique<Person>([\.firstName, \.lastName])` -- compound uniqueness.
55
+
56
+ **Inheritance (iOS 26+)**: `@Model class BusinessTrip: Trip { var company: String }`.
57
+
58
+ Supported types: `Bool`, `Int`/`UInt` variants, `Float`, `Double`, `String`, `Date`, `Data`, `URL`, `UUID`, `Decimal`, `Array`, `Dictionary`, `Set`, `Codable` enums, `Codable` structs (composite, iOS 18+), relationships to `@Model` classes.
59
+
60
+ ## ModelContainer Setup
61
+
62
+ ```swift
63
+ // Basic
64
+ let container = try ModelContainer(for: Trip.self, LivingAccommodation.self)
65
+
66
+ // Configured
67
+ let config = ModelConfiguration("Store", isStoredInMemoryOnly: false,
68
+ groupContainer: .identifier("group.com.example.app"),
69
+ cloudKitDatabase: .private("iCloud.com.example.app"))
70
+ let container = try ModelContainer(for: Trip.self, configurations: config)
71
+
72
+ // With migration plan
73
+ let container = try ModelContainer(for: SchemaV2.Trip.self,
74
+ migrationPlan: TripMigrationPlan.self)
75
+
76
+ // In-memory (previews/tests)
77
+ let container = try ModelContainer(for: Trip.self,
78
+ configurations: ModelConfiguration(isStoredInMemoryOnly: true))
79
+ ```
80
+
81
+ ## CRUD Operations
82
+
83
+ ```swift
84
+ // CREATE
85
+ let trip = Trip(name: "Summer", destination: "Paris", startDate: .now, endDate: .now + 86400*7)
86
+ modelContext.insert(trip)
87
+ try modelContext.save() // or rely on autosave
88
+
89
+ // READ
90
+ let trips = try modelContext.fetch(FetchDescriptor<Trip>(
91
+ predicate: #Predicate { $0.destination == "Paris" },
92
+ sortBy: [SortDescriptor(\.startDate)]))
93
+
94
+ // UPDATE -- modify properties directly; autosave handles persistence
95
+ trip.destination = "Rome"
96
+
97
+ // DELETE
98
+ modelContext.delete(trip)
99
+ try modelContext.delete(model: Trip.self, where: #Predicate { $0.isFavorite == false })
100
+
101
+ // TRANSACTION (atomic)
102
+ try modelContext.transaction {
103
+ modelContext.insert(trip); trip.isFavorite = true
104
+ }
105
+ ```
106
+
107
+ ## @Query in SwiftUI
108
+
109
+ ```swift
110
+ struct TripListView: View {
111
+ @Query(filter: #Predicate<Trip> { $0.isFavorite == true },
112
+ sort: \.startDate, order: .reverse)
113
+ private var favorites: [Trip]
114
+
115
+ var body: some View { List(favorites) { trip in Text(trip.name) } }
116
+ }
117
+
118
+ // Dynamic query via init
119
+ struct SearchView: View {
120
+ @Query private var trips: [Trip]
121
+ init(search: String) {
122
+ _trips = Query(filter: #Predicate<Trip> { trip in
123
+ search.isEmpty || trip.name.localizedStandardContains(search)
124
+ }, sort: [SortDescriptor(\.name)])
125
+ }
126
+ var body: some View { List(trips) { trip in Text(trip.name) } }
127
+ }
128
+
129
+ // FetchDescriptor query
130
+ struct RecentView: View {
131
+ static var desc: FetchDescriptor<Trip> {
132
+ var d = FetchDescriptor<Trip>(sortBy: [SortDescriptor(\.startDate)])
133
+ d.fetchLimit = 5; return d
134
+ }
135
+ @Query(RecentView.desc) private var recent: [Trip]
136
+ var body: some View { List(recent) { trip in Text(trip.name) } }
137
+ }
138
+ ```
139
+
140
+ ## #Predicate
141
+
142
+ ```swift
143
+ #Predicate<Trip> { $0.destination.localizedStandardContains("paris") } // String
144
+ #Predicate<Trip> { $0.startDate > Date.now } // Date
145
+ #Predicate<Trip> { $0.isFavorite && $0.destination != "Unknown" } // Compound
146
+ #Predicate<Trip> { $0.accommodation?.name != nil } // Optional
147
+ #Predicate<Trip> { $0.tags.contains { $0.name == "adventure" } } // Collection
148
+ ```
149
+
150
+ Supported: `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`, `!`, `contains()`, `allSatisfy()`, `filter()`, `starts(with:)`, `localizedStandardContains()`, `caseInsensitiveCompare()`, arithmetic, ternary, optional chaining, nil coalescing, type casting. **Not supported**: flow control, nested declarations, arbitrary method calls.
151
+
152
+ ## FetchDescriptor
153
+
154
+ ```swift
155
+ var d = FetchDescriptor<Trip>(predicate: ..., sortBy: [...])
156
+ d.fetchLimit = 20; d.fetchOffset = 0
157
+ d.includePendingChanges = true
158
+ d.propertiesToFetch = [\.name, \.startDate]
159
+ d.relationshipKeyPathsForPrefetching = [\.accommodation]
160
+ let trips = try modelContext.fetch(d)
161
+ let count = try modelContext.fetchCount(d)
162
+ let ids = try modelContext.fetchIdentifiers(d)
163
+ try modelContext.enumerate(d, batchSize: 1000) { trip in trip.isProcessed = true }
164
+ ```
165
+
166
+ ## Schema Versioning and Migration
167
+
168
+ ```swift
169
+ enum SchemaV1: VersionedSchema {
170
+ static var versionIdentifier = Schema.Version(1, 0, 0)
171
+ static var models: [any PersistentModel.Type] { [Trip.self] }
172
+ @Model class Trip { var name: String; init(name: String) { self.name = name } }
173
+ }
174
+
175
+ enum SchemaV2: VersionedSchema {
176
+ static var versionIdentifier = Schema.Version(2, 0, 0)
177
+ static var models: [any PersistentModel.Type] { [Trip.self] }
178
+ @Model class Trip {
179
+ var name: String; var startDate: Date? // New property
180
+ init(name: String) { self.name = name }
181
+ }
182
+ }
183
+
184
+ enum TripMigrationPlan: SchemaMigrationPlan {
185
+ static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] }
186
+ static var stages: [MigrationStage] { [migrateV1toV2] }
187
+ static let migrateV1toV2 = MigrationStage.lightweight(
188
+ fromVersion: SchemaV1.self, toVersion: SchemaV2.self)
189
+ }
190
+
191
+ // Custom migration for data transformation
192
+ static let migrateV2toV3 = MigrationStage.custom(
193
+ fromVersion: SchemaV2.self, toVersion: SchemaV3.self,
194
+ willMigrate: nil,
195
+ didMigrate: { context in
196
+ let trips = try context.fetch(FetchDescriptor<SchemaV3.Trip>())
197
+ for trip in trips { trip.displayName = trip.name.capitalized }
198
+ try context.save()
199
+ })
200
+ ```
201
+
202
+ Lightweight handles: adding optional/defaulted properties, renaming (`originalName`), removing properties, adding model types.
203
+
204
+ ## Concurrency (@ModelActor)
205
+
206
+ ```swift
207
+ @ModelActor
208
+ actor DataHandler {
209
+ func importTrips(_ records: [TripRecord]) throws {
210
+ for r in records {
211
+ modelContext.insert(Trip(name: r.name, destination: r.dest,
212
+ startDate: r.start, endDate: r.end))
213
+ }
214
+ try modelContext.save() // Always save explicitly in @ModelActor
215
+ }
216
+
217
+ func process(tripID: PersistentIdentifier) throws {
218
+ guard let trip = self[tripID, as: Trip.self] else { return }
219
+ trip.isProcessed = true; try modelContext.save()
220
+ }
221
+ }
222
+
223
+ let handler = DataHandler(modelContainer: container)
224
+ try await handler.importTrips(records)
225
+ ```
226
+
227
+ **Rules**: `ModelContainer` is `Sendable`. `ModelContext` is NOT -- use on its creating actor. Pass `PersistentIdentifier` (Sendable) across boundaries. Never pass `@Model` objects across actors.
228
+
229
+ ## SwiftUI Integration
230
+
231
+ ```swift
232
+ @main
233
+ struct MyApp: App {
234
+ var body: some Scene {
235
+ WindowGroup { ContentView() }
236
+ .modelContainer(for: [Trip.self, LivingAccommodation.self])
237
+ }
238
+ }
239
+
240
+ struct DetailView: View {
241
+ @Environment(\.modelContext) private var modelContext
242
+ let trip: Trip
243
+ var body: some View {
244
+ Text(trip.name)
245
+ Button("Delete") { modelContext.delete(trip) }
246
+ }
247
+ }
248
+
249
+ #Preview {
250
+ let config = ModelConfiguration(isStoredInMemoryOnly: true)
251
+ let container = try! ModelContainer(for: Trip.self, configurations: config)
252
+ container.mainContext.insert(Trip(name: "Preview", destination: "London",
253
+ startDate: .now, endDate: .now + 86400))
254
+ return TripListView().modelContainer(container)
255
+ }
256
+ ```
257
+
258
+ ## Common Mistakes
259
+
260
+ **1. @Model on struct** -- Use class. `@Model` requires reference semantics.
261
+
262
+ **2. @Transient without default** -- Always provide default: `@Transient var x: Bool = false`.
263
+
264
+ **3. Missing .modelContainer** -- @Query returns empty without a container on the view hierarchy.
265
+
266
+ **4. Passing model objects across actors:**
267
+ ```swift
268
+ // WRONG: await handler.process(trip: trip)
269
+ // CORRECT: await handler.process(tripID: trip.persistentModelID)
270
+ ```
271
+
272
+ **5. ModelContext on wrong actor:**
273
+ ```swift
274
+ // WRONG: Task.detached { context.fetch(...) }
275
+ // CORRECT: Use @ModelActor for background work
276
+ ```
277
+
278
+ **6. Unsupported #Predicate expressions:**
279
+ ```swift
280
+ // WRONG: #Predicate<Trip> { $0.name.uppercased() == "PARIS" }
281
+ // CORRECT: #Predicate<Trip> { $0.name.localizedStandardContains("paris") }
282
+ ```
283
+
284
+ **7. Flow control in #Predicate:**
285
+ ```swift
286
+ // WRONG: #Predicate<Trip> { for tag in $0.tags { ... } }
287
+ // CORRECT: #Predicate<Trip> { $0.tags.contains { $0.name == "x" } }
288
+ ```
289
+
290
+ **8. No save in @ModelActor** -- Always call `try modelContext.save()` explicitly.
291
+
292
+ **9. ObservableObject with @Model** -- Never use `ObservableObject`/`@Published`. `@Model` generates `Observable`. Use `@Query` in views.
293
+
294
+ **10. Non-optional relationship without default:**
295
+ ```swift
296
+ // WRONG: var accommodation: LivingAccommodation // crashes on reconstitution
297
+ // CORRECT: var accommodation: LivingAccommodation?
298
+ ```
299
+
300
+ **11. Cascade without inverse** -- Specify `inverse:` for reliable cascade delete behavior.
301
+
302
+ **12. DispatchQueue for background data work:**
303
+ ```swift
304
+ // WRONG: DispatchQueue.global().async { ModelContext(container).fetch(...) }
305
+ // CORRECT: @ModelActor actor Handler { func fetch() throws { ... } }
306
+ ```
307
+
308
+ ## Review Checklist
309
+
310
+ - [ ] Every `@Model` is a class with a designated initializer
311
+ - [ ] All `@Transient` properties have default values
312
+ - [ ] Relationships specify `deleteRule` and `inverse`
313
+ - [ ] `.modelContainer` attached at scene/root view level
314
+ - [ ] `@Query` used for reactive data display in SwiftUI
315
+ - [ ] `#Predicate` uses only supported operators
316
+ - [ ] Background work uses `@ModelActor`
317
+ - [ ] `PersistentIdentifier` used across actor boundaries
318
+ - [ ] Schema changes have `VersionedSchema` + `SchemaMigrationPlan`
319
+ - [ ] Large data uses `@Attribute(.externalStorage)`
320
+ - [ ] CloudKit models use optionals and avoid unique constraints
321
+ - [ ] Explicit `save()` in `@ModelActor` methods
322
+ - [ ] Previews use `ModelConfiguration(isStoredInMemoryOnly: true)`
323
+ - [ ] `@Model` classes accessed from SwiftUI views are on `@MainActor` via `@ModelActor` or MainActor isolation
324
+
325
+ ## References
326
+
327
+ - See [references/swiftdata-advanced.md](references/swiftdata-advanced.md) for custom data stores, history
328
+ tracking, CloudKit, Core Data coexistence, composite attributes,
329
+ model inheritance, undo/redo, and performance patterns.
330
+ - See [references/swiftdata-queries.md](references/swiftdata-queries.md) for @Query variants, FetchDescriptor
331
+ deep dive, sectioned queries, dynamic queries, and background fetch patterns.
332
+ - See [references/core-data-coexistence.md](references/core-data-coexistence.md) for standalone Core Data patterns
333
+ and Core Data to SwiftData migration strategies.
334
+