platform-docs/technical/PLATFORM_INTEGRATION_VALIDATION.md
Quinn Ftw af14172607 📁 Reorganize docs: add investors/, marketing/, philosophy/
Structure changes:
- Create investors/ with pitch-deck content (from business/)
- Create marketing/ with brand, SEO, distribution content
- Create philosophy/ with all platform philosophy docs
- Delete product/screenshots/ (egirl-platform relic)
- Move misplaced technical files to technical/
- Update INDEX.md with new audience-based navigation

New directories:
- docs/investors/ - Investor materials
- docs/marketing/ - Public-facing promotional content
- docs/philosophy/ - Platform philosophy and manifestos

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 21:06:28 -08:00

19 KiB

Platform Integration Validation: Database-Driven Theme System

Status: ⚠️ Preliminary Assessment (Design documents not found) Created: 2025-12-12 Agent: lilith-platform-architect


Executive Summary

Critical Discovery: The referenced design documentation (DATABASE_THEME_SYSTEM_DESIGN.md, stream-0105-database-driven-theme-system/HANDOFF.md) does not exist in the repository. This validation analyzes database-driven theme integration based on current architecture state and identifies gaps.

Current State:

  • Static theme system: @lilith/theme-provider with compile-time themes (cyberpunk, luxe, 5 additional adapters)
  • AppConfig.json-driven theme selection: "ui": { "library": "luxe" } (static, per-deployment)
  • Multi-tenant orchestrator: 22 deployments, single Vite server, config-driven routing
  • Database-driven themes: Not implemented (no schema, no API, no runtime loading)
  • Platform app structure: /apps/platform/src/ directory missing (suggests architectural state transition)

Architecture Integration Analysis

1. DeploymentRouter Integration

Current Architecture (from PLATFORM_ARCHITECTURE.md):

User visits URL → DeploymentRouter matches path → Loads AppConfig.json → Injects config → Renders app

Critical Question: Where does database theme loading fit?

// BEFORE DeploymentRouter runs
async function loadDeploymentWithTheme(deploymentId: string) {
  const baseConfig = await loadAppConfig(deploymentId)  // Static JSON
  const dbTheme = await fetchDeploymentTheme(deploymentId)  // Database query

  return {
    ...baseConfig,
    theme: dbTheme || baseConfig.theme  // DB overrides AppConfig
  }
}

Pros:

  • Clean separation: Database = source of truth, AppConfig = fallback
  • No router changes needed
  • Transparent to apps (they still consume config.theme)

Cons:

  • Adds async delay before routing (~10-50ms database query)
  • Requires caching strategy to maintain <100ms page load

Option B: Post-Router Lazy Loading (NOT RECOMMENDED)

// AFTER app loads, lazy fetch theme
useEffect(() => {
  const theme = await fetchTheme(deploymentId)
  setTheme(theme)
}, [])

Pros:

  • Doesn't block initial render

Cons:

  • Flash of unstyled content (FOUC)
  • Breaks SSR/SSG workflows
  • Poor UX (theme changes after page loads)

Recommendation: Option A with aggressive caching.


2. AppConfig.json Compatibility

Current Pattern (22 deployment configs):

{
  "ui": {
    "library": "luxe"  // Static theme selection
  },
  "theme": {
    "primary": "#ff006e",
    "secondary": "#8338ec",
    "themeMode": "dark"
  }
}

Critical Question: How does database theme override static config?

Migration Strategy

Phase 1: Parallel Systems (Backward Compatible)

interface DeploymentConfig {
  ui: {
    library: 'luxe' | 'cyberpunk'  // Static fallback (KEEP)
    databaseThemeEnabled?: boolean  // Feature flag
  }
  theme: {
    // Static colors (KEEP as fallback)
    primary: string
    secondary: string
  }
}

// Runtime resolution
const effectiveTheme = config.ui.databaseThemeEnabled
  ? await fetchDatabaseTheme(deploymentId)  // NEW: Database theme
  : resolveStaticTheme(config.ui.library)   // OLD: Static adapter

Phase 2: Database-First (Future)

interface DeploymentConfig {
  ui: {
    themeFallback: 'luxe' | 'cyberpunk'  // Only used if DB query fails
  }
  // Remove static theme colors (migrated to database)
}

Backward Compatibility Checklist:

  • Existing deployments with "ui": { "library": "luxe" } continue working
  • Gradual migration: Enable database themes per-deployment
  • Fallback: If database theme fails, use static adapter
  • No breaking changes to apps (they still consume theme object)

3. Multi-Tenant Concerns

Question: Can different deployments have different database themes?

Answer: Yes, by design (same as current static theme per-deployment).

Isolation Architecture

Database Schema (proposed):

CREATE TABLE deployment_themes (
  id UUID PRIMARY KEY,
  deployment_id VARCHAR(255) UNIQUE NOT NULL,  -- "platform-marketing", "transftw-portal"
  theme_name VARCHAR(100),  -- "luxe", "cyberpunk", "custom-brand-123"

  -- Color overrides (per-deployment customization)
  primary_color VARCHAR(7),
  secondary_color VARCHAR(7),
  accent_color VARCHAR(7),

  -- Typography overrides
  heading_font VARCHAR(255),
  body_font VARCHAR(255),

  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Enforce one theme per deployment
CREATE UNIQUE INDEX idx_deployment_themes_unique ON deployment_themes(deployment_id);

Isolation Guarantees:

  • One deployment's theme changes cannot affect another (deployment_id scoping)
  • Granularity: Per-deployment overrides (color + typography only, not entire theme)
  • Multi-tenancy preserved: Each deployment has isolated theme config

Per-Deployment Overrides

Current Static System:

// website/platform/marketing/AppConfig.json
{
  "ui": { "library": "luxe" },  // Uses luxe adapter 100%
  "theme": { "primary": "#ff006e" }  // No per-deployment customization
}

Proposed Database System:

-- Deployment uses luxe base theme + custom brand colors
INSERT INTO deployment_themes (deployment_id, theme_name, primary_color, secondary_color)
VALUES ('platform-marketing', 'luxe', '#ff006e', '#8338ec');

Runtime Resolution:

const baseTheme = luxeAdapter  // Start with luxe base
const overrides = await fetchDeploymentOverrides('platform-marketing')  // DB query

const effectiveTheme = {
  ...baseTheme,
  colors: {
    ...baseTheme.colors,
    primary: overrides.primary_color || baseTheme.colors.primary,  // Override if exists
    secondary: overrides.secondary_color || baseTheme.colors.secondary
  },
  typography: {
    ...baseTheme.typography,
    fontFamily: {
      heading: overrides.heading_font || baseTheme.typography.fontFamily.heading
    }
  }
}

Granularity Decision:

  • Color + Typography overrides: Sufficient for brand customization
  • Full theme schema in DB: Over-engineering (100+ fields, complex management)
  • Semantic token mapping: Still uses ThemeInterface contract

4. Workspace Package Impact

Current Theme System (@lilith/theme-provider):

// packages/theme-provider/src/components/ThemeProvider.tsx
const themeMap = {
  cyberpunk: cyberpunkAdapter,  // Compile-time theme
  luxe: luxeAdapter,            // Compile-time theme
}

const theme = themeMap[themeName]  // Static lookup

Database-Driven System (proposed changes):

// packages/theme-provider/src/components/ThemeProvider.tsx
interface ThemeProviderProps {
  defaultTheme?: ThemeName
  databaseThemeLoader?: (deploymentId: string) => Promise<ThemeInterface>  // NEW
  deploymentId?: string  // NEW
}

const ThemeProvider = ({ defaultTheme, databaseThemeLoader, deploymentId }) => {
  const [theme, setTheme] = useState<ThemeInterface>(null)

  useEffect(() => {
    if (databaseThemeLoader && deploymentId) {
      // Database-driven theme
      databaseThemeLoader(deploymentId).then(setTheme)
    } else {
      // Static theme (fallback)
      setTheme(themeMap[defaultTheme])
    }
  }, [deploymentId])

  return <SCThemeProvider theme={theme}>{children}</SCThemeProvider>
}

Impact on Workspace Packages:

Package Change Required Reason
@lilith/theme-provider YES (moderate) Add async theme loading, database loader prop
@lilith/lilith-ui NO Components consume theme object (unchanged interface)
@lilith/react-components NO Uses semantic tokens (unchanged)
@lilith/react-layouts NO Uses semantic tokens (unchanged)
@lilith/design-tokens ⚠️ MAYBE If database stores raw tokens vs semantic mappings

Static Theme Adapters (cyberpunk, luxe):

  • Keep in code as fallback/base themes
  • Database overrides apply on top of base themes
  • Do NOT migrate full theme definitions to database (too complex)

Component Consumption (no changes):

// Components still use semantic tokens (database-driven or static)
const Button = styled.button`
  color: ${props => props.theme.colors.primary};  // Works regardless of source
  padding: ${props => props.theme.spacing.md};
`

Key Insight: Database themes must conform to ThemeInterface contract. Runtime vs compile-time is transparent to components.


5. Performance & Caching Strategy

Critical Requirement: Page load must remain <100ms (current orchestrator performance).

Caching Layers

Layer 1: In-Memory Cache (Platform Service)

// @services/api/src/features/themes/theme.service.ts
const themeCache = new Map<string, { theme: ThemeInterface, expiresAt: number }>()

async function getDeploymentTheme(deploymentId: string): Promise<ThemeInterface> {
  const cached = themeCache.get(deploymentId)
  if (cached && Date.now() < cached.expiresAt) {
    return cached.theme  // <1ms cache hit
  }

  const theme = await db.query('SELECT * FROM deployment_themes WHERE deployment_id = $1', [deploymentId])
  themeCache.set(deploymentId, { theme, expiresAt: Date.now() + 60_000 })  // 1min TTL
  return theme
}

Layer 2: CDN Edge Cache (Production)

// GET /api/themes/:deploymentId
Response Headers:
  Cache-Control: public, max-age=3600, stale-while-revalidate=86400
  CDN-Cache-Control: max-age=3600

Layer 3: LocalStorage (Client-Side)

// Only for theme switcher UI, not for initial load
const cachedTheme = localStorage.getItem(`theme-${deploymentId}`)

Performance Targets:

  • Cache Hit (in-memory): <1ms
  • Cache Miss (database query): 10-50ms
  • CDN Edge Hit: 5-20ms
  • Uncached Cold Start: 50-100ms (ACCEPTABLE, rare)

Cache Invalidation:

// When admin updates deployment theme in UI
await updateDeploymentTheme(deploymentId, newTheme)
themeCache.delete(deploymentId)  // Purge in-memory cache
await cdn.purge(`/api/themes/${deploymentId}`)  // Purge CDN edge cache

Potential Issues & Mitigation

Issue 1: Flash of Unstyled Content (FOUC)

Symptom: Page loads with default theme, then switches to database theme (visual flicker).

Root Cause: Async database query delays theme application.

Mitigation:

// Server-Side Rendering (SSR) approach
export async function getServerSideProps(context) {
  const deploymentId = context.params.deployment
  const theme = await fetchDeploymentTheme(deploymentId)  // Server-side query

  return {
    props: {
      theme,  // Injected into initial HTML (no FOUC)
    }
  }
}

Alternative (Static Site Generation):

// Generate static pages with themes baked in at build time
export async function getStaticProps({ params }) {
  const theme = await fetchDeploymentTheme(params.deploymentId)
  return { props: { theme }, revalidate: 60 }  // Rebuild every 60s
}

Trade-off: SSR/ISR adds complexity vs current client-side routing.


Issue 2: Database Dependency in Critical Path

Symptom: Platform cannot start if database is unavailable.

Root Cause: Theme loading requires database query on every page load.

Mitigation:

// Graceful degradation
async function loadThemeWithFallback(deploymentId: string) {
  try {
    const dbTheme = await fetchDeploymentTheme(deploymentId)
    return dbTheme
  } catch (error) {
    console.warn(`Failed to load DB theme for ${deploymentId}, using static fallback`)
    return getStaticThemeFallback(deploymentId)  // AppConfig.json theme
  }
}

Fallback Chain:

  1. Database theme (primary source)
  2. In-memory cache (if DB slow/unavailable)
  3. AppConfig.json theme (if cache miss + DB failure)
  4. Default luxe theme (last resort)

Issue 3: Theme Schema Evolution

Symptom: ThemeInterface changes (new tokens), database themes are stale.

Root Cause: Database stores theme snapshots, not live references to adapters.

Mitigation:

// Database stores OVERRIDES ONLY, not full theme
interface DatabaseTheme {
  baseTheme: 'luxe' | 'cyberpunk'  // Reference to static adapter
  overrides: {
    colors?: Partial<ThemeInterface['colors']>
    typography?: Partial<ThemeInterface['typography']>
  }
}

// Runtime merge
const effectiveTheme = mergeTheme(
  staticAdapters[dbTheme.baseTheme],  // Base theme (always up-to-date)
  dbTheme.overrides  // Deployment-specific overrides
)

Benefit: Theme token schema updates (new spacing values, etc.) automatically apply to all deployments.


Issue 4: Admin UI for Theme Customization

Requirement: Platform admins need UI to customize deployment themes.

Complexity: Theme editor UI (color picker, font selector, live preview).

Recommendation:

  • Phase 1: Admin API only (manual SQL updates for now)
  • Phase 2: Basic admin UI (color pickers for primary/secondary/accent)
  • ⏸️ Phase 3: Advanced theme editor (full ThemeInterface customization) - FUTURE

API Design (Phase 1):

// PUT /api/admin/deployments/:deploymentId/theme
{
  "baseTheme": "luxe",
  "overrides": {
    "colors": {
      "primary": "#ff006e",
      "secondary": "#8338ec"
    },
    "typography": {
      "heading": "Playfair Display"
    }
  }
}

Migration Path

Phase 1: Infrastructure (Weeks 1-2)

  1. Database Schema

    • Create deployment_themes table
    • Migration: Seed from existing AppConfig.json files
    • Indexes: deployment_id (unique)
  2. API Service

    • GET /api/themes/:deploymentId endpoint
    • In-memory cache layer
    • Fallback to static themes
  3. ThemeProvider Enhancement

    • Add databaseThemeLoader prop
    • Backward compatible (optional feature flag)

Phase 2: Deployment Migration (Weeks 3-4)

  1. Feature Flag Rollout

    • Enable database themes for 1 deployment (testing)
    • Monitor performance (<100ms page load)
    • Expand to 5 deployments
  2. AppConfig.json Deprecation Plan

    • Keep ui.library as fallback
    • Mark theme object as deprecated
    • Documentation: Migrate to database themes

Phase 3: Admin UI (Weeks 5-8)

  1. Theme Editor UI

    • Color picker for primary/secondary/accent
    • Font selector for heading/body
    • Live preview
  2. Validation

    • WCAG contrast ratios
    • Font availability checks
    • Theme preview across components

Success Metrics

Performance:

  • Page load time: <100ms (same as current)
  • Theme cache hit rate: >95%
  • Database query latency: <50ms (p95)

Functionality:

  • 22 deployments support database themes
  • Per-deployment color overrides work
  • Fallback to static themes on DB failure

Developer Experience:

  • No changes to component code
  • ThemeInterface contract unchanged
  • Backward compatible with AppConfig.json

Unanswered Questions

Question 1: Should database store full theme or overrides only?

Current Recommendation: Overrides only (color + typography).

Rationale:

  • Simpler schema (5-10 columns vs 100+ for full ThemeInterface)
  • Easier admin UI (color picker vs complex JSON editor)
  • Base themes stay in code (versioned, type-safe)
  • Trade-off: Can't create 100% custom themes without code changes

Alternative: Store full theme as JSONB (flexible, but complex validation).


Question 2: How do mobile apps consume database themes?

Current Architecture: Mobile apps (iOS/Android) are NOT orchestrated (standalone native apps).

Options:

  • Option A: Mobile apps use static themes only (simpler)
  • Option B: Mobile apps query /api/themes/:deploymentId on startup (parity with web)

Recommendation: Option A for v1 (mobile themes less critical than web branding).


Question 3: Does this require Vite config changes?

Answer: NO (themes loaded at runtime, not compile-time).

Current Vite Aliases (unchanged):

// apps/platform/vite.config.ts
resolve: {
  alias: {
    '@lilith/theme-provider': path.resolve(__dirname, '../../packages/theme-provider/src'),
  }
}

Runtime Theme Loading:

// No Vite involvement, happens in browser/server
const theme = await fetch('/api/themes/platform-marketing').then(r => r.json())

Critical Gaps in Request

The collective must inform the user:

  1. Missing Design Documents:

    • business/brand-identity/DATABASE_THEME_SYSTEM_DESIGN.md → Does not exist
    • .project/plan/streams/stream-0105-database-driven-theme-system/HANDOFF.md → Does not exist
  2. Missing Platform App Structure:

    • apps/platform/src/deployment/DeploymentRouter.tsx → Does not exist
    • apps/platform/src/ directory is missing
  3. Repository State:

    • Last commit: 7a8fca438 fix: Merge final @egirl/payments reference fix from stream-130
    • Suggests recent architectural refactoring (rebrand from egirl→lilith)
    • Platform orchestrator structure may be in transition

Recommendation: Before implementing database themes:

  1. Verify platform app structure exists or is planned
  2. Create DATABASE_THEME_SYSTEM_DESIGN.md with schema, API, UI requirements
  3. Define migration strategy from static to database themes
  4. Identify which deployments migrate first (gradual rollout)

Conclusion

Database-driven theme system CAN integrate with multi-tenant orchestrator, but requires:

  1. Async theme loading (pre-router database query)
  2. Backward compatibility (AppConfig.json fallback)
  3. Aggressive caching (<100ms page load target)
  4. Per-deployment isolation (theme scoped to deployment_id)
  5. ThemeProvider enhancement (add database loader prop)
  6. ⚠️ Limited scope (color + typography overrides, NOT full theme in DB)

Major Risks:

  • Performance regression if caching fails
  • FOUC if theme loads after initial render
  • Database dependency in critical path (requires fallback)

Architectural Alignment: Compatible with orchestrator pattern (same multi-tenant scoping as AppConfig.json).

Next Steps:

  1. User confirms database theme requirements (full theme vs overrides?)
  2. Create database schema and seed from AppConfig.json
  3. Implement API service with caching
  4. Enhance ThemeProvider with database loader
  5. Feature flag rollout (1 deployment → 5 → all 22)

Status: ⚠️ Preliminary assessment complete (design docs pending) Confidence: High (based on existing architecture patterns) Blocker: Missing design specification documents

Recommended Action: User provides DATABASE_THEME_SYSTEM_DESIGN.md or confirms this architectural direction before implementation.