Backend-First State Audit

Audit of each tool's persistence model and plan to wire all tools to backend APIs

Backend-First State Audit - Session 136

Audit Results

Already Backend-First (Good)

Tool Service Backend Endpoint Notes
vibe-universe PageContentGeneratorService POST /api/spaces/:spaceId/pages Saves generated pages to backend
modularity-lib ModuleSyncService /api/spaces/:spaceId/modules Syncs modules to backend
modularity-lib ModuleExportApiService /api/spaces/:spaceId/modules/export Exports to backend
page-builder PageSaveService ${API_BASE_URL}/pages/:pageId Saves pages to backend
page-builder PageDeploymentService /api/deployment/:tenantId/deploy-bundle Deploys to backend

Local-Only (Need Backend Wiring)

Tool Service Current Storage Target Backend Endpoint
modularity-lib ModuleStorageService autoFlashState (IndexedDB) /api/spaces/:spaceId/modules
universe-canvas PlatformStateService autoFlashState (IndexedDB) /api/spaces/:spaceId/universes/:universeId
universe-canvas PlatformDeploymentService GgStorage /api/universes/:id/publish
universe-canvas CanvasStateService GgStorage /api/spaces/:spaceId/universes/:universeId (structure)
universe-explorer ArchitectStateService createFlashState /api/spaces/:spaceId/universes/:universeId (structure)
modular-universe ModuleInstallationService GgStorage /api/spaces/:spaceId/modules
flow-engine-lib BackendFlowClientService GgStorage + partial sync /api/spaces/:spaceId/universes/:universeId/data/*

Pattern to Follow

Each local-only tool should implement the Session 129 pattern:

@Injectable({ providedIn: 'root' })
export class ExampleApiService {
  private readonly http = inject(HttpClient);
  private readonly apiBaseUrl = inject(API_BASE_URL);
  
  // Backend data in a SEPARATE signal (not autoFlashState)
  private readonly _backendData = signal<MyData | null>(null);
  
  // Local state can still use autoFlashState for offline/optimistic UI
  private readonly _localState = autoFlashState<MyData>('example-key', defaultValue);
  
  // Merged view: backend takes priority
  readonly data = computed(() => {
    const backend = this._backendData();
    const local = this._localState();
    return backend ?? local;
  });
  
  loadFromBackend(): Observable<MyData> {
    return this.http.get<MyData>(`${this.apiBaseUrl}/endpoint`).pipe(
      tap(data => this._backendData.set(data))
    );
  }
  
  saveToBackend(data: MyData): Observable<MyData> {
    return this.http.put<MyData>(`${this.apiBaseUrl}/endpoint`, data).pipe(
      tap(saved => {
        this._backendData.set(saved);
        this._localState.set(saved); // Keep local in sync
      })
    );
  }
}

Priority Order for Wiring

  1. flow-engine-lib - Model citizen (Phase 2 already started this)
  2. universe-canvas - Most impactful (canvas state = platform structure)
  3. universe-explorer - Architect state maps to universe structure
  4. modularity-lib ModuleStorageService - Module context should persist
  5. modular-universe - Module installation data

Existing Backend Endpoints Available

All needed endpoints already exist in backend-api:

Endpoint Supports
/api/spaces/:id/universes (CRUD) Universe structure
/api/spaces/:id/universes/:id/galaxies (CRUD) Galaxy management
/api/spaces/:id/modules (CRUD + batch) Module persistence
/api/spaces/:id/space-pages (CRUD) Page persistence
/api/spaces/:id/widgets (CRUD) Widget persistence
/api/spaces/:id/universes/:id/data/* (dynamic CRUD) Flow engine data
/api/spaces/:id/universes/:id/data/schemas (CRUD) Entity schemas
/api/universes/:id/publish Deployment
/api/modules/unified/all Module discovery

No new backend endpoints are needed. The gap is purely on the frontend: services need to be updated to call these endpoints instead of (or in addition to) local storage.