Phase 1 - Data Unification

Creating the single source of truth for all platform data

Phase 1: Data Unification

Priority: P0 - CRITICAL
Duration: 2-3 days
Status: ๐ŸŸข IN PROGRESS
Started: October 28, 2025


๐ŸŽฏ Objectives

Create UnifiedDataService as the single source of truth for all platform data, eliminating data islands and enabling seamless cross-tool communication.

Problems Being Solved

  1. Launch Station Data Disconnect

    • Currently shows "No Universes"
    • Not using SpaceService or UniverseManagerService
    • Isolated data management
  2. Modularity Can't See Data

    • Cannot browse pages from Editor
    • Cannot browse flows from Singularity
    • Must manually track what exists
  3. Dashboard Lacks Insights

    • No cross-universe statistics
    • Cannot show space-level analytics
    • Missing data aggregations
  4. Data Duplication

    • Each tool loads its own data
    • Multiple sources of truth
    • Inconsistency issues

๐Ÿ“‹ Implementation Checklist

Files to Create

Files to Modify


๐Ÿ—๏ธ Architecture

UnifiedDataService Structure

@Injectable({ providedIn: 'root' })
export class UnifiedDataService {
  // Core data signals
  private _spaces = signal<Space[]>([]);
  private _universes = signal<UniverseWithContent[]>([]);
  private _pages = signal<WorldWithMetadata[]>([]);
  private _flows = signal<FlowWithMetadata[]>([]);
  private _modules = signal<ModuleWithAssets[]>([]);
  private _widgets = signal<SpaceWidget[]>([]);
  
  // Read-only accessors
  readonly spaces = this._spaces.asReadonly();
  readonly universes = this._universes.asReadonly();
  readonly pages = this._pages.asReadonly();
  readonly flows = this._flows.asReadonly();
  readonly modules = this._modules.asReadonly();
  readonly widgets = this._widgets.asReadonly();
  
  // Computed cross-references
  readonly universesWithContent = computed(() => { /* ... */ });
  readonly modulesWithAssets = computed(() => { /* ... */ });
  readonly pagesWithFlows = computed(() => { /* ... */ });
  
  // Methods
  async loadSpaceData(spaceSlug: string): Promise<void> { /* ... */ }
  async updatePage(pageId: string, updates: Partial<World>): Promise<void> { /* ... */ }
  async attachFlowToPage(pageId: string, flowId: string): Promise<void> { /* ... */ }
}

Extended Types

// UniverseWithContent - Universe + aggregated counts
interface UniverseWithContent extends Universe {
  pageCount: number;
  moduleCount: number;
  flowCount: number;
  galaxyCount: number;
}

// WorldWithMetadata - Page + additional metadata
interface WorldWithMetadata extends World {
  universeId: string;
  universeName: string;
  attachedFlowIds?: string[];
  attachedFlowCount: number;
  widgetCount: number;
  lastModified: Date;
}

// FlowWithMetadata - Flow + usage data
interface FlowWithMetadata extends Flow {
  attachedToPages: string[];
  usageCount: number;
  category: string;
}

// ModuleWithAssets - Module + loaded assets
interface ModuleWithAssets extends ModuleConfig {
  pages: World[];
  flows: Flow[];
  assetCount: number;
  deploymentReady: boolean;
}

๐Ÿ”ง Implementation Details

Step 1: Create UnifiedDataService

File: libs/core-angular/src/lib/services/unified-data.service.ts

Key Features:

Step 2: Update Launch Station

File: libs/launch-station-lib/src/lib/components/launch-dashboard/launch-dashboard.component.ts

Changes:

// BEFORE (broken)
private readonly universeManager = inject(UniverseManagerService);
readonly universes = []; // Empty!

// AFTER (working)
private readonly unifiedData = inject(UnifiedDataService);
readonly universes = this.unifiedData.universesWithContent;
// Now shows: universe.pageCount, universe.moduleCount, etc.

Step 3: Update Modularity

File: libs/modularity-lib/src/lib/components/module-builder/module-builder.component.ts

Changes:

// BEFORE (can't see pages)
// Had to manually track pages

// AFTER (sees everything)
private readonly unifiedData = inject(UnifiedDataService);

// Tab 1: Page Selection
readonly availablePages = this.unifiedData.pages;

// Tab 2: Flow Selection
readonly availableFlows = this.unifiedData.flows;

// Tab 4: Preview
readonly modulePreview = computed(() => {
  return this.unifiedData.modulesWithAssets().find(m => m.id === this.currentModuleId());
});

Step 4: Update Dashboard

File: apps/gograb-space/src/app/g-space-dashboard/g-space-dashboard.component.ts

Changes:

// BEFORE (no stats)
// Only showed universe list

// AFTER (full analytics)
private readonly unifiedData = inject(UnifiedDataService);

readonly totalPages = computed(() => this.unifiedData.pages().length);
readonly totalFlows = computed(() => this.unifiedData.flows().length);
readonly totalModules = computed(() => this.unifiedData.modules().length);
readonly totalWidgets = computed(() => this.unifiedData.widgets().length);

readonly universesWithMostPages = computed(() => {
  return this.unifiedData.universesWithContent()
    .sort((a, b) => b.pageCount - a.pageCount)
    .slice(0, 5);
});

Step 5: Initialize on App Startup

File: apps/gograb-space/src/app/app.component.ts

Changes:

export class AppComponent implements OnInit {
  private readonly unifiedData = inject(UnifiedDataService);
  private readonly auth = inject(AuthService);
  
  async ngOnInit() {
    // Wait for auth
    const user = this.auth.user();
    if (!user) return;
    
    // Load all space data
    await this.unifiedData.loadSpaceData('default-space');
    
    console.log('โœ… UnifiedData loaded:', {
      universes: this.unifiedData.universes().length,
      pages: this.unifiedData.pages().length,
      flows: this.unifiedData.flows().length,
      modules: this.unifiedData.modules().length
    });
  }
}

โœ… Acceptance Criteria

Data Loading

Launch Station

Modularity

Dashboard

Performance


๐Ÿงช Testing Plan

Unit Tests

describe('UnifiedDataService', () => {
  it('should load space data', async () => {
    const service = TestBed.inject(UnifiedDataService);
    await service.loadSpaceData('test-space');
    expect(service.spaces().length).toBeGreaterThan(0);
  });
  
  it('should compute universesWithContent', () => {
    const service = TestBed.inject(UnifiedDataService);
    const universes = service.universesWithContent();
    expect(universes[0].pageCount).toBeDefined();
  });
});

Integration Tests

  1. Load app โ†’ verify data loads
  2. Navigate to Launch Station โ†’ verify universes display
  3. Navigate to Modularity โ†’ verify pages/flows display
  4. Navigate to Dashboard โ†’ verify stats display

Manual Testing

  1. Open app at http://localhost:5200/
  2. Check console for "โœ… UnifiedData loaded"
  3. Navigate to /launch-station
  4. Verify universes are displayed with counts
  5. Navigate to /modularity
  6. Verify Tab 1 shows pages
  7. Verify Tab 2 shows flows

๐Ÿ“Š Progress Tracking

Created Files

Modified Files

Overall Progress: 0%


๐Ÿ› Known Issues & Blockers

None yet - just starting implementation


๐Ÿ“ Notes & Learnings

To be filled as implementation progresses


Last Updated: October 28, 2025 - Starting implementation
Next Checkpoint: UnifiedDataService implementation complete