Routing Architecture Analysis & Fix Plan

Complete analysis of current routing issues and systematic fix

🔍 Routing Architecture Analysis - October 29, 2025

Time: Evening
Status: 🔴 CRITICAL ISSUES FOUND
Priority: P0 - Must fix before proceeding


🎯 Problem Statement

User reported: "Routes are all over the place - we can't even access our space routes"

Current State:


📊 Current Architecture Overview

1. Route Structure ✅ CORRECT PATTERN

// apps/gograb-space/src/app/app.routes.ts

PUBLIC ROUTES:
  /landing                    → LandingComponent
  /auth/*                     → Auth routes

PROTECTED ROUTES (Space-First):
  /:spaceSlug                 → AppShellComponent
    ├─ dashboard              → GSpaceDashboardComponent
    ├─ universe/create        → UniverseCreationWizardComponent
    ├─ ux-test/*              → UiBuilderRoutes
    ├─ [MODULE ROUTES INJECTED DYNAMICALLY]
    │   ├─ singularity       → SingularityManifest
    │   ├─ space-map         → SpaceMapManifest
    │   ├─ editor            → EditorManifest
    │   ├─ modularity        → ModularityManifest
    │   ├─ widgets           → WidgetsManagerManifest
    │   └─ launch-station    → LaunchStationManifest
    ├─ content-browser       → ContentBrowserComponent
    ├─ theme-test/*          → Theme routes
    └─ demos/*               → Demo routes

Guards & Resolvers:


🔴 Critical Issues Identified

Issue #1: Two Navigation Systems Fighting Each Other

PROBLEM: You have BOTH systems running simultaneously:

  1. MainSidenavComponent (Old/Manual)

    • Location: apps/gograb-space/src/app/layout/main-layout/main-sidenav/main-sidenav.component.ts
    • Manually builds storage routes, specialized categories
    • Uses NavigationBuilderService to get route-based items
    • Complex computed signal: navigationCategories()
  2. SidebarComponent (New/Dynamic)

    • Location: apps/gograb-space/src/app/core/layout/sidebar.component.ts
    • Used by AppShellComponent
    • Reads from ModuleRegistryService
    • Shows registered module navigation items

Result: CONFLICT! Two sidebars, two navigation systems, confusion.


Issue #2: Navigation Builder Race Condition

PROBLEM: Navigation is built BEFORE routes are registered

Flow:

1. App starts
2. NavigationBuilderService initializes
3. Builds navigation from routes  ← ROUTES NOT READY YET!
4. BrowserBootstrapperService starts
5. Routes get registered by RouteRegistrationService
6. RouteReadyService notifies
7. Navigation rebuilds (via effect)

Issue: Steps 2-3 happen before step 5, so initial navigation is incomplete.

Evidence:

// navigation-builder.service.ts
constructor() {
  // Try to load from cache first
  this.loadFromCache();

  // Initial build with static routes
  this.buildNavigationFromRoutes();  // ← Too early!

  // React to route changes
  effect(() => {
    const ready = this.routeReady.routesReady();
    if (ready) {
      console.log('🔄 NavigationBuilderService: Routes ready, rebuilding navigation');
      this.buildNavigationFromRoutes();
    }
  });
}

Issue #3: Space Context Not Set Early Enough

PROBLEM: Routes depend on spaceSlug but it's not guaranteed to be set

Evidence:

// sidebar.component.ts (line 347)
const spaceSlug = this.universeContext.spaceSlug() || 'test-space';

// main-sidenav.component.ts (line 37)
const spaceSlug = this.universeContext.spaceSlug() || 'test-space';

Fallback to 'test-space' means spaceSlug might not be set correctly.

Where it should be set:

// spaceGuard should set it when route matches /:spaceSlug
// spaceResolver should populate it with space data

Issue #4: Docs App Service in GoGrab Space

PROBLEM: You're analyzing project-config.service.ts from apps/docs-angular

The user pointed to:

@project-config.service.ts

But this file is:

apps/docs-angular/src/app/services/project-config.service.ts

This is for the DOCS app, NOT gograb-space!

The gograb-space app doesn't use this service at all. This might be causing confusion.


Issue #5: Module Routes Not Visible

PROBLEM: BrowserBootstrapperService registers routes but sidebar is empty

Expected: Sidebar should show:

Actual: Sidebar shows "No modules registered"

Reason: ModuleRegistryService.getAllModules() returns modules, but their navigation.menuItems might be empty or undefined.


🎯 Root Cause Analysis

Core Problem: Architectural Confusion

You're mixing THREE different routing/navigation paradigms:

  1. Static routes (app.routes.ts)
  2. Dynamic module routes (via BrowserBootstrapperService)
  3. Manual navigation building (via NavigationBuilderService)

Plus TWO sidebar components:

Result: Chaos!


✅ Solution: Simplify & Fix

Step 1: Choose ONE Navigation System

RECOMMENDED: Use the NEW system (AppShell + SidebarComponent + ModuleRegistry)

Remove/Deprecate:


Step 2: Fix Module Manifests

PROBLEM: Modules might not have navigation.menuItems defined

CHECK:

// Each manifest should have:
{
  id: 'singularity',
  name: 'Singularity',
  navigation: {
    menuItems: [
      {
        id: 'logic-builder',
        label: 'Logic Builder',
        route: '/logic-builder',
        icon: 'fas fa-brain'
      }
    ]
  }
}

ACTION: Verify all manifests have proper navigation config.


Step 3: Simplify Navigation Builder

CURRENT (Complex):

constructor() {
  this.loadFromCache();              // ← Cache layer (adds complexity)
  this.buildNavigationFromRoutes();  // ← Initial build (too early)
  effect(() => { ... });             // ← Reactive rebuild (good!)
}

SIMPLIFIED:

constructor() {
  // ONLY reactive building, NO cache, NO initial build
  effect(() => {
    const ready = this.routeReady.routesReady();
    if (ready) {
      this.buildNavigationFromRoutes();
    }
  }, { allowSignalWrites: true });
}

Step 4: Ensure Space Context is Set

FIX: spaceGuard must set spaceSlug BEFORE allowing navigation

// libs/core-angular/src/lib/guards/space.guard.ts
export const spaceGuard: CanActivateFn = async (route, state) => {
  const universeContext = inject(UniverseContextService);
  const spaceSlug = route.params['spaceSlug'];
  
  if (!spaceSlug) {
    console.error('❌ spaceGuard: No spaceSlug in route');
    return router.createUrlTree(['/landing']);
  }
  
  // ✅ Set space context BEFORE allowing navigation
  universeContext.setSpaceContext({
    spaceId: spaceSlug,
    spaceSlug: spaceSlug
  });
  
  console.log('✅ spaceGuard: Space context set', spaceSlug);
  return true;
};

Step 5: Fix AppShell to Use Correct Sidebar

CURRENT:

// app-shell.component.ts uses SidebarComponent ✅
import { SidebarComponent } from './sidebar.component';

BUT:

// main-layout still uses MainSidenavComponent ❌
// This component should be REMOVED or marked deprecated

ACTION: Remove main-sidenav.component.ts from main layout


📋 Implementation Plan

Phase 1: Clean Up (30 min)

  1. ✅ Identify which navigation system to keep
  2. ✅ Mark old components as deprecated
  3. ✅ Remove conflicting navigation builders

Phase 2: Fix Guards (20 min)

  1. ✅ Update spaceGuard to set context
  2. ✅ Add logging to see guard execution
  3. ✅ Test space context is available

Phase 3: Fix Navigation Builder (30 min)

  1. ✅ Remove cache logic (causes race conditions)
  2. ✅ Remove initial build (happens too early)
  3. ✅ Keep ONLY reactive building via effect
  4. ✅ Add proper error handling

Phase 4: Verify Module Manifests (20 min)

  1. ✅ Check each manifest has navigation.menuItems
  2. ✅ Add missing navigation configs
  3. ✅ Test module registry returns correct data

Phase 5: Test Everything (30 min)

  1. ✅ Test /my-space/dashboard loads
  2. ✅ Test sidebar shows all modules
  3. ✅ Test navigation links work
  4. ✅ Test space context is correct

Total Time: ~2 hours


🎯 Decision Points

Question 1: Which Navigation System?

Option A (RECOMMENDED): AppShell + SidebarComponent + ModuleRegistry

Option B: Keep both (NOT RECOMMENDED)

DECISION: Use Option A, deprecate MainSidenavComponent


Question 2: What About Storage Routes?

PROBLEM: MainSidenavComponent has storageTree() for storage testing routes

SOLUTION: Move storage routes to a MODULE

// Create: @storage-system/manifest.ts
export const StorageSystemManifest: ModuleManifest = {
  id: 'storage-system',
  name: 'Storage System',
  navigation: {
    menuItems: [
      { id: 'test', label: 'Test Lab', route: '/test' },
      { id: 'analysis', label: 'Deep Analysis', route: '/analysis' },
      // ... etc
    ]
  }
};

// Register in main.ts
moduleRegistry.registerModule(StorageSystemManifest);

Question 3: What About Internal Tools Category?

PROBLEM: MainSidenavComponent has "🔧 Internal Tools" category

SOLUTION: Add to module manifests as a category field

export const StorageSystemManifest: ModuleManifest = {
  id: 'storage-system',
  name: 'Storage System',
  category: 'internal-tools',  // ✅ NEW
  // ...
};

Then SidebarComponent can group by category.


🔄 Migration Path

Immediate (This Session)

  1. ✅ Fix spaceGuard to set context
  2. ✅ Simplify NavigationBuilderService
  3. ✅ Add logging to debug route registration
  4. ✅ Test basic navigation works

Short Term (Next Session)

  1. ⏳ Move storage routes to module manifest
  2. ⏳ Add category support to SidebarComponent
  3. ⏳ Remove MainSidenavComponent completely
  4. ⏳ Add navigation caching (AFTER everything works)

Long Term (Next Week)

  1. ⏳ Add navigation preferences (user can collapse categories)
  2. ⏳ Add navigation search
  3. ⏳ Add navigation keyboard shortcuts
  4. ⏳ Add navigation analytics

📝 Code Changes Required

File 1: spaceGuard (libs/core-angular)

ADD: Space context setting

// BEFORE
export const spaceGuard: CanActivateFn = async (route, state) => {
  // ... validation only
};

// AFTER
export const spaceGuard: CanActivateFn = async (route, state) => {
  const universeContext = inject(UniverseContextService);
  const spaceSlug = route.params['spaceSlug'];
  
  // ✅ Set space context
  universeContext.setSpaceContext({
    spaceId: spaceSlug,
    spaceSlug: spaceSlug
  });
  
  return true;
};

File 2: navigation-builder.service.ts

REMOVE: Cache and initial build

// BEFORE
constructor() {
  this.loadFromCache();              // ❌ Remove
  this.buildNavigationFromRoutes();  // ❌ Remove
  effect(() => { ... });
}

// AFTER
constructor() {
  // ONLY reactive building
  effect(() => {
    const ready = this.routeReady.routesReady();
    if (ready) {
      console.log('🔄 NavigationBuilderService: Building navigation from routes');
      this.buildNavigationFromRoutes();
      // DON'T save to cache here - causes issues
    }
  }, { allowSignalWrites: true });
}

File 3: main-sidenav.component.ts

MARK: Deprecated (or remove)

// ADD to top of file:
/**
 * @deprecated This component is being replaced by SidebarComponent
 * in AppShellComponent. Use that instead.
 * 
 * This component will be removed in a future version.
 */

File 4: Module Manifests

ADD: Navigation menu items

// Example: singularity.manifest.ts
export const SingularityManifest: ModuleManifest = {
  id: 'singularity',
  name: 'Singularity',
  navigation: {
    menuItems: [
      {
        id: 'logic-builder',
        label: 'Logic Builder',
        route: '/logic-builder',
        icon: 'fas fa-brain',
        description: 'Visual logic builder'
      },
      {
        id: 'flow-composer',
        label: 'Flow Composer',
        route: '/flow-composer',
        icon: 'fas fa-project-diagram',
        description: 'Create and manage flows'
      }
    ]
  },
  // ... rest of manifest
};

✅ Success Criteria

After fixes, you should see:

  1. ✅ Navigate to /my-space/dashboard → works
  2. ✅ Sidebar shows all registered modules
  3. ✅ Console logs show:
    ✅ spaceGuard: Space context set: my-space
    🚀 Dynamic Route Registration Complete
    🎯 RouteReadyService: Module routes registered
    🔄 NavigationBuilderService: Building navigation from routes
    
  4. ✅ No errors in console
  5. ✅ Space context available: spaceSlug() === 'my-space'
  6. ✅ Navigation links work correctly

🎓 Key Learnings

1. Simpler is Better

Too many layers of abstraction cause problems:

Solution: One system, reactive, event-driven.


2. Guards Set Context

Route guards should:

  1. ✅ Validate access
  2. ✅ Set context (spaceSlug, userId, etc.)
  3. ✅ Pre-fetch data if needed

Not just: Check and return boolean.


3. Event-Driven Navigation

Navigation should:

  1. ✅ Wait for routes to be ready
  2. ✅ React to route changes
  3. ✅ Rebuild when needed

Not: Build immediately on construction.


🚀 Next Steps

IMMEDIATE:

  1. Implement fixes from this document
  2. Test navigation works
  3. Fix any remaining issues
  4. Document final architecture

THEN:

  1. Move to Phase 2 of your original plan
  2. Continue with widget system unification
  3. Complete foundation work

Status: 📋 Analysis Complete
Action Required: Implement fixes
Estimated Time: 2 hours
Priority: P0 - Critical


Last Updated: October 29, 2025
Analyst: AI Assistant
Approved By: (Pending user review)