omgkit 2.13.0 → 2.16.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 (138) hide show
  1. package/README.md +129 -10
  2. package/package.json +2 -2
  3. package/plugin/agents/api-designer.md +5 -0
  4. package/plugin/agents/architect.md +8 -0
  5. package/plugin/agents/brainstormer.md +4 -0
  6. package/plugin/agents/cicd-manager.md +6 -0
  7. package/plugin/agents/code-reviewer.md +6 -0
  8. package/plugin/agents/copywriter.md +2 -0
  9. package/plugin/agents/data-engineer.md +255 -0
  10. package/plugin/agents/database-admin.md +10 -0
  11. package/plugin/agents/debugger.md +10 -0
  12. package/plugin/agents/devsecops.md +314 -0
  13. package/plugin/agents/docs-manager.md +4 -0
  14. package/plugin/agents/domain-decomposer.md +181 -0
  15. package/plugin/agents/embedded-systems.md +397 -0
  16. package/plugin/agents/fullstack-developer.md +12 -0
  17. package/plugin/agents/game-systems-designer.md +375 -0
  18. package/plugin/agents/git-manager.md +10 -0
  19. package/plugin/agents/journal-writer.md +2 -0
  20. package/plugin/agents/ml-engineer.md +284 -0
  21. package/plugin/agents/observability-engineer.md +353 -0
  22. package/plugin/agents/oracle.md +9 -0
  23. package/plugin/agents/performance-engineer.md +290 -0
  24. package/plugin/agents/pipeline-architect.md +6 -0
  25. package/plugin/agents/planner.md +12 -0
  26. package/plugin/agents/platform-engineer.md +325 -0
  27. package/plugin/agents/project-manager.md +3 -0
  28. package/plugin/agents/researcher.md +5 -0
  29. package/plugin/agents/scientific-computing.md +426 -0
  30. package/plugin/agents/scout.md +3 -0
  31. package/plugin/agents/security-auditor.md +7 -0
  32. package/plugin/agents/sprint-master.md +17 -0
  33. package/plugin/agents/tester.md +10 -0
  34. package/plugin/agents/ui-ux-designer.md +12 -0
  35. package/plugin/agents/vulnerability-scanner.md +6 -0
  36. package/plugin/commands/data/pipeline.md +47 -0
  37. package/plugin/commands/data/quality.md +49 -0
  38. package/plugin/commands/domain/analyze.md +34 -0
  39. package/plugin/commands/domain/map.md +41 -0
  40. package/plugin/commands/game/balance.md +56 -0
  41. package/plugin/commands/game/optimize.md +62 -0
  42. package/plugin/commands/iot/provision.md +58 -0
  43. package/plugin/commands/ml/evaluate.md +47 -0
  44. package/plugin/commands/ml/train.md +48 -0
  45. package/plugin/commands/perf/benchmark.md +54 -0
  46. package/plugin/commands/perf/profile.md +49 -0
  47. package/plugin/commands/platform/blueprint.md +56 -0
  48. package/plugin/commands/security/audit.md +54 -0
  49. package/plugin/commands/security/scan.md +55 -0
  50. package/plugin/commands/sre/dashboard.md +53 -0
  51. package/plugin/registry.yaml +787 -0
  52. package/plugin/skills/ai-ml/experiment-tracking/SKILL.md +338 -0
  53. package/plugin/skills/ai-ml/feature-stores/SKILL.md +340 -0
  54. package/plugin/skills/ai-ml/llm-ops/SKILL.md +454 -0
  55. package/plugin/skills/ai-ml/ml-pipelines/SKILL.md +390 -0
  56. package/plugin/skills/ai-ml/model-monitoring/SKILL.md +398 -0
  57. package/plugin/skills/ai-ml/model-serving/SKILL.md +386 -0
  58. package/plugin/skills/event-driven/cqrs-patterns/SKILL.md +348 -0
  59. package/plugin/skills/event-driven/event-sourcing/SKILL.md +334 -0
  60. package/plugin/skills/event-driven/kafka-deep/SKILL.md +252 -0
  61. package/plugin/skills/event-driven/saga-orchestration/SKILL.md +335 -0
  62. package/plugin/skills/event-driven/schema-registry/SKILL.md +328 -0
  63. package/plugin/skills/event-driven/stream-processing/SKILL.md +313 -0
  64. package/plugin/skills/game/game-audio/SKILL.md +446 -0
  65. package/plugin/skills/game/game-networking/SKILL.md +490 -0
  66. package/plugin/skills/game/godot-patterns/SKILL.md +413 -0
  67. package/plugin/skills/game/shader-programming/SKILL.md +492 -0
  68. package/plugin/skills/game/unity-patterns/SKILL.md +488 -0
  69. package/plugin/skills/iot/device-provisioning/SKILL.md +405 -0
  70. package/plugin/skills/iot/edge-computing/SKILL.md +369 -0
  71. package/plugin/skills/iot/industrial-protocols/SKILL.md +438 -0
  72. package/plugin/skills/iot/mqtt-deep/SKILL.md +418 -0
  73. package/plugin/skills/iot/ota-updates/SKILL.md +426 -0
  74. package/plugin/skills/microservices/api-gateway-patterns/SKILL.md +201 -0
  75. package/plugin/skills/microservices/circuit-breaker-patterns/SKILL.md +246 -0
  76. package/plugin/skills/microservices/contract-testing/SKILL.md +284 -0
  77. package/plugin/skills/microservices/distributed-tracing/SKILL.md +246 -0
  78. package/plugin/skills/microservices/service-discovery/SKILL.md +304 -0
  79. package/plugin/skills/microservices/service-mesh/SKILL.md +181 -0
  80. package/plugin/skills/mobile-advanced/mobile-ci-cd/SKILL.md +407 -0
  81. package/plugin/skills/mobile-advanced/mobile-security/SKILL.md +403 -0
  82. package/plugin/skills/mobile-advanced/offline-first/SKILL.md +473 -0
  83. package/plugin/skills/mobile-advanced/push-notifications/SKILL.md +494 -0
  84. package/plugin/skills/mobile-advanced/react-native-deep/SKILL.md +374 -0
  85. package/plugin/skills/simulation/numerical-methods/SKILL.md +434 -0
  86. package/plugin/skills/simulation/parallel-computing/SKILL.md +382 -0
  87. package/plugin/skills/simulation/physics-engines/SKILL.md +377 -0
  88. package/plugin/skills/simulation/validation-verification/SKILL.md +479 -0
  89. package/plugin/skills/simulation/visualization-scientific/SKILL.md +365 -0
  90. package/plugin/stdrules/ALIGNMENT_PRINCIPLE.md +240 -0
  91. package/plugin/workflows/ai-engineering/agent-development.md +3 -3
  92. package/plugin/workflows/ai-engineering/fine-tuning.md +3 -3
  93. package/plugin/workflows/ai-engineering/model-evaluation.md +3 -3
  94. package/plugin/workflows/ai-engineering/prompt-engineering.md +2 -2
  95. package/plugin/workflows/ai-engineering/rag-development.md +4 -4
  96. package/plugin/workflows/ai-ml/data-pipeline.md +188 -0
  97. package/plugin/workflows/ai-ml/experiment-cycle.md +203 -0
  98. package/plugin/workflows/ai-ml/feature-engineering.md +208 -0
  99. package/plugin/workflows/ai-ml/model-deployment.md +199 -0
  100. package/plugin/workflows/ai-ml/monitoring-setup.md +227 -0
  101. package/plugin/workflows/api/api-design.md +1 -1
  102. package/plugin/workflows/api/api-testing.md +2 -2
  103. package/plugin/workflows/content/technical-docs.md +1 -1
  104. package/plugin/workflows/database/migration.md +1 -1
  105. package/plugin/workflows/database/optimization.md +1 -1
  106. package/plugin/workflows/database/schema-design.md +3 -3
  107. package/plugin/workflows/development/bug-fix.md +3 -3
  108. package/plugin/workflows/development/code-review.md +2 -1
  109. package/plugin/workflows/development/feature.md +3 -3
  110. package/plugin/workflows/development/refactor.md +2 -2
  111. package/plugin/workflows/event-driven/consumer-groups.md +190 -0
  112. package/plugin/workflows/event-driven/event-storming.md +172 -0
  113. package/plugin/workflows/event-driven/replay-testing.md +186 -0
  114. package/plugin/workflows/event-driven/saga-implementation.md +206 -0
  115. package/plugin/workflows/event-driven/schema-evolution.md +173 -0
  116. package/plugin/workflows/fullstack/authentication.md +4 -4
  117. package/plugin/workflows/fullstack/full-feature.md +4 -4
  118. package/plugin/workflows/game-dev/content-pipeline.md +218 -0
  119. package/plugin/workflows/game-dev/platform-submission.md +263 -0
  120. package/plugin/workflows/game-dev/playtesting.md +237 -0
  121. package/plugin/workflows/game-dev/prototype-to-production.md +205 -0
  122. package/plugin/workflows/microservices/contract-first.md +151 -0
  123. package/plugin/workflows/microservices/distributed-tracing.md +166 -0
  124. package/plugin/workflows/microservices/domain-decomposition.md +123 -0
  125. package/plugin/workflows/microservices/integration-testing.md +149 -0
  126. package/plugin/workflows/microservices/service-mesh-setup.md +153 -0
  127. package/plugin/workflows/microservices/service-scaffolding.md +151 -0
  128. package/plugin/workflows/omega/1000x-innovation.md +2 -2
  129. package/plugin/workflows/omega/100x-architecture.md +2 -2
  130. package/plugin/workflows/omega/10x-improvement.md +2 -2
  131. package/plugin/workflows/quality/performance-optimization.md +2 -2
  132. package/plugin/workflows/research/best-practices.md +1 -1
  133. package/plugin/workflows/research/technology-research.md +1 -1
  134. package/plugin/workflows/security/penetration-testing.md +3 -3
  135. package/plugin/workflows/security/security-audit.md +3 -3
  136. package/plugin/workflows/sprint/sprint-execution.md +2 -2
  137. package/plugin/workflows/sprint/sprint-retrospective.md +1 -1
  138. package/plugin/workflows/sprint/sprint-setup.md +1 -1
@@ -0,0 +1,473 @@
1
+ # Offline-First
2
+
3
+ Local-first architecture, data synchronization, conflict resolution, and offline data persistence strategies.
4
+
5
+ ## Overview
6
+
7
+ Offline-first design ensures applications work seamlessly without network connectivity, syncing data when connections are restored.
8
+
9
+ ## Core Concepts
10
+
11
+ ### Offline-First Principles
12
+ - **Local First**: Data stored locally before remote
13
+ - **Background Sync**: Automatic synchronization
14
+ - **Conflict Resolution**: Handle concurrent edits
15
+ - **Optimistic UI**: Assume operations succeed
16
+
17
+ ### Sync Strategies
18
+ - **Full Sync**: Download complete dataset
19
+ - **Incremental Sync**: Only changes since last sync
20
+ - **Real-time Sync**: Continuous bidirectional
21
+ - **Manual Sync**: User-triggered
22
+
23
+ ## Local Database
24
+
25
+ ### WatermelonDB (React Native)
26
+ ```typescript
27
+ import { Database, Model, Q } from '@nozbe/watermelondb';
28
+ import { field, date, children, relation } from '@nozbe/watermelondb/decorators';
29
+ import SQLiteAdapter from '@nozbe/watermelondb/adapters/sqlite';
30
+
31
+ // Schema
32
+ const schema = appSchema({
33
+ version: 1,
34
+ tables: [
35
+ tableSchema({
36
+ name: 'tasks',
37
+ columns: [
38
+ { name: 'title', type: 'string' },
39
+ { name: 'description', type: 'string', isOptional: true },
40
+ { name: 'is_completed', type: 'boolean' },
41
+ { name: 'project_id', type: 'string', isIndexed: true },
42
+ { name: 'created_at', type: 'number' },
43
+ { name: 'updated_at', type: 'number' },
44
+ { name: 'synced_at', type: 'number', isOptional: true },
45
+ { name: 'is_dirty', type: 'boolean' }
46
+ ]
47
+ }),
48
+ tableSchema({
49
+ name: 'projects',
50
+ columns: [
51
+ { name: 'name', type: 'string' },
52
+ { name: 'color', type: 'string' },
53
+ { name: 'synced_at', type: 'number', isOptional: true }
54
+ ]
55
+ })
56
+ ]
57
+ });
58
+
59
+ // Models
60
+ class Task extends Model {
61
+ static table = 'tasks';
62
+ static associations = {
63
+ projects: { type: 'belongs_to', key: 'project_id' }
64
+ };
65
+
66
+ @field('title') title!: string;
67
+ @field('description') description?: string;
68
+ @field('is_completed') isCompleted!: boolean;
69
+ @field('is_dirty') isDirty!: boolean;
70
+ @date('created_at') createdAt!: Date;
71
+ @date('updated_at') updatedAt!: Date;
72
+ @date('synced_at') syncedAt?: Date;
73
+ @relation('projects', 'project_id') project!: Project;
74
+
75
+ async markComplete() {
76
+ await this.update(task => {
77
+ task.isCompleted = true;
78
+ task.isDirty = true;
79
+ });
80
+ }
81
+ }
82
+
83
+ // Database setup
84
+ const adapter = new SQLiteAdapter({
85
+ schema,
86
+ migrations: [],
87
+ jsi: true,
88
+ onSetUpError: error => {
89
+ console.error('Database setup failed:', error);
90
+ }
91
+ });
92
+
93
+ const database = new Database({
94
+ adapter,
95
+ modelClasses: [Task, Project]
96
+ });
97
+ ```
98
+
99
+ ### Queries and Operations
100
+ ```typescript
101
+ class TaskRepository {
102
+ private collection = database.get<Task>('tasks');
103
+
104
+ async create(data: TaskInput): Promise<Task> {
105
+ return await database.write(async () => {
106
+ return await this.collection.create(task => {
107
+ task.title = data.title;
108
+ task.description = data.description;
109
+ task.isCompleted = false;
110
+ task.isDirty = true;
111
+ task.createdAt = new Date();
112
+ task.updatedAt = new Date();
113
+ });
114
+ });
115
+ }
116
+
117
+ async getByProject(projectId: string): Promise<Task[]> {
118
+ return await this.collection
119
+ .query(Q.where('project_id', projectId))
120
+ .fetch();
121
+ }
122
+
123
+ async getPendingSync(): Promise<Task[]> {
124
+ return await this.collection
125
+ .query(Q.where('is_dirty', true))
126
+ .fetch();
127
+ }
128
+
129
+ async markSynced(task: Task): Promise<void> {
130
+ await database.write(async () => {
131
+ await task.update(t => {
132
+ t.isDirty = false;
133
+ t.syncedAt = new Date();
134
+ });
135
+ });
136
+ }
137
+ }
138
+ ```
139
+
140
+ ## Sync Engine
141
+
142
+ ### Bidirectional Sync
143
+ ```typescript
144
+ interface SyncResult {
145
+ pushed: number;
146
+ pulled: number;
147
+ conflicts: ConflictRecord[];
148
+ }
149
+
150
+ class SyncEngine {
151
+ private api: ApiClient;
152
+ private db: Database;
153
+ private lastSyncTimestamp: number = 0;
154
+
155
+ async sync(): Promise<SyncResult> {
156
+ const result: SyncResult = { pushed: 0, pulled: 0, conflicts: [] };
157
+
158
+ try {
159
+ // 1. Push local changes
160
+ const localChanges = await this.getLocalChanges();
161
+ const pushResult = await this.pushChanges(localChanges);
162
+ result.pushed = pushResult.success.length;
163
+ result.conflicts.push(...pushResult.conflicts);
164
+
165
+ // 2. Pull remote changes
166
+ const remoteChanges = await this.api.getChanges(this.lastSyncTimestamp);
167
+ const pullResult = await this.applyRemoteChanges(remoteChanges);
168
+ result.pulled = pullResult.applied;
169
+ result.conflicts.push(...pullResult.conflicts);
170
+
171
+ // 3. Update sync timestamp
172
+ this.lastSyncTimestamp = Date.now();
173
+ await this.saveSyncState();
174
+
175
+ return result;
176
+ } catch (error) {
177
+ console.error('Sync failed:', error);
178
+ throw new SyncError('Sync failed', error);
179
+ }
180
+ }
181
+
182
+ private async getLocalChanges(): Promise<Change[]> {
183
+ const changes: Change[] = [];
184
+
185
+ for (const table of ['tasks', 'projects']) {
186
+ const dirtyRecords = await this.db
187
+ .get(table)
188
+ .query(Q.where('is_dirty', true))
189
+ .fetch();
190
+
191
+ for (const record of dirtyRecords) {
192
+ changes.push({
193
+ table,
194
+ id: record.id,
195
+ operation: record._raw._status === 'created' ? 'create' : 'update',
196
+ data: record._raw,
197
+ timestamp: record.updatedAt.getTime()
198
+ });
199
+ }
200
+ }
201
+
202
+ return changes;
203
+ }
204
+
205
+ private async pushChanges(changes: Change[]): Promise<PushResult> {
206
+ const result = await this.api.pushChanges(changes);
207
+
208
+ // Mark successfully synced records
209
+ await this.db.write(async () => {
210
+ for (const success of result.success) {
211
+ const record = await this.db.get(success.table).find(success.id);
212
+ await record.update(r => {
213
+ r.isDirty = false;
214
+ r.syncedAt = new Date();
215
+ });
216
+ }
217
+ });
218
+
219
+ return result;
220
+ }
221
+
222
+ private async applyRemoteChanges(changes: RemoteChange[]): Promise<PullResult> {
223
+ const conflicts: ConflictRecord[] = [];
224
+ let applied = 0;
225
+
226
+ await this.db.write(async () => {
227
+ for (const change of changes) {
228
+ const collection = this.db.get(change.table);
229
+
230
+ try {
231
+ const existing = await collection.find(change.id).catch(() => null);
232
+
233
+ if (existing && existing.isDirty) {
234
+ // Conflict: local and remote both modified
235
+ const resolution = await this.resolveConflict(existing, change);
236
+ conflicts.push({ local: existing._raw, remote: change, resolution });
237
+
238
+ if (resolution === 'remote') {
239
+ await existing.update(r => Object.assign(r, change.data));
240
+ }
241
+ } else if (existing) {
242
+ // Update existing
243
+ await existing.update(r => Object.assign(r, change.data));
244
+ } else {
245
+ // Create new
246
+ await collection.create(r => Object.assign(r, change.data));
247
+ }
248
+
249
+ applied++;
250
+ } catch (error) {
251
+ console.error(`Failed to apply change ${change.id}:`, error);
252
+ }
253
+ }
254
+ });
255
+
256
+ return { applied, conflicts };
257
+ }
258
+ }
259
+ ```
260
+
261
+ ## Conflict Resolution
262
+
263
+ ### Resolution Strategies
264
+ ```typescript
265
+ type ConflictStrategy = 'last-write-wins' | 'client-wins' | 'server-wins' | 'merge' | 'manual';
266
+
267
+ class ConflictResolver {
268
+ private strategy: ConflictStrategy;
269
+
270
+ constructor(strategy: ConflictStrategy = 'last-write-wins') {
271
+ this.strategy = strategy;
272
+ }
273
+
274
+ resolve<T extends Record<string, any>>(
275
+ local: T,
276
+ remote: T,
277
+ base?: T
278
+ ): { result: T; strategy: string } {
279
+ switch (this.strategy) {
280
+ case 'last-write-wins':
281
+ return this.lastWriteWins(local, remote);
282
+
283
+ case 'client-wins':
284
+ return { result: local, strategy: 'client-wins' };
285
+
286
+ case 'server-wins':
287
+ return { result: remote, strategy: 'server-wins' };
288
+
289
+ case 'merge':
290
+ return this.mergeFields(local, remote, base);
291
+
292
+ case 'manual':
293
+ throw new ManualResolutionRequired(local, remote);
294
+ }
295
+ }
296
+
297
+ private lastWriteWins<T extends { updatedAt: number }>(
298
+ local: T,
299
+ remote: T
300
+ ): { result: T; strategy: string } {
301
+ return {
302
+ result: local.updatedAt > remote.updatedAt ? local : remote,
303
+ strategy: 'last-write-wins'
304
+ };
305
+ }
306
+
307
+ private mergeFields<T extends Record<string, any>>(
308
+ local: T,
309
+ remote: T,
310
+ base?: T
311
+ ): { result: T; strategy: string } {
312
+ const result = { ...remote };
313
+
314
+ for (const key of Object.keys(local)) {
315
+ const localValue = local[key];
316
+ const remoteValue = remote[key];
317
+ const baseValue = base?.[key];
318
+
319
+ // If local changed and remote didn't, use local
320
+ if (localValue !== baseValue && remoteValue === baseValue) {
321
+ result[key] = localValue;
322
+ }
323
+ // If both changed to same value, use either
324
+ else if (localValue === remoteValue) {
325
+ result[key] = localValue;
326
+ }
327
+ // If both changed to different values, use remote (or mark conflict)
328
+ }
329
+
330
+ return { result: result as T, strategy: 'merge' };
331
+ }
332
+ }
333
+ ```
334
+
335
+ ## Network Detection
336
+
337
+ ### Connection Monitoring
338
+ ```typescript
339
+ import NetInfo, { NetInfoState } from '@react-native-community/netinfo';
340
+
341
+ class NetworkMonitor {
342
+ private listeners: Set<(isConnected: boolean) => void> = new Set();
343
+ private isConnected: boolean = true;
344
+ private unsubscribe?: () => void;
345
+
346
+ start(): void {
347
+ this.unsubscribe = NetInfo.addEventListener(state => {
348
+ this.handleStateChange(state);
349
+ });
350
+ }
351
+
352
+ stop(): void {
353
+ this.unsubscribe?.();
354
+ }
355
+
356
+ private handleStateChange(state: NetInfoState): void {
357
+ const wasConnected = this.isConnected;
358
+ this.isConnected = state.isConnected ?? false;
359
+
360
+ if (!wasConnected && this.isConnected) {
361
+ // Back online - trigger sync
362
+ this.notifyListeners(true);
363
+ this.triggerSync();
364
+ } else if (wasConnected && !this.isConnected) {
365
+ this.notifyListeners(false);
366
+ }
367
+ }
368
+
369
+ private async triggerSync(): Promise<void> {
370
+ try {
371
+ await syncEngine.sync();
372
+ } catch (error) {
373
+ console.error('Auto-sync failed:', error);
374
+ }
375
+ }
376
+
377
+ onConnectionChange(callback: (isConnected: boolean) => void): () => void {
378
+ this.listeners.add(callback);
379
+ return () => this.listeners.delete(callback);
380
+ }
381
+
382
+ private notifyListeners(isConnected: boolean): void {
383
+ this.listeners.forEach(listener => listener(isConnected));
384
+ }
385
+ }
386
+ ```
387
+
388
+ ## Optimistic Updates
389
+
390
+ ### React Hook
391
+ ```typescript
392
+ function useOptimisticMutation<T, R>(
393
+ mutationFn: (data: T) => Promise<R>,
394
+ options: {
395
+ onOptimisticUpdate: (data: T) => void;
396
+ onSuccess?: (result: R) => void;
397
+ onError?: (error: Error, data: T) => void;
398
+ onRollback: (data: T) => void;
399
+ }
400
+ ) {
401
+ const [isPending, setIsPending] = useState(false);
402
+
403
+ const mutate = useCallback(async (data: T) => {
404
+ setIsPending(true);
405
+
406
+ // Optimistic update
407
+ options.onOptimisticUpdate(data);
408
+
409
+ try {
410
+ const result = await mutationFn(data);
411
+ options.onSuccess?.(result);
412
+ return result;
413
+ } catch (error) {
414
+ // Rollback on failure
415
+ options.onRollback(data);
416
+ options.onError?.(error as Error, data);
417
+ throw error;
418
+ } finally {
419
+ setIsPending(false);
420
+ }
421
+ }, [mutationFn, options]);
422
+
423
+ return { mutate, isPending };
424
+ }
425
+
426
+ // Usage
427
+ const { mutate: completeTask } = useOptimisticMutation(
428
+ (task: Task) => api.updateTask(task.id, { completed: true }),
429
+ {
430
+ onOptimisticUpdate: (task) => {
431
+ setTasks(prev => prev.map(t =>
432
+ t.id === task.id ? { ...t, completed: true } : t
433
+ ));
434
+ },
435
+ onRollback: (task) => {
436
+ setTasks(prev => prev.map(t =>
437
+ t.id === task.id ? { ...t, completed: false } : t
438
+ ));
439
+ }
440
+ }
441
+ );
442
+ ```
443
+
444
+ ## Best Practices
445
+
446
+ 1. **Queue Operations**: Store pending ops for retry
447
+ 2. **Idempotent Sync**: Handle duplicate syncs safely
448
+ 3. **Conflict UI**: Show conflicts to users clearly
449
+ 4. **Partial Sync**: Don't require full dataset
450
+ 5. **Background Sync**: Sync when app not active
451
+
452
+ ## Anti-Patterns
453
+
454
+ - Blocking UI during sync
455
+ - No conflict handling
456
+ - Full dataset sync every time
457
+ - Ignoring sync failures
458
+ - No offline indicator
459
+
460
+ ## When to Use
461
+
462
+ - Field/remote work apps
463
+ - Note-taking apps
464
+ - Collaborative tools
465
+ - Apps for unreliable networks
466
+ - Real-time collaboration
467
+
468
+ ## When NOT to Use
469
+
470
+ - Real-time only features
471
+ - Always-online requirements
472
+ - Simple read-only apps
473
+ - No data persistence needed