Universe State Isolation Fix
Fixed cross-universe state pollution in Vibe Universe Generator
Universe State Isolation Fix
Problem: Generator "Stuck" Showing Stale State
Symptoms
- Generate platform in Universe A → Success screen shows
- Switch to Universe B (new/empty) → Same success screen appears
- Generator appears "stuck" with wrong universe's generation state
- Cannot start new generation without manual refresh
Root Cause
SessionStorage key was NOT universe-scoped:
// BEFORE (Bug):
private readonly GENERATION_STATE_KEY = 'vibe-universe:generation-state';
// Problem: Same key for ALL universes
// Universe A state → Universe B restoration = wrong state!
When restoreGenerationState() ran in ngOnInit(), it used the global key and restored whatever was last saved, regardless of which universe you're in.
Solution
1. Universe-Scoped Storage Keys
// AFTER (Fixed):
private getGenerationStateKey(universeId: string): string {
return `vibe-universe:${universeId}:generation-state`;
}
// Each universe has its own state:
// - 'vibe-universe:universe_123:generation-state'
// - 'vibe-universe:universe_456:generation-state'
Changes:
restoreGenerationState(): Reads from universe-scoped keypersistGenerationState(): Writes to universe-scoped keyresetGenerationState(): Clears universe-scoped key
2. Universe Switch Detection
Added an effect that watches universeContext.universeId() and automatically resets the generator when switching universes:
effect(() => {
const currentUniverseId = this.universeContext.universeId();
if (!currentUniverseId) {
this.lastUniverseId = null;
return;
}
// Universe switched - clear generator to fresh state
if (this.lastUniverseId && currentUniverseId !== this.lastUniverseId) {
this.logger.info('🔄 Universe switched, resetting generator', {
from: this.lastUniverseId,
to: currentUniverseId
});
// Clear stale state completely
this.mode.set('input');
this.workflowState.reset();
this.modulesGenerated.set([]);
this.pendingAutoSync.set(false);
// Restore state for the NEW universe
this.restoreGenerationState();
}
this.lastUniverseId = currentUniverseId;
});
3. Auto-Sync Protection
Enhanced the pendingAutoSync retry effect to prevent cross-universe pollution:
effect(() => {
if (!this.pendingAutoSync()) return;
const universeId = this.universeContext.universeId();
const manifest = this.generatedManifest();
// CRITICAL: Only retry if manifest belongs to current universe
if (this.lastUniverseId !== universeId) {
this.logger.warn('⚠️ Skipping auto-sync retry - universe mismatch');
this.pendingAutoSync.set(false);
return;
}
this.autoSyncModulesToCanvas();
});
Additional Improvements
Empty Canvas Save Protection
File: canvas-builder.component.ts
Added double-check with backend before allowing empty saves:
if (!options?.allowEmpty && modules.length === 0) {
const existingStructure = this.universeModuleService.structure(universeId)();
if (existingStructure.length > 0) {
return; // Signal says there's structure - skip save
}
// Double-check with backend (signal might be stale)
const backendModules = await firstValueFrom(
this.universeModuleService.load(universeId, { force: true })
);
if (backendModules.length > 0) {
this.logger.warn('⚠️ Skipping empty save - backend has modules');
return;
}
}
This prevents:
- Canvas from wiping structure if signal is stale
- Race conditions during Vibe → Canvas handoffs
Structure Signal Sync
File: universe-module.service.ts
When loading modules, also update the structure signal for consistency:
private loadFlatArchitecture(universeId: string): Observable<UniversalModule[]> {
return this.spaceApi.hydrateUniverseStructure(space.id, universeId).pipe(
map(modules => {
const treeSignal = this.getSignal(universeId);
const structureSignal = this.getStructureSignal(universeId);
treeSignal.set(modules as UniversalModule[]);
// NEW: Also update structure signal for consistency
const structure = this.moduleTreeToFlat(modules, space.id, universeId).structure;
structureSignal.set(structure);
return modules;
})
);
}
Testing Checklist
- Generate platform in Universe A
- Verify success screen shows
- Switch to Universe B (empty)
- Verify generator shows input screen (not stuck with A's success)
- Switch back to Universe A
- Verify success screen still shows (state restored)
- Refresh page in Universe A
- Verify success screen survives refresh
- Switch to Universe B after refresh
- Verify Universe B still shows input screen
Impact
Before:
- ❌ Generator showed wrong state across universes
- ❌ Appeared "stuck" after switching
- ❌ Cross-universe state pollution
- ❌ Manual refresh required to unstick
After:
- ✅ Each universe has isolated state
- ✅ Automatic reset on universe switch
- ✅ State persistence survives refresh (per universe)
- ✅ No cross-universe pollution
Files Changed
vibe-universe-generator.component.ts- Universe-scoped keys + switch detectioncanvas-builder.component.ts- Enhanced empty save protectionuniverse-module.service.ts- Structure signal sync on load
Related Issues Fixed
- Generator "stuck" after universe switch
- Empty canvas overwriting generated structure
- Cross-universe manifest pollution in auto-sync