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:
- Complete Angular 20 scaffold: 38 NX libs, 2 feature modules, lazy-loaded routes
- Composable CRUD pattern:
useCrudList<T>,useCrudForm<T>,useCrudDetail<T>— well-designed API surface - Signals for state (no NGXS), OnPush everywhere, standalone components, event bus
- Entity interfaces extend
BaseEntitywith proper typing BaseCrudApiService<T>abstracts data layer throughUniverseDataService- Reactive forms with
Validatorsper field - Scoped SCSS per component, dark theme by default
What's broken (5 critical compilation errors):
- Route lazy-load imports use wrong file paths (
../../../modules/structural/...vs actual structure) - Templates call
crud.onSort()— composable exportsonSortChange() - Templates call
crud.toggleSelectAll()— composable exportsselectAll() - Club members module imports
ClubMembersRoutes— actual export isMODULE_ROUTES - Index barrel files reference
./lib/models— actual path is./models(nolib/dir)
What's missing entirely:
- Zero backend code (no schemas, DTOs, services, controllers)
- No seed data
- No unit tests or E2E tests
- No deployment config (Docker, env files)
<ggs-entity-lookup>referenced in templates but implementation unclear- No error handling middleware
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:
- LLM hallucinates method names because prompt context lacks exact composable API signatures
- Two inconsistent
pluralize()implementations intemplate-generator.service.ts(lines 52-56 and 94-98) fullstack-orchestration.service.tsexists and references full-stack orchestration but is underutilized
ID generation bugs (systemic — Date.now() in loops):
structure-builder.service.ts: 7 locations (lines 37, 42, 47, 61, 75, 89, 148)platform-generation.service.ts: line 90spaces.service.ts: line 881
Data layer bugs:
- Delete BSON version error from ObjectId construction mismatch in
data.controller.ts - 22+ inline
universe_${universeId}_${collection}concatenations risk double-prefix when universeId already containsuniverse_ spaceId/universeIdoptional in generation DTO — should be required
Properties System (MOST MATURE SUBSYSTEM)
Fully production-grade:
- 15 control types: text, number, boolean, color, select, multiselect, slider, textarea, richtext, code, json, date, datetime, array, object
- 7 categories: content, appearance, layout, sizing, spacing, typography, behavior (plus border, effects, data, accessibility, animation, advanced in unified types)
- 7 built-in template categories: content, sizing, colors, typography, spacing, layout, behavior
- AI metadata per property: importance, useCases, recommendedWidgets, sampleValues
- Property mappers:
mapButtonVariant(),mapSize(),mapButtonShape(),mapButtonEffect() - Two storage tiers:
gograb-core.core_property_templates(global) +space_{spaceId}.shared_properties(per-space) - AI training context endpoint:
getAITrainingContext(spaceId)— includes widget usage patterns, layout mappings, theme integration - Frontend:
UnifiedPropertyServicewith signal-based state, flash persistence, stale-while-revalidate caching - Property references: Components can reference properties by ID with overrides (order, group, required, hidden, readonly, transform)
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)
- Blob iframe uses Angular JIT →
@Input()/@Output()decorators required, NOT signalinput()/output() inject()and Angular DI fail in blob iframe → preview stubs use nativefetch()andwindow.location.hash- FontAwesome not available in preview → widget wrappers replace FA icons with span placeholders
autoFlashStatehas unresolvable race conditions → MongoDB is source of truth viaChatBackendSyncService- rspack worker has its OWN config (not from rspack-angular.config.ts) → changes must be duplicated
Problem Statement
Generated platforms are thin CRUD shells that don't compile and have zero backend intelligence. Specifically:
- Frontend doesn't compile — 5 systematic template bugs (wrong method names, broken imports, mismatched exports, broken barrel files, bad pluralization)
- 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 - Backend is a generic proxy —
UniverseDataControlleraccepts any JSON, zero validation, zero business logic, zero relationships, zero per-entity access control - ID collisions —
Date.now()used in loops withPARALLEL_MODULE_GEN_BATCH > 1causes duplicate IDs - Collection naming —
universe_universe_double-prefix risk from inlineuniverse_${universeId}_${collection}concatenation (22+ locations). May manifest at runtime when universeId already includesuniverse_prefix. Needs runtime verification. - 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.
- Page-builder disconnected — renders widgets that don't use the same components as generated platforms, so visual editing and code generation diverge
- 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
GgsButtonhas@Input() variant,@Input() size,@Input() disabled— normal Angular inputsButtonWidgetwrapsGgsButton, reads property configs, passes values as inputs- Property system lives in the widget layer. SDK components are standalone and testable without it.
- A developer can use
<ggs-button variant="primary">without knowing the property system exists
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:
- Add re-export bridges (old path → new path) so existing code doesn't break
- Migrate consumers file-by-file using NX tooling
- Delete the re-export bridges and empty libraries only after all consumers are migrated
1f. Validation gate
Generate a test platform, verify:
- ✅
ng buildsucceeds with zero errors - ✅ List view renders with data from MongoDB
- ✅ Create form submits and record appears in list
- ✅ Edit loads entity, saves changes
- ✅ Delete removes entity with confirmation
- ✅ Console: no NG0303 errors, no silent fetch failures
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:
items,loading,error,totalItems(signals)searchTerm,currentPage,pageSize,sortBy,sortOrder(writable signals)onSortChange(column),onPaginationChange(page),onSearchChange(term)selectAll(),clearSelection(),toggleSelection(id),selectedIdsdeleteItem(id),bulkDelete(ids)navigateToCreate(),navigateToDetail(id),navigateToEdit(id)refresh()
useCrudForm<T>(config) — Returns:
form(FormGroup),entity(signal),mode(signal: 'create' | 'edit')isFormValid,saving,errorsave(),cancel()isFieldInvalid(field),getFieldError(field)
useCrudDetail<T>(config) — Returns:
entity(signal),loading,errornavigateToEdit(),navigateToList(),deleteEntity()refresh()formatDate(date),formatDateTime(date)
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:
- Page-builder preview = production output (no visual mismatch)
- GgsButton CSS maintained in ONE place
- Widget adds only canvas-specific behavior (inline editing, selection, resize)
- New SDK component automatically available in page-builder once widget wrapper is created
Scope: Refactor the most-used widgets first:
- Button, Input, Card, Select, Checkbox, Toggle (form controls)
- Table, Badge, Alert, EmptyState (data display)
- 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:
- Page-builder's property panel auto-generates editors for DataTable properties
- Vitto's
getAITrainingContext()includes the full DataTable API surface - Users can configure tables visually without touching code
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):
- Vitto generates Angular code that imports SDK components
- Output:
.ts,.html,.scssfiles in VFS - User edits via Vitto chat ("add a date range filter to the match list")
- Vitto modifies code surgically because it knows the exact @Input() API surface
Visual-first (new, uses page-builder):
- Vitto places SDK widgets on page-builder canvas with property configurations
- Output: Page definition (JSON) that renders via widget system
- User edits via property panel (click table → change columns, add filters)
- User also edits via Vitto chat (same surgical property mutations)
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:
- Workbench becomes a Vitto surface (like vitto-studio)
- Each wizard step is a Vitto tool/capability
- Step 1 (Models): Vitto defines entities from natural language, validated against entity definition schema
- Step 2 (State): Auto-generated from entities, Vitto can customize
- Step 3 (Actions): Vitto adds business logic hooks using SDK service patterns
- Step 4 (UI): Vitto composes from platform-sdk components via page-builder
- Step 5 (Test): Vitto runs generated tests
- Step 6 (Deploy): Triggers backend-vitto-api pipeline with full entity definitions
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:
log_decision()captures which components/configurations Vitto choseoutcome()captures whether the generation compiled and workedintelligence({ action: "reflect" })extracts lessonsgetAITrainingContext()includes accumulated patterns
Over time, Vitto's property template suggestions improve because it knows:
- Which column configurations users kept vs changed
- Which entity field types are most common
- Which component compositions work well together
- Which validation rules get added post-generation
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
- Per-universe microservice deployment — premature until dynamic modules are validated
- Cloudflare Workers deployment — even more premature
- i18n — not requested, add when needed
- File upload → R2 integration — Phase III stretch goal, not MVP
- Animation system in page-builder — not blocking anything
- Flutter export — deleted with layout-builder
- Accessibility overhaul — needed but separate initiative (no component has ARIA today)
- Responsive generation testing — page-builder infrastructure exists but not validated for generated code
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
- Phase I bug fixes: Each fix is an independent commit. Revert individual commits if a fix introduces regressions.
- Phase I consolidation: Re-export bridges mean old import paths keep working. If migration stalls, bridges stay indefinitely — no hard deadline.
- Phase II SDK promotion: New components (GgsEntityForm, GgsDataTable) are additive — they don't replace existing code. Widget wrapper refactoring is opt-in per widget. Can be done incrementally.
- Phase III: Generic
UniverseDataControllerremains as permanent fallback. Generated backend modules are per-universe — one broken generation doesn't affect others. - Feature flags: Not needed at the component level. The incremental approach (add new → migrate consumers → delete old) IS the feature flag pattern, just at the library level.
Success Criteria
Phase I
- Generated platform passes
ng buildwith zero errors - CRUD (list, create, edit, delete) works end-to-end with real MongoDB data
- No
Date.now()ID collisions in parallel generation - No
universe_universe_double-prefix in any collection name - Zero duplicate component implementations across libraries
Phase II
GgsEntityFormrenders dynamic forms from field definitionsGgsDataTablesupports column config, sort, filter, pagination- Widget wrappers render SDK components identically to standalone usage
- Page-builder property panel can edit all SDK component properties
- Composable method names match 1:1 with generated template usage
Phase III
- Per-entity Mongoose schemas generated with proper validators
- Per-entity DTOs generated with class-validator decorators
- Generated controllers handle typed CRUD with pagination
- 3-5 seed records created per entity with realistic data
- Generic UniverseDataController still works as fallback
Phase IV
- Vitto can modify a generated platform by mutating property values
- User can visually edit a DataTable's columns in page-builder property panel
- Modularity-lib workbench triggers Vitto-orchestrated generation
- At least 3 generation cycles show measurable improvement in output quality
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.