2026 03 16 Platform Generation V2 Design

Documentation for Plans

Platform Generation v2: Full-Stack, Property-Driven, Vitto-Orchestrated

Session: S226 Author: Claude Code (S226.2) Date: 2026-03-16 Status: Draft


SCOPE NOTE: This spec covers the FULL scope of making platform generation production-ready — not just UI components. It includes backend generation, pipeline hardening, library consolidation, property system integration, page-builder wiring, Vitto orchestration, and the complete dependency chain from entity definition to deployed platform. Future agents/developers: this is the comprehensive reference, but it is NOT the only work remaining. Each phase will spawn its own implementation plans with granular task breakdowns.


Complete System Audit (S226.2 Findings)

Before the design, here's everything we learned about the current state. Future agents should read this section to avoid re-discovering known issues.

Generated Platform Output (spaces/vitto-uni-test-1)

What works well:

What's broken (5 critical compilation errors):

  1. Route lazy-load imports use wrong file paths (../../../modules/structural/... vs actual structure)
  2. Templates call crud.onSort() — composable exports onSortChange()
  3. Templates call crud.toggleSelectAll() — composable exports selectAll()
  4. Club members module imports ClubMembersRoutes — actual export is MODULE_ROUTES
  5. Index barrel files reference ./lib/models — actual path is ./models (no lib/ dir)

What's missing entirely:

Generation Pipeline (backend-vitto-api)

Pipeline is 8 stages (not 6 as sometimes referenced): PRE-FLIGHT → ENHANCE → ANALYZE → DECOMPOSE → PLAN → EXECUTE → INTEGRATE → DEPLOY

Template engine bugs:

ID generation bugs (systemic — Date.now() in loops):

Data layer bugs:

Properties System (MOST MATURE SUBSYSTEM)

Fully production-grade:

Underutilized: No SDK component actually uses UnifiedPropertyDefinition directly. Integration point should be the widget wrapper layer.

UI Component Libraries (5 OVERLAPPING)

Library Components Signal Inputs Avg LOC Strengths Weaknesses
libs/core/ui-framework 44 100% 97 CRUD shells, table, entity-lookup, date picker, file upload, media gallery No property mappers, no a11y
libs-ui-core/ui-core 14 ~80% 296 Breadcrumb (1697 LOC), floating-window (1209 LOC) Most components weaker than ui-framework
libs-ui-core/ui-forms 9 ~80% 364 Color picker (1773 LOC), multi-select (542 LOC) Narrow scope
libs-ui-core/ui-feedback 3 ~80% 727 Toast (1117 LOC), modal (739 LOC) Only 3 components
libs-widgets/widgets 27 57% 428 300+ variant combos, inline editing, canvas-aware Different purpose (studio), not for direct app use

Duplications found: Button (3 implementations), Card (4 implementations), Input (3), Select (3), Checkbox (2), Panel/Modal services (4). No component has production-grade accessibility — zero aria-* attributes, zero role definitions across ALL libraries.

Page Builder

Production-ready infrastructure: Canvas (drag-drop, snap-to-grid, zoom 25-200%), responsive breakpoints (4 tiers with inheritance), properties panel (dynamic per widget type, draft config), serialization (JSON, version-stamped), code generator (Angular + deployment HTML), undo/redo (50 entries), export/import + templates.

Gaps: Widget rendering disconnected from SDK components (ButtonPreviewComponent has 399 LOC of inline CSS separate from GgsButton). Deployment service minimal. Animation system stubbed. Design tokens not integrated with theme system.

Modularity-lib

Working: 6-step wizard with flash persistence, entity definition with field types/validation/relationships, auto-generation of state/CRUD actions/embeddables from entities, EntityExtractionService for NL→entity suggestion.

Broken/Stubbed: Deploy step completely stubbed. AI is pattern matching (12 built-in patterns), not LLM. Flow editor references deprecated singularity-lib. No real code generation — that's handled by backend-vitto-api. No import mechanism (export only). Step validation not enforced (canProceedToNext() always returns true).

Layout Builder

Status: 40% complete, 3 consumers in libs-structural/vibe-universe (shell-generator, platform-blueprint, vibe-universe-generator). NOT orphaned as initially claimed. Has its own zone model disconnected from page-builder. Generates Angular + Flutter code. Uses 12-column CSS Grid with 5 preset templates.

Verdict: Deprecate after migrating the 3 vibe-universe consumers to use page-builder's zone system.

Preview System Constraints (from S225)


Problem Statement

Generated platforms are thin CRUD shells that don't compile and have zero backend intelligence. Specifically:

  1. Frontend doesn't compile — 5 systematic template bugs (wrong method names, broken imports, mismatched exports, broken barrel files, bad pluralization)
  2. Frontend has no real components — generated code references <ggs-entity-lookup>, <ggs-crud-list-shell> etc. from ui-framework, but the generation pipeline doesn't reliably use the correct API surface
  3. Backend is a generic proxy — UniverseDataController accepts any JSON, zero validation, zero business logic, zero relationships, zero per-entity access control
  4. ID collisions — Date.now() used in loops with PARALLEL_MODULE_GEN_BATCH > 1 causes duplicate IDs
  5. Collection naming — universe_universe_ double-prefix risk from inline universe_${universeId}_${collection} concatenation (22+ locations). May manifest at runtime when universeId already includes universe_ prefix. Needs runtime verification.
  6. 3+ duplicate implementations per component type — Button, Card, Input each exist in ui-framework, libs-ui-core, AND libs-widgets/widgets. Violates DRY. Confuses Vitto about which to use.
  7. Page-builder disconnected — renders widgets that don't use the same components as generated platforms, so visual editing and code generation diverge
  8. Vitto can't edit — no property-system integration means Vitto must rewrite code to change a column order or field label

Vision

A generated platform should be a real business application: validated inputs, enforced relationships, typed API endpoints, configurable UI that's editable both visually (page-builder) and conversationally (Vitto). One component per type. One source of truth. Everything composable.


Architecture

Dependency Chain

Platform SDK (libs/core/ui-framework, path: @libs-core/ui-framework)
  │  Clean @Input() APIs. Used directly in templates.
  │  Knows NOTHING about property system.
  │  NOTE: Physical path is libs/core/ui-framework/ (NOT libs/libs-core/)
  │
  ├──→ Generated Platforms (consume SDK components via imports)
  │
  ├──→ Widget Wrappers (libs-widgets/widgets)
  │      Wraps SDK components for page-builder canvas.
  │      Maps property configs → @Input() values.
  │      Adds canvas features (inline edit, resize, selection).
  │      │
  │      └──→ Page Builder (libs-structural/page-builder)
  │             Renders widgets on canvas.
  │             Property panel edits widget configs.
  │             Code generator outputs SDK component usage.
  │             │
  │             └──→ Vitto AI Orchestrator
  │                    Knows component API surface via AI training context.
  │                    Generates by placing widgets OR emitting code.
  │                    Edits by mutating property values (surgical, not rewrite).
  │
  └──→ Backend Generated Code (per-entity schemas, DTOs, services, controllers)
         Entity field definitions map to SDK form control types.
         Same property types drive frontend controls AND backend validators.

Key Principle: Components Are Property-System-Compatible, Not Property-System-Dependent


Phase I: Fix Generation Pipeline

Goal: Generated platforms compile and CRUD works end-to-end with generic backend.

1a. Fix 5 systematic template bugs in backend-vitto-api

Bug Location Fix
Route lazy-load paths wrong template-registry / route generation Emit correct relative import paths matching actual file structure
onSort() → onSortChange() List component template generation Match composable's actual exported method names
toggleSelectAll() → selectAll() List component template generation Match composable's actual exported method names
ClubMembersRoutes → MODULE_ROUTES Module file generation Use consistent export constant name
./lib/models → ./models Barrel file generation Remove phantom lib/ directory segment

Root cause: The LLM hallucinates method names and paths because the prompt context doesn't include the exact composable API surface. Fix by adding the real method signatures to the template prompt context.

File: APIs/backend-vitto-api/src/app/module-generation/templates/template-registry.service.ts

1b. Consolidate collection pluralization

Two different pluralize() implementations already exist in template-generator.service.ts (lines 52-56 and 94-98) with inconsistent behavior. The second one short-circuits on -s endings (e.g., "bus" stays "bus").

Fix: Create ONE shared pluralize() utility in a common utils file. Rules: -ch → -ches, -sh → -shes, -x → -xes, -z → -zes, -s → -ses, consonant+y → -ies, default → -s. Replace both inline implementations and all other call sites.

Why: Currently generates matchs instead of matches.

1c. Fix 3 ID generation bugs

Location Current Fix
structure-builder.service.ts (7 locations) Date.now() randomUUID().substring(0,8)
platform-generation.service.ts (line 90) Date.now()-Math.random() randomUUID()
spaces.service.ts (line 881) Date.now() randomUUID().substring(0,8)

1d. Fix data layer bugs

Bug Location Fix
Delete BSON error data.controller.ts (~line 316) Use string filter { _id: recordId } instead of new ObjectId(recordId)
universe_ prefix duplication risk 22+ inline universe_${universeId}_${collection} concatenations Create buildCollectionName(universeId, collection) utility in data/collection-name.util.ts. Strips existing universe_ prefix before prepending. Verify at runtime whether double-prefix actually occurs.
New universe IDs include prefix space.service.ts (line 232) Generate IDs without universe_ prefix; utility adds it

1e. Consolidation cleanup

Action Target Reason
DELETE contents libs-ui-core/ui-core components: button, card, input, select, checkbox, toggle Weaker duplicates of ui-framework equivalents
ABSORB into ui-framework libs-ui-core/ui-forms: color-picker, multi-select Production-grade, no equivalent in ui-framework
ABSORB into ui-framework libs-ui-core/ui-feedback: toast, modal Production-grade, no equivalent in ui-framework
ABSORB into ui-framework libs-ui-core/ui-core: breadcrumb, floating-window Unique capabilities, no equivalent
DEPRECATE libs-structural/layout-builder Low usage — only libs-structural/vibe-universe imports it (shell-generator, platform-blueprint, vibe-universe-generator). Migrate those 3 consumers to use page-builder's zone system, THEN delete.
CONSOLIDATE 4 panel/modal services → unified UxOverlayService DRY violation

Important: libs-ui-core's sub-libraries (ui-forms, ui-feedback) don't get deleted — their unique components move into ui-framework. Only the duplicate components get removed.

Migration strategy for library consolidation: libs-ui-core has ~45 import occurrences across ~38 files. libs-ui-feedback has ~32 occurrences across ~28 files. Use a 3-step incremental approach:

  1. Add re-export bridges (old path → new path) so existing code doesn't break
  2. Migrate consumers file-by-file using NX tooling
  3. Delete the re-export bridges and empty libraries only after all consumers are migrated

1f. Validation gate

Generate a test platform, verify:


Phase II: Platform SDK (Promote ui-framework)

Goal: One authoritative component library that generated platforms, page-builder, and Vitto all share.

2a. Promote ui-framework

@libs-core/ui-framework becomes the Platform SDK — the single source of truth for UI components.

Existing components (44) — all 100% signal inputs, standalone, OnPush:

Category Components
Form Controls GgsButton, GgsInput, GgsCheckbox, GgsToggle, GgsTextarea, GgsSelect, GgsDropdown, GgsSearchInput, GgsDatePicker, GgsDateRangePicker, GgsFileUpload
Data Display GgsTable, GgsPagination, GgsAvatar, GgsBadge, GgsStatusBadge, GgsTagGroup, GgsSimpleChart, GgsMediaGallery
Layout GgsCard, GgsModal, GgsAlert, GgsEmptyState, GgsInfoBox, GgsFilterPanel, GgsFilterChips
Navigation GgsBreadcrumb (absorbed), GgsSidebar, GgsTabs/TabPanel
CRUD Shells GgsCrudListShell, GgsCrudFormShell, GgsCrudDetailShell
Specialized GgsEntityLookup, GgsConfirmDialog, GgsConfigField, GgsWorkflowHistory
Feedback GgsLoadingSpinner, GgsProgressBar, GgsToast (absorbed), GgsStatItem

2b. Absorb unique components from libs-ui-core

Component Source Destination
Color Picker (1,773 LOC) libs-ui-core/ui-forms ui-framework/components/ggs-color-picker
Multi-Select (542 LOC) libs-ui-core/ui-forms ui-framework/components/ggs-multi-select
Toast System (1,117 LOC) libs-ui-core/ui-feedback ui-framework/components/ggs-toast
Floating Window (1,209 LOC) libs-ui-core/ui-core ui-framework/components/ggs-floating-window
Breadcrumb (1,697 LOC) libs-ui-core/ui-core ui-framework/components/ggs-breadcrumb

After absorption: update all import paths across codebase, then delete the now-empty libs-ui-core sub-packages.

2c. Add missing components for generated platforms

Component Purpose Builds On
GgsEntityForm Dynamic form from field definitions. Renders correct control per field type (text→GgsInput, date→GgsDatePicker, enum→GgsSelect, reference→GgsEntityLookup, file→GgsFileUpload). Validation from field config. Existing form controls
GgsDataTable Enhanced table: column filtering, inline editing, bulk actions, export. Existing GgsTable
GgsEntityDetail Configurable detail view with field sections and display formatting. New component

These components accept configuration objects (column definitions, field definitions) — NOT property system types. They're normal Angular components with rich @Input() APIs.

// Example: GgsEntityForm usage in generated code
<ggs-entity-form
  [fields]="playerFields"
  [entity]="player()"
  [mode]="'edit'"
  (save)="onSave($event)"
  (cancel)="onCancel()">
</ggs-entity-form>

// Where playerFields is:
const playerFields: EntityFieldConfig[] = [
  { key: 'firstName', label: 'First Name', type: 'text', required: true, maxLength: 100 },
  { key: 'jerseyNumber', label: 'Jersey #', type: 'number', min: 1, max: 99 },
  { key: 'status', label: 'Status', type: 'select', options: ['active', 'inactive', 'injured'] },
  { key: 'teamId', label: 'Team', type: 'entity-lookup', entityType: 'teams', displayField: 'name' },
];

2d. Real composable implementations

Replace the stub composables in platform-preview with real ones that work both in preview AND generated platforms:

useCrudList<T>(config) — Returns:

useCrudForm<T>(config) — Returns:

useCrudDetail<T>(config) — Returns:

Method names are EXACTLY what the generation templates use. No more hallucinated names.

2e. Update page-builder to use SDK components

Current state: Page-builder renders widgets via ViewContainerRef using components from UnifiedWidgetRegistry. Widget preview components (in libs-widgets/widgets) have their OWN rendering — they don't use SDK components.

Target state: Widget preview components wrap SDK components:

// Before (ButtonPreviewComponent — 399 LOC of inline rendering)
@Component({
  template: `<button [class]="classes()" (click)="onClick()">{{ displayText() }}</button>`,
  styles: [/* 300 lines of button CSS */]
})

// After (ButtonPreviewComponent — ~80 LOC, wraps GgsButton)
@Component({
  template: `
    <ggs-button
      [variant]="config().variant"
      [size]="config().size"
      [disabled]="config().disabled"
      [loading]="config().loading"
      [icon]="config().icon"
      (click)="onClick()">
      @if (isEditing()) {
        <span contenteditable="true" (blur)="onTextChange($event)">{{ config().text }}</span>
      } @else {
        {{ config().text }}
      }
    </ggs-button>
  `
})

Benefits:

Scope: Refactor the most-used widgets first:

  1. Button, Input, Card, Select, Checkbox, Toggle (form controls)
  2. Table, Badge, Alert, EmptyState (data display)
  3. Container, Section, Grid (layout)

Lower-priority widgets (Divider, Spacer, Icon, Video) can keep self-contained rendering — they're simple enough that wrapping adds no value.

2f. Property templates for SDK components

Register new property templates in core-properties backend for each SDK component that has a widget wrapper:

// Example: GgsDataTable property template
{
  id: 'template_data_table',
  name: 'Data Table',
  category: 'data',
  properties: [
    { id: 'prop_dt_columns', key: 'columns', label: 'Columns', controlType: 'array',
      description: 'Column definitions', aiMetadata: { importance: 'essential' } },
    { id: 'prop_dt_striped', key: 'striped', label: 'Striped Rows', controlType: 'boolean' },
    { id: 'prop_dt_hoverable', key: 'hoverable', label: 'Row Hover', controlType: 'boolean' },
    { id: 'prop_dt_paginated', key: 'paginated', label: 'Pagination', controlType: 'boolean' },
    { id: 'prop_dt_pageSize', key: 'pageSize', label: 'Page Size', controlType: 'number', validation: { min: 5, max: 100 } },
  ],
  widgetTypes: ['ggs-data-table'],
  aiSummary: 'Configurable data table with sorting, pagination, and column management',
}

This means:


Phase III: Backend Generation

Goal: Generate entity-specific backend code — schemas, DTOs, services, controllers — per entity.

3a. New pipeline phase: BACKEND_CODEGEN

The actual pipeline is 8 stages: PRE-FLIGHT → ENHANCE → ANALYZE → DECOMPOSE → PLAN → EXECUTE → INTEGRATE → DEPLOY (confirmed in module-generation.types.ts enum and platform-generation.service.ts).

Insert BACKEND_CODEGEN between EXECUTE and INTEGRATE:

PRE-FLIGHT → ENHANCE → ANALYZE → DECOMPOSE → PLAN → EXECUTE → BACKEND_CODEGEN → INTEGRATE → DEPLOY

Note: The existing fullstack-orchestration.service.ts in backend-vitto-api references full-stack orchestration. This service is the integration point for BACKEND_CODEGEN — extend it, don't create a parallel system.

IMPORTANT — Requires spike before implementation: NestJS DynamicModule loading at runtime is non-trivial. Generated code must be pre-compiled (not eval'd from VFS). Mongoose schema registration requires module init time. Route conflicts between universes need namespace isolation. Run a proof-of-concept with ONE entity type before committing to this approach. If the spike fails, the fallback is to enhance the generic UniverseDataController with per-entity validation middleware (schema-driven validation without generated controllers).

3b. Per-entity generated artifacts

For entity Player in module club-members:

Mongoose Schema (player.schema.ts):

@Schema({ timestamps: true, collection: buildCollectionName(universeId, 'players') })
export class Player {
  @Prop({ required: true, trim: true, maxlength: 100 })
  firstName: string;

  @Prop({ required: true, min: 1, max: 99 })
  jerseyNumber: number;

  @Prop({ required: true, enum: ['active', 'inactive', 'injured'], default: 'active' })
  status: string;

  @Prop({ type: String, ref: 'Team' })
  teamId: string;
}

Create DTO (create-player.dto.ts):

export class CreatePlayerDto {
  @IsString() @IsNotEmpty() @MaxLength(100)
  firstName: string;

  @IsNumber() @Min(1) @Max(99)
  jerseyNumber: number;

  @IsEnum(['active', 'inactive', 'injured'])
  status: string;

  @IsOptional() @IsString()
  teamId?: string;
}

Service (player.service.ts):

@Injectable()
export class PlayerService {
  constructor(@InjectModel(Player.name) private model: Model<Player>) {}

  async create(dto: CreatePlayerDto): Promise<Player> {
    return this.model.create(dto);
  }

  async findAll(pagination: PaginationDto): Promise<PaginatedResult<Player>> {
    // Pagination, sorting, search
  }

  async findOne(id: string): Promise<Player> { ... }
  async update(id: string, dto: UpdatePlayerDto): Promise<Player> { ... }
  async remove(id: string): Promise<void> {
    // Check no active match references before deleting
  }
}

Controller (player.controller.ts):

@Controller('spaces/:spaceId/universes/:universeId/players')
export class PlayerController {
  @Post() create(@Body(ValidationPipe) dto: CreatePlayerDto) { ... }
  @Get() findAll(@Query() pagination: PaginationDto) { ... }
  @Get(':id') findOne(@Param('id') id: string) { ... }
  @Patch(':id') update(@Param('id') id: string, @Body() dto: UpdatePlayerDto) { ... }
  @Delete(':id') remove(@Param('id') id: string) { ... }
}

Seed data (3-5 records with realistic values derived from entity field definitions).

3c. Property type → Backend type mapping

PropertyControlType Mongoose Type class-validator Frontend Control
text String @IsString() GgsInput
number Number @IsNumber() GgsInput[type=number]
boolean Boolean @IsBoolean() GgsToggle
select String (enum) @IsEnum() GgsSelect
date Date @IsDateString() GgsDatePicker
datetime Date @IsDateString() GgsDatePicker
file String (URL) @IsUrl() GgsFileUpload
color String @IsHexColor() GgsColorPicker
array [Schema.Types.Mixed] @IsArray() GgsMultiSelect
object Schema.Types.Mixed @IsObject() JSON editor
richtext String @IsString() Rich text editor
slider Number @IsNumber() @Min() @Max() GgsInput[type=range]
icon String @IsString() Icon picker
code String @IsString() Code editor
json Schema.Types.Mixed @IsObject() JSON editor

Same entity field definition drives frontend controls, backend schemas, AND backend validation. One definition, three outputs.

EntityFieldConfig vs PropertyDefinition: These are intentionally separate types serving different consumers. EntityFieldConfig is a simple, flat config for generated platform code ({ key, label, type, required, min, max }). PropertyDefinition is the full property system type with AI metadata, templates, mappers, categories. The widget wrapper layer maps between them: EntityFieldConfig.type ↔ PropertyDefinition.controlType. They share the same type enum values but not the same structure, avoiding coupling SDK components to the property system.

3d. Deployment: Option A (dynamic modules on shared backend-api)

Generated backend code is stored in the universe's VFS and registered as a NestJS dynamic module on backend-api:

// Dynamic module loader
@Module({})
export class UniverseModuleLoader {
  static forUniverse(universeId: string, entities: EntityDefinition[]): DynamicModule {
    const schemas = entities.map(e => generateSchema(e));
    const services = entities.map(e => generateService(e));
    const controllers = entities.map(e => generateController(e));

    return {
      module: UniverseModuleLoader,
      imports: [MongooseModule.forFeature(schemas)],
      providers: services,
      controllers: controllers,
    };
  }
}

Generic UniverseDataController remains as fallback for entities that haven't been fully generated yet.

Future: Option B (standalone microservice per universe) for production. Option C (Cloudflare Workers) for edge deployment. Not in scope for v2.


Phase IV: Visual + Vibe Editing Integration

Goal: Close the loop — Vitto can generate platforms AND users can edit them visually AND conversationally.

4a. Vitto generation uses SDK + page-builder

Two generation modes, both using the same SDK components:

Code-first (current, enhanced):

Visual-first (new, uses page-builder):

Both modes produce identical visual output because they use the same underlying GgsDataTable, GgsEntityForm, etc.

4b. Modularity-lib → Vitto orchestration (ASPIRATIONAL — requires dedicated spec)

This section describes the target vision, not a concrete implementation plan. The specifics (API contracts, sequence diagrams, error handling, Vitto tool definitions) will be designed in a dedicated spec after Phases I-III are validated.

Vision: Replace the pattern-matching AIModuleGeneratorService with real Vitto orchestration:

Prerequisite: Phases I-III must be complete. The SDK components, composables, and backend generation must work before Vitto can orchestrate them.

4c. Vitto learns from usage

Each generation cycle feeds back into the intelligence system:

Over time, Vitto's property template suggestions improve because it knows:


Library Consolidation Summary

Final State

Library Status Role
@libs-core/ui-framework PROMOTED — Platform SDK Single source of truth for UI components. ~49 components after absorption.
@libs-widgets/widget-system KEEP AS-IS Widget runtime engine (BaseWidgetComponent, registry, state machine)
@libs-widgets/widgets REFACTORED Widget wrappers consume SDK components. Thin adapter layer.
@libs-widgets/widget-builder KEEP AS-IS Widget authoring tool (Vitto capability)
@libs-widgets/widget-showcase KEEP, DEPRIORITIZE Demo/gallery (update to show SDK components)
@libs-widgets/widgets-manager KEEP AS-IS Widget lifecycle management
@libs-ui-core/ui-core DEPRECATED Unique components absorbed into SDK, duplicates deleted
@libs-ui-core/ui-forms DEPRECATED Color picker + multi-select absorbed into SDK
@libs-ui-core/ui-feedback DEPRECATED Toast + modal absorbed into SDK
@libs-ui-core/ui-structure KEEP (dashboard) Specialized, no overlap
@libs-ux/ux-utils-lib KEEP, consolidate overlays Panel/modal services → UxOverlayService
@libs-structural/page-builder ENHANCED Uses SDK components via widget wrappers
@libs-structural/modularity-lib ENHANCED (Phase IV) Connected to Vitto orchestration
@libs-structural/platform-preview ENHANCED Real composables from SDK, not stubs
@libs-structural/layout-builder DELETED Orphaned, zero imports
@libs-foundational/core-properties ENHANCED New property templates for SDK components

What Gets Deleted

Target Reason Replacement
libs-structural/layout-builder Only 3 consumers in vibe-universe (migrate first) page-builder handles layouts
libs-ui-core/ui-core: UiButtonComponent Weaker than GgsButton (fewer variants, less modern) GgsButton in SDK
libs-ui-core/ui-core: UxCardComponent Weaker than GgsCard (no image, no actions) GgsCard in SDK
libs-ui-core/ui-core: UiInputComponent Weaker than GgsInput (no validation display) GgsInput in SDK
libs-ui-core/ui-core: SelectComponent Weaker than GgsSelect (less keyboard nav) GgsSelect in SDK
libs-ui-core/ui-core: ux-checkbox Uses old @Input decorators (not signal inputs) GgsCheckbox in SDK
libs-ui-core/ui-core: toggle Equivalent to GgsToggle GgsToggle in SDK

What This Design Does NOT Include


Risks and Mitigations

Risk Impact Mitigation
Absorbing components breaks imports across codebase HIGH — build breaks Automated search-and-replace of import paths. NX has tooling for library moves.
Widget wrappers add latency to page-builder rendering MEDIUM Wrapper is thin (50-80 LOC). Profile before optimizing.
Generated backend modules conflict with shared backend-api HIGH — runtime crashes Namespace all generated routes under /universes/:id/. Use NestJS module isolation.
LLM still hallucinates method names despite prompt context MEDIUM Add exact TypeScript signatures to template context. Add post-generation lint step.
Phase III complexity — NestJS dynamic module loading untested at scale HIGH Start with 1 entity type. Validate hot-reload. Fallback to generic controller.
Property templates proliferate unmanageably LOW Category-based organization. Usage tracking identifies dead templates.
JIT compatibility in preview HIGH SDK components use signal input() syntax which may not work in Angular JIT (blob iframe preview). Mitigation: Preview stubs in sdk-preview-stubs.ts already handle this — they use @Input() decorators. Generated platforms compile with AOT. Only the live preview iframe uses JIT. Ensure SDK components used in preview context are shimmed via the existing stub system, not loaded directly.
ESM SDK interaction MEDIUM Composables that use inject() or Angular DI will break in blob iframe context (S225 finding). Mitigation: Preview composables use native fetch() and window.location.hash — no Angular DI. Real composables in generated platforms use inject() normally.
FontAwesome dependency LOW GgsButton imports FaIconComponent. Preview iframe may not have it. Mitigation: Widget wrappers already replace FA icons with span placeholders in preview mode.
Library consolidation breaks builds HIGH ~75+ files import from libs being deprecated. Mitigation: 3-step migration (re-export bridge → migrate consumers → delete). Never hard-delete before all consumers are migrated.

Rollback Strategy


Success Criteria

Phase I

Phase II

Phase III

Phase IV


Implementation Order

Phase I-A  ──→  Phase I-B  ──→  Phase II  ──→  Phase III  ──→  Phase IV
(1-2 sessions)  (2-3 sessions)   (3-4 sessions)  (2-3 sessions)   (3+ sessions)
  │                │                │                │                │
  │                │                │                │                └─ Vitto orchestration
  │                │                │                └─ Backend generation (spike first)
  │                │                └─ SDK components + page-builder + composables
  │                └─ Library consolidation (incremental migration)
  └─ Bug fixes (template, IDs, data layer)

Phase I is split into I-A (bug fixes, independently deployable) and I-B (library consolidation, incremental migration with re-export bridges). Each phase has its own validation gate. Do not start Phase N+1 until Phase N's success criteria are met.