SYNC MECHANISMS AUDIT
Documentation for Plans
π Synchronization Mechanisms Audit
Date: November 1, 2025
Time: 1:15 PM
Status: β οΈ PARTIALLY IMPLEMENTED
Purpose: Audit all synchronization mechanisms for offlineβonline and guestβreal-user
π― Two Critical Sync Scenarios
Scenario 1: Offline β Online Auto-Sync
Question: When app goes from offline to online, do local changes automatically sync to backend?
Scenario 2: Guest β Real User Migration
Question: When guest user registers, does their temporary space data transfer to their real account?
π Audit Results
β IMPLEMENTED: Guest β Real User Migration
Service: GuestAuthService
File: libs/core-angular/src/lib/modules/guest-auth/guest-auth.service.ts
Status: β
FULLY IMPLEMENTED
Transfer Method
async transferToRealAccount(
tempUserId: string,
realUserId: string,
realSpaceId: string
): Promise<{ success: boolean; itemsTransferred: number; errors: string[] }>
What Gets Transferred
// Transfer data from temp space to real space:
1. β
Universes β `universes/${guestSlug}/*` β `universes/${realSpaceId}/*`
2. β
Pages β `pages/${guestSlug}/*` β `pages/${realSpaceId}/*`
3. β
Widgets β `widgets/${guestSlug}/*` β `widgets/${realSpaceId}/*`
4. β
Flows β `flows/${guestSlug}/*` β `flows/${realSpaceId}/*`
5. β
Modules β `modules/${guestSlug}/*` β `modules/${realSpaceId}/*`
6. β
Settings β `settings/${tempUserId}/*` β `settings/${realUserId}/*`
7. β
Analytics β Records conversion for tracking
Guest User Tracking
interface GuestUser {
userId: string; // temp-{uuid}
spaceSlug: string; // temp-{shortid}
createdAt: number;
expiresAt: number; // 7 days
lastActivity: number;
convertedToReal: boolean; // β
Tracks conversion
realUserId?: string; // β
Links to real account
realSpaceId?: string; // β
Links to real space
}
Automatic Cleanup
// Runs every 6 hours
async cleanupExpiredGuests(): Promise<{cleaned: number; errors: string[]}> {
// 1. Find expired guests (7+ days old)
// 2. Delete their data if not converted
// 3. Keep converted guest records for analytics
}
Verdict: β FULLY WORKING - Complete data migration on registration
β οΈ PARTIALLY IMPLEMENTED: Offline β Online Auto-Sync
What Works: Online Detection
Service: SpaceService
File: libs/core-angular/src/lib/services/space.service.ts
// β
Monitors online/offline status
private readonly _isOnline = signal(navigator.onLine);
constructor() {
window.addEventListener('online', () => this._isOnline.set(true));
window.addEventListener('offline', () => this._isOnline.set(false));
}
Status: β App knows when it goes online/offline
What Works: API-First Strategy
async updateSpace(updates: UpdateSpaceRequest): Promise<Space> {
try {
// 1. Try API first (if online)
if (this._isOnline() && this.spaceApi.isConnected()) {
const space = await this.spaceApi.updateSpace(id, updates);
await this.storage.put(`space_${space.ownerId}`, space); // Cache
return space;
}
// 2. Fallback to local storage (offline)
await this.storage.put(`space_${space.ownerId}`, updatedSpace);
return updatedSpace;
}
}
Status: β Works offline, caches when online
β What's MISSING: Background Sync Queue
Problem: When a user makes changes WHILE OFFLINE, those changes are NOT automatically synced when they come back ONLINE.
Example Failure Scenario:
1. User is offline
2. User edits space slug: "my-space" β "new-slug"
3. SpaceService.updateSpace() saves to GgStorage β
4. User goes online
5. β Change is NOT pushed to backend
6. Backend still has old slug: "my-space"
7. Next login from different device β loads old slug from backend β
β IMPLEMENTED: Asset Registry Sync Queue
Service: UniversalAssetRegistryService
File: libs/core-angular/src/lib/services/universal-asset-registry.service.ts
// β
HAS sync queue for assets!
private _syncQueue = autoFlashState<SyncQueueItem[]>('assets-sync-queue', []);
interface SyncQueueItem {
id: string;
operation: 'create' | 'update' | 'delete';
type: AssetType;
data: any;
timestamp: number;
retries: number;
}
// Automatically queues operations when offline
private async queueBackendSync(operation, type, data): Promise<void> {
const queueItem = { id, operation, type, data, timestamp, retries: 0 };
this._syncQueue.update(queue => [...queue, queueItem]);
// Try to sync immediately (will fail if offline)
this.processSyncQueue().catch(error => console.warn('Sync failed'));
}
// Processes queue with retry logic
private async processSyncQueue(): Promise<void> {
for (const item of queue) {
try {
await this.syncItemToBackend(item);
// Remove from queue on success
} catch (error) {
// Increment retry count
// Give up after 5 retries
}
}
}
Status: β Asset Registry HAS working sync queue!
π Gap Analysis
What Needs to Be Implemented
1. Space/Universe Sync Queue
Missing: SpaceService needs a sync queue like UniversalAssetRegistryService
Required Features:
// In SpaceService
private _syncQueue = autoFlashState<SpaceSyncQueueItem[]>('space-sync-queue', []);
interface SpaceSyncQueueItem {
id: string;
operation: 'update_space' | 'create_universe' | 'update_universe' | 'delete_universe';
spaceId: string;
data: any;
timestamp: number;
retries: number;
}
// Queue operations when offline
private async queueSyncOperation(operation, data) {
// Add to queue
// Try immediate sync
// Retry on failure
}
// Background processor
private async processSyncQueue() {
// Process each queued item
// Retry with exponential backoff
// Remove on success or after max retries
}
// Listen to online event
window.addEventListener('online', () => {
this._isOnline.set(true);
this.processSyncQueue(); // β
Auto-sync when online!
});
2. Conflict Resolution
Scenario: User edits same space from two devices
Device A (offline):
- Updates slug to "space-a"
- Queued for sync
Device B (online):
- Updates slug to "space-b"
- Synced immediately to backend
Device A comes online:
- Tries to sync "space-a"
- Backend has "space-b"
- β CONFLICT!
Need:
interface ConflictResolution {
strategy: 'server-wins' | 'client-wins' | 'manual' | 'merge';
onConflict: (local: Space, server: Space) => Promise<Space>;
}
// Conflict detection
private async detectConflict(localSpace: Space, serverSpace: Space): Promise<boolean> {
return localSpace.updatedAt !== serverSpace.updatedAt;
}
// Conflict resolution
private async resolveConflict(local: Space, server: Space): Promise<Space> {
switch (this.conflictStrategy) {
case 'server-wins':
return server; // Backend is source of truth
case 'client-wins':
return local; // Force push local changes
case 'merge':
return this.mergeSpaces(local, server); // Smart merge
case 'manual':
return await this.showConflictDialog(local, server); // Ask user
}
}
3. Sync Status UI
User Needs to Know:
- β³ "Syncing changes to server..."
- β "All changes synced"
- β οΈ "3 changes waiting to sync (offline)"
- β "Sync failed - will retry"
Implementation:
interface SyncStatus {
state: 'idle' | 'syncing' | 'error' | 'pending';
pendingCount: number;
lastSync: Date | null;
error: string | null;
}
// Expose sync status
readonly syncStatus = computed(() => ({
state: this._syncInProgress() ? 'syncing' :
this._syncQueue().length > 0 ? 'pending' : 'idle',
pendingCount: this._syncQueue().length,
lastSync: this._lastSyncTime(),
error: this._lastSyncError()
}));
π Implementation Plan
Phase 1: Space Sync Queue (Critical) π΄
Priority: HIGH
Impact: Users lose data when switching devices
Effort: Medium (2-3 hours)
Tasks:
- Add sync queue to
SpaceService - Queue space updates when offline
- Process queue when online
- Add retry logic with exponential backoff
- Persist queue to GgStorage (survives page reload)
Files to Create/Modify:
libs/core-angular/src/lib/services/space.service.ts(add queue)libs/core-angular/src/lib/services/space-sync-queue.service.ts(new)
Phase 2: Conflict Resolution (Important) π‘
Priority: MEDIUM
Impact: Data conflicts when editing from multiple devices
Effort: Medium (3-4 hours)
Tasks:
- Detect conflicts (compare timestamps)
- Implement "server-wins" strategy (safest default)
- Add conflict detection UI
- Log conflicts for debugging
Files to Create/Modify:
libs/core-angular/src/lib/services/space-conflict-resolver.service.ts(new)libs/core-angular/src/lib/models/sync-conflict.model.ts(new)
Phase 3: Sync Status UI (Nice to Have) π’
Priority: LOW
Impact: Better user experience
Effort: Low (1-2 hours)
Tasks:
- Create sync status component
- Show pending sync count
- Show last sync time
- Show sync errors
Files to Create/Modify:
apps/gograb-space/src/app/components/sync-status/sync-status.component.ts(new)
π― Current State Summary
| Feature | Status | Notes |
|---|---|---|
| Guest β Real User Migration | β COMPLETE | Full data transfer on registration |
| Online/Offline Detection | β COMPLETE | App knows connection state |
| API-First Strategy | β COMPLETE | Tries API, falls back to local |
| Local Caching | β COMPLETE | GgStorage with LRU cache |
| Asset Sync Queue | β COMPLETE | For pages, widgets, flows |
| Space Sync Queue | β MISSING | Need to implement |
| Conflict Resolution | β MISSING | Need to implement |
| Sync Status UI | β MISSING | Need to implement |
π§ Workarounds (Current State)
How It Works Now
Scenario: User edits slug offline
1. User offline
2. Edit slug β Saves to GgStorage β
3. Page reload β Loads from GgStorage β
(slug persists locally)
4. User goes online
5. Backend still has old slug β
6. Next API call β Backend returns old slug
7. Local slug gets OVERWRITTEN by backend slug β
Workaround: Manual re-edit after going online
Why This Isn't Terrible (Short Term)
- Most Users Stay Online: Offline edits are rare
- Guest Mode Works: GuestβReal migration works perfectly
- Data Not Lost: Everything persists locally
- Detectable: Easy to see when backend is out of sync (logs show it)
β Recommendations
For Production Launch
MUST HAVE (Block release):
- β GuestβReal migration (DONE)
- β Space sync queue (IMPLEMENT NOW)
SHOULD HAVE (Post-launch): 3. β Conflict resolution (Add within 1 week) 4. β Sync status UI (Add within 2 weeks)
Implementation Order
Week 1 (Pre-launch):
βββ Implement Space Sync Queue
βββ Test offline β online sync
βββ Test multi-device scenarios
Week 2 (Post-launch):
βββ Add conflict resolution
βββ Test conflict scenarios
βββ Monitor production logs
Week 3 (Polish):
βββ Add sync status UI
βββ Improve error messages
βββ Add retry limits
π§ͺ Testing Scenarios
Test 1: Offline Space Edit
1. Go offline (DevTools β Network β Offline)
2. Edit space slug
3. Verify: Slug persists locally β
4. Go online
5. Expected: Slug syncs to backend
6. Current: β Slug NOT synced (need queue)
Test 2: Guest Registration
1. Start as guest
2. Create universe, pages, widgets
3. Register for account
4. Expected: All data transferred
5. Current: β
ALL DATA TRANSFERRED
Test 3: Multi-Device Conflict
1. Device A offline, edit slug β "slug-a"
2. Device B online, edit slug β "slug-b"
3. Device A comes online
4. Expected: Conflict detected, resolution applied
5. Current: β Device B wins (A's change lost)
π Risk Assessment
| Scenario | Risk | Impact | Mitigation |
|---|---|---|---|
| Offline edits lost | π΄ HIGH | Data loss | Implement sync queue |
| Multi-device conflicts | π‘ MEDIUM | Confusion | Add conflict resolution |
| No sync feedback | π’ LOW | UX issue | Add status UI |
| Guest data not migrated | β NONE | Complete | Already implemented |
π Summary
β What's Working
- GuestβReal user migration (PERFECT!)
- Online/offline detection
- Local persistence (GgStorage)
- Asset sync queue (pages, widgets, flows)
β What's Missing
- Space sync queue (CRITICAL - need to implement)
- Conflict resolution (important)
- Sync status UI (nice to have)
π― Next Steps
- Implement Space Sync Queue (TODAY)
- Test offlineβonline scenarios
- Deploy and monitor
- Add conflict resolution (WEEK 2)
- Add sync status UI (WEEK 3)
Last Updated: November 1, 2025, 1:15 PM
Status: β οΈ Partially Implemented - Sync Queue Needed
Priority: π΄ HIGH - Implement Before Production Launch