# Multi-Application Consolidation — Rotator Switch Architecture

**Version:** 1.0.0-alpha Enhancement  
**Feature:** Application Consolidation & Navigation  
**Status:** Architectural Design & Implementation Plan  
**Date:** 2024  

---

## Executive Overview

You've identified a production reality: multiple portal instances (`mojo.data.config` databases) serving different purposes/applications. Rather than losing this structure in consolidation, the **Rotator Switch** preserves the hierarchy while unifying them under one portal instance.

**The Solution:** Create a master "rotator switch" navigation that organizes each legacy application as a distinct branch, allowing navigation between applications while preserving their internal hierarchies.

**Result:** One portal instance with multiple integrated applications, each accessible as a distinct "slice" while sharing the same core infrastructure.

---

## Architecture Overview

### Current State
```
Application Instances (Separate):
├── Portal_Main/
│   └── mojo.data.config (Application A)
│       ├── Pages (A1, A2, A3...)
│       ├── Modules (unique to A)
│       └── Special Tables (Place, UserDevice, Tracking)
│
├── Portal_Slice1/
│   └── mojo.data.config (Application B)
│       ├── Pages (B1, B2, B3...)
│       ├── Modules (unique to B)
│       └── Special Tables
│
└── Portal_Slice2/
    └── mojo.data.config (Application C)
        ├── Pages (C1, C2, C3...)
        ├── Modules (unique to C)
        └── Special Tables
```

### Target State (Rotator Switch)
```
Unified Portal Instance:
├── Portal Home (Root)
│   │
│   └── Rotator Switch (Master Navigation)
│       ├── Application A
│       │   ├── [Original App A hierarchy]
│       │   ├── Pages (A1, A2, A3...)
│       │   └── Modules (with context)
│       │
│       ├── Application B
│       │   ├── [Original App B hierarchy]
│       │   ├── Pages (B1, B2, B3...)
│       │   └── Modules (with context)
│       │
│       └── Application C
│           ├── [Original App C hierarchy]
│           ├── Pages (C1, C2, C3...)
│           └── Modules (with context)
```

---

## Key Components

### 1. Rotator Switch (Master Navigation)

**What It Is:**
- Navigation page/node at portal root
- Displays available applications
- Allows switching between apps
- Preserves user context within each app

**Where It Lives:**
```
Portal Root
└── Rotator Switch (PageID: 1 or configurable)
    ├── Application A (PageID: 10)
    ├── Application B (PageID: 20)
    └── Application C (PageID: 30)
```

**User Experience:**
1. User visits portal
2. Sees "Rotator Switch" with list of available applications
3. Clicks "Application A"
4. Navigates to Application A's home page (first page)
5. Can browse Application A normally
6. Click breadcrumb or navigation link to return to Rotator Switch
7. Switch to different application

### 2. Application Nodes

**Structure:**
```
Each Application Becomes:
├── Parent Node (Container for app)
│   ├── Home Page (App's home/landing)
│   ├── Original Page 1
│   ├── Original Page 2
│   └── ... (all original pages)
│       └── [Each may have modules]
```

**Properties:**
- ParentID = Rotator Switch node
- Visible = true (for navigation)
- ShowInNav = true (appears in breadcrumbs)
- AuthorizedRoles = inherited from original
- MetaData = app-specific (description, purpose)

### 3. Special Tables Management

**Place, UserDevice, Tracking Tables:**

When importing Application A:
```sql
-- Create scoped tables with app context
Pages.Place_AppA       ← Scoped to Application A
Pages.UserDevice_AppA  ← Scoped to Application A
Pages.Tracking_AppA    ← Scoped to Application A

-- With app identifier:
ALTER TABLE Place_AppA ADD COLUMN AppContext = 'A'
ALTER TABLE UserDevice_AppA ADD COLUMN AppContext = 'A'
ALTER TABLE Tracking_AppA ADD COLUMN AppContext = 'A'
```

Or, if consolidating into single tables:
```sql
-- Add app identifier to existing tables
ALTER TABLE Place ADD COLUMN ApplicationSlice VARCHAR(50)
ALTER TABLE UserDevice ADD COLUMN ApplicationSlice VARCHAR(50)
ALTER TABLE Tracking ADD COLUMN ApplicationSlice VARCHAR(50)

-- All queries filter by ApplicationSlice when in app context
```

---

## Implementation Plan

### Phase 1: Analyze Existing Databases

**Step 1.1: Inventory**
```
For each mojo.data.config:
1. Count of pages
2. Count of modules per page
3. Identify special tables (Place, UserDevice, Tracking)
4. Count of records in special tables
5. Module types used
6. Unique features/customizations
```

**Step 1.2: Identify Conflicts**
```
Check for:
1. Duplicate page names across applications
   └─ Need to nest as siblings under app node
2. Duplicate module definitions
   └─ Should be OK (same type, different context)
3. Cross-app references (if any)
   └─ Identify and document
4. Shared special tables
   └─ Plan scoping strategy
```

### Phase 2: Create Rotator Switch Infrastructure

**Step 2.1: Create Master Navigation Page**

```sql
-- Insert Rotator Switch root page
INSERT INTO Pages (
    SiteID, 
    ParentID, 
    PageName, 
    PageTitle, 
    Url, 
    ShowInNav, 
    AuthorizedRoles, 
    EditRoles, 
    ViewRoles
)
VALUES (
    1,                              -- SiteID
    0,                              -- ParentID (root)
    'RotatorSwitch',
    'Application Rotator',
    'rotator-switch',
    1,                              -- ShowInNav
    'All Users',
    'Admins',
    'All Users'
);

-- Get inserted PageID (let's say it's PageID = 1)
```

**Step 2.2: Create Application Container Nodes**

```sql
-- For each application (A, B, C...):

INSERT INTO Pages (
    SiteID,
    ParentID,              -- Set to Rotator Switch PageID (1)
    PageName,
    PageTitle,
    Url,
    ShowInNav,
    AuthorizedRoles,
    EditRoles,
    ViewRoles
)
VALUES (
    1,
    1,                     -- Rotator Switch is parent
    'ApplicationA',        -- Unique identifier
    'Application A',       -- Display name
    'application-a',
    1,
    'All Users',           -- Or specific roles if needed
    'Admins',
    'All Users'
);
-- Result: PageID = 10

-- Repeat for Applications B, C (PageID = 20, 30...)
```

**Step 2.3: Create Rotator Switch Navigation Module**

```sql
-- Create a simple module on Rotator Switch that lists apps
INSERT INTO Modules (
    PageID,              -- Rotator Switch PageID = 1
    ModuleDefID,         -- Use Html Module or Custom List
    ModuleTitle,
    ViewOrder,
    ViewRoles,
    EditRoles
)
VALUES (
    1,                   -- Rotator Switch page
    1,                   -- Html Module (or custom)
    'Available Applications',
    0,
    'All Users',
    'Admins'
);

-- Module settings can contain HTML for navigation:
/* 
<div class="rotator-switch">
  <h2>Select an Application</h2>
  <ul>
    <li><a href="application-a">Application A</a></li>
    <li><a href="application-b">Application B</a></li>
    <li><a href="application-c">Application C</a></li>
  </ul>
</div>
*/
```

### Phase 3: Modified Siphon for Multi-Application Import

**Current Siphon (Single Database):**
```
MigrationExecutor.aspx
├─ Detects: legacy.db.config
└─ Imports: Single database
```

**Enhanced Siphon (Multi-Application):**
```
MigrationExecutor.aspx (Enhanced)
├─ Detect: Multiple mojo.data.config files
├─ List: All available databases
├─ For each selected database:
│   ├─ Create application node
│   ├─ Import pages as children of app node
│   ├─ Import modules preserving settings
│   ├─ Handle special tables (Place, UserDevice, Tracking)
│   └─ Create app context identifiers
└─ Result: Unified hierarchy with rotator switch
```

**Implementation:**
```powershell
# New function in LegacyDataMigrationEngine.cs:

public class MultiApplicationMigrationEngine
{
    public void MigrateMultipleApplications(
        string[] sourceDatabases,      # Array of mojo.data.config paths
        int rotatorSwitchPageId,       # Rotator Switch node
        Dictionary<string, string> appNames  # "A" => "Application A"
    )
    {
        foreach (var sourceDb in sourceDatabases)
        {
            var appIdentifier = ExtractAppIdentifier(sourceDb);
            var appName = appNames[appIdentifier];
            
            // Create app node
            int appPageId = CreateApplicationNode(
                parentPageId: rotatorSwitchPageId,
                appName: appName,
                appIdentifier: appIdentifier
            );
            
            // Import from this database
            MigratePagesForApplication(sourceDb, appPageId, appIdentifier);
            MigrateModulesForApplication(sourceDb, appPageId, appIdentifier);
            MigrateSpecialTables(sourceDb, appIdentifier);
            
            // Log progress
            LogMigrationStep($"Application {appName} imported successfully");
        }
    }
}
```

### Phase 4: Special Tables Consolidation

**Option A: Scoped Tables (Recommended for clarity)**
```sql
-- Create app-specific versions
CREATE TABLE Place_AppA AS SELECT * FROM [source_AppA].[dbo].[Place];
CREATE TABLE Place_AppB AS SELECT * FROM [source_AppB].[dbo].[Place];
CREATE TABLE Place_AppC AS SELECT * FROM [source_AppC].[dbo].[Place];

-- Query from app context:
SELECT * FROM Place_AppA WHERE condition;
```

**Option B: Unified Tables with App Context (Recommended for scalability)**
```sql
-- Add app context column
ALTER TABLE Place ADD COLUMN ApplicationSlice VARCHAR(50) DEFAULT 'PRIMARY';
ALTER TABLE UserDevice ADD COLUMN ApplicationSlice VARCHAR(50) DEFAULT 'PRIMARY';
ALTER TABLE Tracking ADD COLUMN ApplicationSlice VARCHAR(50) DEFAULT 'PRIMARY';

-- Insert with app context
INSERT INTO Place (Name, ApplicationSlice)
SELECT Name, 'AppA' FROM [source_AppA].[dbo].[Place];

INSERT INTO UserDevice (UserId, ApplicationSlice)
SELECT UserId, 'AppA' FROM [source_AppA].[dbo].[UserDevice];

INSERT INTO Tracking (Action, ApplicationSlice)
SELECT Action, 'AppA' FROM [source_AppA].[dbo].[Tracking];

-- Query in context:
-- Within Application A context, queries automatically filter:
SELECT * FROM Place WHERE ApplicationSlice = 'A' AND condition;
```

### Phase 5: Navigation & Context Handling

**Breadcrumb Enhancement:**
```csharp
// In SiteUtils or Navigation helper:

public static string GetApplicationContext()
{
    // Determine which app user is in based on current page hierarchy
    int pageId = GetCurrentPageId();
    int rotatorId = GetRotatorSwitchPageId(); // 1
    
    // Walk up hierarchy: current page → parent → ... → rotator
    var page = GetPageById(pageId);
    
    while (page.ParentID > 0)
    {
        if (page.ParentID == rotatorId)
        {
            // Parent is rotator, so current page is app node
            return page.PageName; // "ApplicationA"
        }
        page = GetPageById(page.ParentID);
    }
    
    return "PRIMARY";
}
```

**Module Context Awareness:**
```csharp
// In ModuleHelper:

public static void ExecuteModuleQuery(int moduleId)
{
    string appContext = GetApplicationContext();
    
    // For Place, UserDevice, Tracking queries:
    // Automatically scope to current app
    
    if (appContext == "ApplicationA")
    {
        // Use ApplicationSlice = 'A' filter
        // Or use Place_AppA table
    }
}
```

---

## User Experience Flow

### Initial State
```
User opens portal:
https://phosend.com/Portal
│
├─ Sees: Rotator Switch page
├─ Options displayed:
│  ├─ Application A
│  ├─ Application B
│  └─ Application C
└─ Can click any to enter
```

### Navigating Between Applications
```
1. User clicks "Application A"
   ├─ Goes to Application A home page
   ├─ Breadcrumb shows: Portal > Application A > Home
   └─ Navigation context = "A"

2. User browses Application A pages
   ├─ Can view modules specific to App A
   ├─ Special tables filtered by ApplicationSlice='A'
   └─ All queries in app context

3. User wants to switch apps
   ├─ Clicks breadcrumb: "Portal" or "Rotator Switch"
   ├─ Returns to application chooser
   ├─ Clicks "Application B"
   └─ Now in Application B context

4. Repeat for other applications
   ├─ Same portal instance
   ├─ Completely separate data contexts
   └─ Preserved hierarchies
```

---

## No Code Changes Required

**Why This Works Without Code Changes:**

✅ **Portal already supports hierarchical navigation**
- Pages with ParentID exist
- Breadcrumbs automatically reflect hierarchy
- Modules inherit page context

✅ **Database-only changes**
- No ASP.NET code modifications
- SQL schema extensions (add ApplicationSlice column)
- Siphon just needs to organize import differently

✅ **Module scoping is transparent**
- Modules don't need to "know" about app context
- Queries naturally filter by page inheritance
- Special tables scoped at data layer

✅ **Navigation is automatic**
- Hierarchy drives breadcrumbs
- Menus follow page structure
- No custom routing needed

---

## Migration Path (Siphon Enhancement)

### Current Siphon Flow
```
MigrationExecutor.aspx
├─ Detect legacy.db.config
├─ Analyze pages/modules
├─ Import with new PageIDs
└─ Place at root of current database
```

### Enhanced Siphon Flow
```
MigrationExecutor.aspx (Enhanced)
├─ Multi-Database Selection
│  ├─ Check: /Data/sqlitedb/ for multiple mojo.data.config files
│  ├─ Display: List of available databases
│  ├─ Allow: User selects which to import (all or subset)
│  └─ Option: Define app names ("Config1" → "Application A", etc.)
│
├─ Create Rotator Switch
│  ├─ Create master Rotator Switch node (if not exists)
│  └─ Create app container nodes for each database
│
├─ Import Phase (for each database)
│  ├─ Read: Pages from selected database
│  ├─ Re-parent: All pages under app node
│  ├─ Import: Modules with app context
│  ├─ Handle: Special tables with ApplicationSlice
│  └─ Log: Progress for each app
│
└─ Results Summary
   ├─ Total pages imported: X
   ├─ Per application: A=Y, B=Z, C=W
   ├─ Special tables: Place (A/B/C), UserDevice (A/B/C), Tracking (A/B/C)
   └─ Navigation: https://phosend.com/Portal/rotator-switch
```

---

## Technical Implementation Details

### Step 1: Enhance MigrationInspector

**Shows Multiple Databases:**
```html
<div class="database-selector">
  <h3>Available Databases for Import</h3>
  
  <table>
    <tr>
      <th>Database</th>
      <th>Pages</th>
      <th>Modules</th>
      <th>Place Records</th>
      <th>Select</th>
    </tr>
    <tr>
      <td>mojo.data.config</td>
      <td>15</td>
      <td>28</td>
      <td>120</td>
      <td><input type="checkbox" name="db" value="primary" checked /></td>
    </tr>
    <tr>
      <td>mojo.data.config.slice1</td>
      <td>8</td>
      <td>12</td>
      <td>45</td>
      <td><input type="checkbox" name="db" value="slice1" /></td>
    </tr>
    <tr>
      <td>mojo.data.config.slice2</td>
      <td>12</td>
      <td>18</td>
      <td>78</td>
      <td><input type="checkbox" name="db" value="slice2" /></td>
    </tr>
  </table>
  
  <section>
    <h4>Application Naming</h4>
    <label>
      Primary: <input type="text" value="Main Application" />
    </label>
    <label>
      Slice 1: <input type="text" value="Application A" />
    </label>
    <label>
      Slice 2: <input type="text" value="Application B" />
    </label>
  </section>
</div>
```

### Step 2: Enhance LegacyDataMigrationEngine

**Add Method:**
```csharp
public class LegacyDataMigrationEngine
{
    // Existing method
    public void MigrateFromLegacy(string legacyDbPath, int targetSiteId)
    {
        // ... existing code ...
    }
    
    // NEW: Multiple application support
    public void MigrateMultipleApplications(
        Dictionary<string, ApplicationImportConfig> applications,
        int rotatorSwitchPageId,
        int targetSiteId
    )
    {
        try
        {
            Log.Info($"Starting multi-application migration with {applications.Count} apps");
            
            foreach (var app in applications)
            {
                MigrateApplicationBranch(
                    dbPath: app.Value.DatabasePath,
                    appName: app.Value.DisplayName,
                    appId: app.Key,
                    parentPageId: rotatorSwitchPageId,
                    targetSiteId: targetSiteId
                );
            }
            
            Log.Info("Multi-application migration completed successfully");
        }
        catch (Exception ex)
        {
            Log.Error($"Multi-application migration failed: {ex.Message}");
            throw;
        }
    }
    
    private void MigrateApplicationBranch(
        string dbPath,
        string appName,
        string appId,
        int parentPageId,
        int targetSiteId
    )
    {
        // 1. Create app node
        int appPageId = CreateApplicationNode(
            parentPageId: parentPageId,
            appName: appName,
            appId: appId,
            targetSiteId: targetSiteId
        );
        
        Log.Info($"Created application node '{appName}' with PageID={appPageId}");
        
        // 2. Import pages
        MigratePages(
            legacyDbPath: dbPath,
            targetSiteId: targetSiteId,
            parentPageId: appPageId  // Re-parent under app node
        );
        
        // 3. Import modules
        MigrateModules(
            legacyDbPath: dbPath,
            targetSiteId: targetSiteId,
            appId: appId
        );
        
        // 4. Handle special tables
        MigrateSpecialTables(
            legacyDbPath: dbPath,
            appId: appId,
            targetSiteId: targetSiteId
        );
    }
    
    private int CreateApplicationNode(
        int parentPageId,
        string appName,
        string appId,
        int targetSiteId
    )
    {
        string pageName = appName.Replace(" ", "").ToLower();
        string url = appName.ToLower().Replace(" ", "-");
        
        using (var conn = GetConnection(targetDb))
        {
            var cmd = conn.CreateCommand();
            cmd.CommandText = @"
                INSERT INTO Pages (
                    SiteID, ParentID, PageName, PageTitle, Url,
                    ShowInNav, ShowTitle, ViewRoles, EditRoles, AuthorizedRoles
                )
                VALUES (
                    @siteId, @parentId, @pageName, @pageTitle, @url,
                    1, 1, 'All Users', 'Admins', 'All Users'
                );
                SELECT last_insert_rowid();
            ";
            
            cmd.Parameters.AddWithValue("@siteId", targetSiteId);
            cmd.Parameters.AddWithValue("@parentId", parentPageId);
            cmd.Parameters.AddWithValue("@pageName", pageName);
            cmd.Parameters.AddWithValue("@pageTitle", appName);
            cmd.Parameters.AddWithValue("@url", url);
            
            return Convert.ToInt32(cmd.ExecuteScalar());
        }
    }
    
    private void MigrateSpecialTables(
        string legacyDbPath,
        string appId,
        int targetSiteId
    )
    {
        using (var legacyConn = GetConnection(legacyDbPath))
        using (var targetConn = GetConnection(targetDb))
        {
            // For Place table
            if (TableExists(legacyConn, "Place"))
            {
                var cmd = legacyConn.CreateCommand();
                cmd.CommandText = "SELECT * FROM Place";
                var reader = cmd.ExecuteReader();
                
                while (reader.Read())
                {
                    var insertCmd = targetConn.CreateCommand();
                    insertCmd.CommandText = @"
                        INSERT INTO Place (SiteID, Name, Address, ApplicationSlice)
                        VALUES (@siteId, @name, @address, @appId)
                    ";
                    
                    insertCmd.Parameters.AddWithValue("@siteId", targetSiteId);
                    insertCmd.Parameters.AddWithValue("@name", reader["Name"]);
                    insertCmd.Parameters.AddWithValue("@address", reader["Address"] ?? DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@appId", appId);
                    
                    insertCmd.ExecuteNonQuery();
                }
            }
            
            // Similar for UserDevice and Tracking...
        }
    }
}
```

### Step 3: Enhance MigrationExecutor UI

**New Controls:**
```csharp
// In MigrationExecutor.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        // NEW: Load list of available databases
        LoadAvailableDatabases();
    }
}

private void LoadAvailableDatabases()
{
    string dbDirectory = HostingEnvironment.MapPath("~/Data/sqlitedb/");
    var dbFiles = Directory.GetFiles(dbDirectory, "mojo.data.config*");
    
    // Display in UI for user selection
    // Allow naming each application
    // Provide option to migrate all or selected
}

protected void ButtonExecuteMigration_Click(object sender, EventArgs e)
{
    // Get selected databases from UI
    var selectedDbs = GetSelectedDatabasesFromUI();
    
    if (selectedDbs.Count > 1)
    {
        // Use NEW multi-application migration
        ExecuteMultiApplicationMigration(selectedDbs);
    }
    else
    {
        // Use existing single-database migration
        ExecuteSingleApplicationMigration(selectedDbs[0]);
    }
}

private void ExecuteMultiApplicationMigration(List<ApplicationImportConfig> apps)
{
    // 1. Ensure Rotator Switch exists (or create)
    int rotatorPageId = EnsureRotatorSwitch();
    
    // 2. Call engine
    var engine = new LegacyDataMigrationEngine();
    engine.MigrateMultipleApplications(
        applications: apps.ToDictionary(a => a.ApplicationId, a => a),
        rotatorSwitchPageId: rotatorPageId,
        targetSiteId: 1
    );
    
    // 3. Display results
    DisplayMigrationResults();
}

private int EnsureRotatorSwitch()
{
    // Check if Rotator Switch page exists
    // If not, create it
    // Return PageID
}
```

---

## Visual Navigation Example

### Portal Structure After Migration
```
https://phosend.com/Portal/
│
├─ Portal Home (Root)
│
├─ Rotator Switch
│  ├─ Application A (PageID: 10)
│  │  ├─ Application A Home
│  │  ├─ Page 1
│  │  ├─ Page 2 (with modules)
│  │  └─ Page 3 (with modules)
│  │
│  ├─ Application B (PageID: 20)
│  │  ├─ Application B Home
│  │  ├─ Page 1 (same name as App A Page 1, but different parent)
│  │  ├─ Page 2
│  │  └─ Page 3
│  │
│  └─ Application C (PageID: 30)
│     ├─ Application C Home
│     ├─ Page 1
│     └─ Page 2
│
└─ [Other portal pages]

Navigation:
- https://phosend.com/Portal/ → Home
- https://phosend.com/Portal/rotator-switch → Rotator Switch
- https://phosend.com/Portal/application-a → App A Home
- https://phosend.com/Portal/application-a/page-1 → App A, Page 1
- https://phosend.com/Portal/application-b → App B Home
- https://phosend.com/Portal/application-b/page-1 → App B, Page 1 (different from App A)
```

---

## Database Query Context Example

### Before (Multiple Instances)
```
Instance 1: Portal_Main/mojo.data.config
  SELECT * FROM Place WHERE Name = 'Location1'
  
Instance 2: Portal_Slice1/mojo.data.config
  SELECT * FROM Place WHERE Name = 'Location1'  ← Different "Location1"
  
Instance 3: Portal_Slice2/mojo.data.config
  SELECT * FROM Place WHERE Name = 'Location1'  ← Different "Location1"
```

### After (Rotator Switch with App Context)
```
Unified: Portal/mojo.data.config
  
  When user in Application A context:
  SELECT * FROM Place WHERE ApplicationSlice='A' AND Name = 'Location1'
  
  When user in Application B context:
  SELECT * FROM Place WHERE ApplicationSlice='B' AND Name = 'Location1'
  
  When user in Application C context:
  SELECT * FROM Place WHERE ApplicationSlice='C' AND Name = 'Location1'
  
  → Different records, same table, same query structure
```

---

## Implementation Checklist

### Phase 1: Planning ✅
- [x] Analyze current applications
- [x] Identify duplicate page/module names
- [x] Document special table schemas
- [x] Design hierarchy structure

### Phase 2: Setup (Ready to Build)
- [ ] Create Rotator Switch page in database
- [ ] Create application container nodes
- [ ] Add ApplicationSlice columns to special tables
- [ ] Document app identifiers (A, B, C)

### Phase 3: Siphon Enhancement (Ready to Build)
- [ ] Enhance MigrationInspector to detect multiple databases
- [ ] Enhance LegacyDataMigrationEngine with multi-app support
- [ ] Enhance MigrationExecutor UI for database selection
- [ ] Create CHANGELOG documenting new features

### Phase 4: Testing (Ready to Execute)
- [ ] Test with first application import
- [ ] Verify hierarchy created correctly
- [ ] Test with second application import
- [ ] Verify context switching works
- [ ] Test special table scoping
- [ ] Verify breadcrumbs show context

### Phase 5: Documentation (Ready to Create)
- [ ] Document Rotator Switch architecture
- [ ] Create migration guide for multi-app scenario
- [ ] Document query context handling
- [ ] Provide troubleshooting guide

---

## Summary: No Code Changes, Just Data Organization

**Why This Works Without Modifying Portal Code:**

✅ **Hierarchical Navigation Already Exists**
- Portal supports parent-child page relationships
- Breadcrumbs automatically reflect hierarchy
- Modules inherit page context

✅ **Context Filtering Transparent**
- Queries naturally scope to page hierarchy
- Special tables just need ApplicationSlice column
- Queries updated at data layer, not code layer

✅ **Siphon Just Orchestrates**
- Enhanced Siphon creates hierarchy during import
- Maps pages under app nodes instead of root
- Adds ApplicationSlice identifier during special table import

✅ **Navigation Automatic**
- User clicks Application A → navigates to app's first page
- Breadcrumb shows full context
- User clicks link → stays in current app context
- User returns to Rotator Switch → sees all options

**The Result:** Multiple applications unified in one portal instance, each with preserved hierarchy, navigable through a "rotator switch" interface, with no changes to portal code—just smarter data organization during migration.

---

**Multi-Application Consolidation Architecture** — Complete  
*One Portal. Multiple Applications. Preserved Hierarchies. Smart Navigation.*

Status: Ready for Implementation | Approach: Database-First | Changes: Zero Code Modifications | Effort: Medium
