diff --git a/PRD.md b/PRD.md
index 1311f6f..c2a0c4e 100644
--- a/PRD.md
+++ b/PRD.md
@@ -266,6 +266,23 @@ Implementation note: Onboarding flows and root-level shell transitions should av
| TR-DATA-04 | Use App Group shared container for widget data access |
| TR-DATA-05 | Run a startup integrity migration to normalize arc date ranges, in-progress arc state, and persisted sort indexes |
| TR-DATA-06 | Enable iCloud runtime compatibility by shipping `com.apple.developer.ubiquity-kvstore-identifier` and `remote-notification` background mode when CloudKit/KVS sync is enabled |
+| TR-DATA-07 | For SwiftData CloudKit sync, ship Push Notifications entitlement (`aps-environment`) with per-configuration environment values |
+| TR-DATA-08 | Observe `.NSPersistentStoreRemoteChange` and reload SwiftData-backed state when remote merges arrive |
+| TR-DATA-09 | Maintain a foreground sync safety-net refresh, but do not rely on app relaunch for cross-device update visibility |
+| TR-DATA-10 | Ensure UI invalidation on each remote merge so batched updates appear without manual pull-to-refresh |
+| TR-DATA-11 | Emit sync diagnostics (remote event count and timestamp) to support field debugging on physical devices |
+| TR-DATA-12 | Validate every release with a two-device real-time sync test matrix (single toggle, rapid batch toggles, background/foreground recovery) |
+
+#### 5.3.1 SwiftData to CloudKit Reuse Standard
+
+Use `SWIFTDATA_CLOUDKIT_SYNC_REQUIREMENTS.md` as the canonical implementation and QA checklist for any app that uses SwiftData with CloudKit.
+
+Standard coverage includes:
+
+- capabilities/entitlements baseline (CloudKit, Push Notifications, Remote Notifications background mode)
+- runtime remote-change handling and stale-context mitigation
+- deterministic UI refresh behavior for remote merges and rapid batched edits
+- repeatable two-device verification and CloudKit Console validation flow
### 5.4 Third-Party Dependencies
@@ -579,6 +596,7 @@ Andromida/
| `Andromida/Shared/Theme/RitualsTheme.swift` | Bedrock theme configuration |
| `Andromida/Shared/BrandingConfig.swift` | Branding constants |
| `Andromida/Shared/Configuration/Base.xcconfig` | Build configuration source of truth |
+| `SWIFTDATA_CLOUDKIT_SYNC_REQUIREMENTS.md` | Reusable SwiftData to CloudKit sync standard and test checklist |
| `Andromida/Resources/LaunchScreen.storyboard` | Native launch screen |
| `Andromida/App/State/RitualStore.swift` | Primary data store |
| `Andromida/App/State/SettingsStore.swift` | Settings with cloud sync |
@@ -609,3 +627,4 @@ Andromida/
| 1.0 | February 2026 | Initial PRD based on implemented features |
| 1.1 | February 2026 | Fixed time-of-day refresh bug in Today view and Widget; added debug time simulation |
| 1.2 | February 2026 | Added deterministic UI-test launch harness and expanded critical UI flow coverage |
+| 1.3 | February 2026 | Added reusable SwiftData to CloudKit sync requirements, runtime expectations, and two-device verification standard |
diff --git a/SWIFTDATA_CLOUDKIT_SYNC_REQUIREMENTS.md b/SWIFTDATA_CLOUDKIT_SYNC_REQUIREMENTS.md
new file mode 100644
index 0000000..322fbd6
--- /dev/null
+++ b/SWIFTDATA_CLOUDKIT_SYNC_REQUIREMENTS.md
@@ -0,0 +1,76 @@
+# SwiftData to CloudKit Sync Requirements (Reusable)
+
+Use this checklist for any iOS app that uses SwiftData with CloudKit and requires near-real-time multi-device sync.
+
+## 1) Capabilities and Entitlements
+
+- Enable `iCloud` with `CloudKit` for the app target.
+- Enable `Push Notifications` for the app target.
+- Enable `Background Modes > Remote notifications`.
+- Add App Group if app + widget share local SQLite.
+
+Required entitlement keys:
+- `com.apple.developer.icloud-container-identifiers`
+- `com.apple.developer.icloud-services` including `CloudKit`
+- `com.apple.developer.ubiquity-kvstore-identifier` (if KVS is used)
+- `aps-environment` (must resolve per config)
+- `com.apple.security.application-groups` (if widget/app group storage is used)
+
+## 2) Build Configuration
+
+Use xcconfig variables so environments are explicit and portable:
+
+- Debug: `APS_ENVIRONMENT = development`
+- Release: `APS_ENVIRONMENT = production`
+
+Entitlements should reference variables, not hard-coded values, where possible.
+
+## 3) Model and Schema Constraints (SwiftData + CloudKit)
+
+- Avoid `@Attribute(.unique)` in CloudKit-mirrored models.
+- Ensure all stored properties have defaults or are optional.
+- Keep relationships optional for CloudKit compatibility.
+- Use additive schema evolution only (add fields/models; do not remove/rename/change types in place).
+
+## 4) Runtime Sync Behavior
+
+- Observe `.NSPersistentStoreRemoteChange` to detect remote merges.
+- On remote change, refetch from SwiftData and invalidate derived caches.
+- For long-lived stores, recreate `ModelContext` on remote change before refetch when stale objects are observed.
+- Keep a foreground fallback refresh as a safety net, but do not rely on force-quit/relaunch behavior.
+- Emit structured logs for remote sync events (event count + timestamp) for debugging.
+
+## 5) UI Freshness Requirements
+
+- UI must re-render on each remote merge, even for batched updates.
+- Keep an observable refresh version/counter and increment on each successful reload.
+- Ensure list/detail views do not rely on stale assumptions when models are updated remotely.
+- Make high-frequency interaction rows fully tappable to reduce missed user actions.
+
+## 6) Verification Matrix
+
+Test all cases on two physical devices with the same Apple ID and same app flavor:
+
+1. Single toggle on Device A appears on Device B while both apps are open.
+2. Rapid batch toggles on Device A all appear on Device B without manual pull-to-refresh.
+3. Device B in background receives updates after foregrounding (without force quit).
+4. Airplane mode recovery syncs correctly after reconnection.
+5. Simultaneous edits resolve predictably (CloudKit last-writer-wins).
+
+## 7) Observability and Console Checks
+
+- Device logs: filter by sync logger category (for example `CloudKitSync`).
+- CloudKit Console: validate record updates in the app container private database.
+- If pushes are delivered but UI is stale, investigate context freshness and view invalidation, not transport.
+
+## 8) Reuse Checklist for New Apps
+
+Before shipping any new SwiftData+CloudKit app:
+
+- [ ] Capabilities: iCloud/CloudKit + Push + Remote Notifications are enabled
+- [ ] Entitlements include `aps-environment` and correct container IDs
+- [ ] xcconfig defines `APS_ENVIRONMENT` per configuration
+- [ ] Remote change observer reloads data and invalidates caches
+- [ ] UI has deterministic invalidation on remote reload
+- [ ] Two-device batch-update test passes without manual refresh
+- [ ] CloudKit Console verification documented in README/PRD
diff --git a/SWIFTDATA_CLOUDKIT_SYNC_TEMPLATE.swift.md b/SWIFTDATA_CLOUDKIT_SYNC_TEMPLATE.swift.md
new file mode 100644
index 0000000..99bea3f
--- /dev/null
+++ b/SWIFTDATA_CLOUDKIT_SYNC_TEMPLATE.swift.md
@@ -0,0 +1,188 @@
+# SwiftData to CloudKit Sync Template (Copy/Paste)
+
+Use this as a starter for new apps that need near-real-time SwiftData sync across Apple devices.
+
+## 1) Build Config (xcconfig)
+
+```xcconfig
+// Debug.xcconfig
+#include "Base.xcconfig"
+APS_ENVIRONMENT = development
+
+// Release.xcconfig
+#include "Base.xcconfig"
+APS_ENVIRONMENT = production
+```
+
+Entitlements should include:
+
+```xml
+aps-environment
+$(APS_ENVIRONMENT)
+```
+
+## 2) App Container Setup
+
+```swift
+import SwiftData
+
+enum AppIdentifiers {
+ static let appGroupIdentifier = "group.com.example.app"
+ static let cloudKitContainerIdentifier = "iCloud.com.example.app"
+}
+
+func makeModelContainer(isRunningTests: Bool) throws -> ModelContainer {
+ let schema = Schema([
+ Ritual.self,
+ RitualArc.self,
+ ArcHabit.self
+ ])
+
+ let storeURL = FileManager.default
+ .containerURL(forSecurityApplicationGroupIdentifier: AppIdentifiers.appGroupIdentifier)?
+ .appendingPathComponent("App.sqlite")
+ ?? URL.documentsDirectory.appendingPathComponent("App.sqlite")
+
+ let config = ModelConfiguration(
+ schema: schema,
+ url: storeURL,
+ cloudKitDatabase: isRunningTests ? .none : .private(AppIdentifiers.cloudKitContainerIdentifier)
+ )
+
+ return try ModelContainer(for: schema, configurations: [config])
+}
+```
+
+## 3) Store Pattern (Remote Change + Context Refresh)
+
+```swift
+import Foundation
+import Observation
+import SwiftData
+import CoreData
+import os
+
+@MainActor
+@Observable
+final class RitualStore {
+ @ObservationIgnored private let modelContainer: ModelContainer
+ @ObservationIgnored private var modelContext: ModelContext
+ @ObservationIgnored private var remoteChangeObserver: NSObjectProtocol?
+ @ObservationIgnored private let syncLogger = Logger(
+ subsystem: Bundle.main.bundleIdentifier ?? "App",
+ category: "CloudKitSync"
+ )
+
+ private(set) var rituals: [Ritual] = []
+ private(set) var dataRefreshVersion: Int = 0
+ private(set) var lastRemoteChangeDate: Date?
+ private(set) var remoteChangeEventCount: Int = 0
+
+ init(modelContext: ModelContext) {
+ self.modelContainer = modelContext.container
+ self.modelContext = modelContext
+ reloadRituals()
+ observeRemoteChanges()
+ }
+
+ deinit {
+ if let observer = remoteChangeObserver {
+ NotificationCenter.default.removeObserver(observer)
+ }
+ }
+
+ private func observeRemoteChanges() {
+ syncLogger.info("Starting CloudKit remote change observation")
+ remoteChangeObserver = NotificationCenter.default.addObserver(
+ forName: .NSPersistentStoreRemoteChange,
+ object: nil,
+ queue: .main
+ ) { [weak self] _ in
+ Task { @MainActor [weak self] in
+ self?.handleRemoteStoreChange()
+ }
+ }
+ }
+
+ private func handleRemoteStoreChange() {
+ remoteChangeEventCount += 1
+ lastRemoteChangeDate = Date()
+
+ // Important when long-lived contexts become stale after remote merges.
+ modelContext = ModelContext(modelContainer)
+
+ syncLogger.info(
+ "Received remote store change #\(self.remoteChangeEventCount, privacy: .public); reloading"
+ )
+ reloadRituals()
+ }
+
+ func refresh() {
+ reloadRituals()
+ }
+
+ private func reloadRituals() {
+ do {
+ rituals = try modelContext.fetch(FetchDescriptor())
+ dataRefreshVersion &+= 1
+ } catch {
+ syncLogger.error("Reload failed: \(error.localizedDescription, privacy: .public)")
+ }
+ }
+}
+```
+
+## 4) Root View Pattern (Safety-Net Refresh)
+
+```swift
+import SwiftUI
+
+struct RootView: View {
+ @Bindable var store: RitualStore
+ @Environment(\.scenePhase) private var scenePhase
+ @State private var fallbackRefreshTask: Task?
+
+ var body: some View {
+ // Ensure body observes remote reload increments.
+ let _ = store.dataRefreshVersion
+
+ ContentView(store: store)
+ .onChange(of: scenePhase) { _, newPhase in
+ if newPhase == .active {
+ scheduleCloudKitFallbackRefresh()
+ } else if newPhase == .background {
+ fallbackRefreshTask?.cancel()
+ }
+ }
+ }
+
+ private func scheduleCloudKitFallbackRefresh() {
+ let activationDate = Date()
+ fallbackRefreshTask?.cancel()
+ fallbackRefreshTask = Task { @MainActor in
+ try? await Task.sleep(for: .seconds(8))
+ guard !Task.isCancelled else { return }
+ let gotRemoteSinceActive = store.lastRemoteChangeDate.map { $0 >= activationDate } ?? false
+ guard !gotRemoteSinceActive else { return }
+ store.refresh()
+ }
+ }
+}
+```
+
+## 5) Verification Script (Manual)
+
+Run this release gate on two real devices:
+
+1. Single habit toggle on Device A appears on Device B with both open.
+2. Rapid 4-6 toggles on Device A all appear on Device B without pull-to-refresh.
+3. Background Device B, then foreground, data remains consistent.
+4. Device logs show `CloudKitSync` remote-change events.
+5. CloudKit Console private DB shows matching record updates.
+
+## 6) Common Failure Modes
+
+- Push works but UI stale: context/view invalidation issue.
+- Only updates after relaunch: missing remote observer or stale long-lived context.
+- Works in one build flavor only: `aps-environment` mismatch or signing/provisioning drift.
+- Partial batch updates shown: render invalidation not happening for every remote merge.