Enhanced Phantom Universe Cleanup Solution

Comprehensive fix for phantom universe IDs persisting across browsers

Enhanced Phantom Universe Cleanup Solution

Problem Statement

External browsers were continuing to reference deleted universes, causing:

Root Cause

The sync engine was not properly cleaning up phantom universe IDs when it detected that universes no longer exist in MongoDB. While the previous fix handled the client-side cleanup, it didn't update the sync engine to properly propagate these cleanups across all browsers.

Solution Implementation

1. Enhanced Sync Engine Cleanup

File: libs/libs-foundational/core-angular/src/lib/services/sync-engine.service.ts

// Track universes to remove during sync
const universesToRemove: string[] = [];

// When universe not found (404 or null response)
if (!backendUniverse || error.status === 404) {
  universesToRemove.push(universeId);
  // Remove from local storage
  await this.storage.remove(universeKey);
  await this.storage.remove(layoutKey);
}

// After checking all universes, clean up space.universeIds
if (universesToRemove.length > 0) {
  mergedSpace.universeIds = mergedSpace.universeIds.filter(
    id => !universesToRemove.includes(id)
  );
  
  // Save cleaned space and queue sync
  await this.storage.put(spaceKey, mergedSpace);
  await this.storage.put(spaceSlugKey, mergedSpace);
  
  // Queue update to sync cleanup to backend
  await this.queueOperation({
    id: `space-cleanup-${Date.now()}`,
    operation: 'update',
    entity: 'space',
    key: spaceKey,
    data: mergedSpace,
    timestamp: Date.now(),
    retries: 0
  });
}

2. Enhanced Delete Universe Method

File: libs/libs-foundational/core-angular/src/lib/services/space.service.ts

async deleteUniverse(universeId: string): Promise<void> {
  // ... existing delete logic ...
  
  // Also remove layout data
  const layoutKey = `modular-universe/layout/${universeId}/universe`;
  await this.storage.remove(layoutKey);
  
  // Broadcast deletion to other tabs/browsers
  try {
    this.realtimeSync.broadcastUniverseUpdate(universeId, {
      action: 'delete',
      universeId
    });
  } catch (broadcastError) {
    // Optional broadcast, don't fail if not available
  }
}

3. Proactive Phantom Cleanup

File: libs/libs-foundational/core-angular/src/lib/services/space.service.ts

private async cleanupPhantomUniverseIds(): Promise<void> {
  const space = this._currentSpace();
  if (!space) return;
  
  const validIds: string[] = [];
  const phantomIds: string[] = [];
  
  // Check each universe ID for actual data
  for (const universeId of space.universeIds) {
    const universeData = await this.storage.get(`universe_${universeId}`);
    if (universeData) {
      validIds.push(universeId);
    } else {
      phantomIds.push(universeId);
    }
  }
  
  // Clean up if phantoms found
  if (phantomIds.length > 0) {
    space.universeIds = validIds;
    space.updatedAt = new Date().toISOString();
    
    await this.syncEngine.write(`space_${space.ownerId}`, space, 'space');
    await this.cacheSpaceSlugVariant(space);
    
    this._currentSpace.set(space);
  }
}

// Called automatically in loadUniverses() when phantoms detected

Key Improvements

  1. Bidirectional Cleanup: Sync engine now cleans phantom IDs when pulling from backend
  2. Layout Cleanup: Properly removes both universe data AND layout data
  3. Broadcast Deletion: Notifies other tabs/browsers immediately
  4. Proactive Detection: Automatically detects and cleans phantoms during load
  5. Queue Sync: Ensures cleanup propagates to backend and other browsers

Testing

  1. Create a universe in Browser A
  2. Delete the universe in Browser A
  3. Check Browser B - universe should disappear
  4. Refresh Browser B - deleted universe should not reappear
  5. Check MongoDB - space.universeIds should not contain deleted ID

Benefits

Related Documentation