Next Steps - Actionable Implementation Guide

Concrete ordered tasks to complete platform generation with code patterns and file references

Next Steps - Actionable Implementation Guide

This document provides the ordered action items to complete platform generation. For architecture reference, see system-analysis.md.


Priority 1: Complete Deploy Step (Blocks Everything)

Problem

The deploy step UI exists but doesn't actually generate NX libraries.

Files to Modify

1. Deploy Step Component libs/libs-structural/modularity-lib/src/lib/components/steps/deploy-step/deploy-step.component.ts

// Add actual export call in startExport()
async startExport(): Promise<void> {
  this.isExporting.set(true);
  
  // Get module context
  const ctx = this.context();
  const entities = this.entities();
  const actions = this.actions();
  const embeddables = this.embeddables();
  
  // Call export service (THIS IS MISSING)
  const result = await this.moduleExportService.exportModule({
    moduleName: ctx.moduleName,
    entities,
    actions,
    embeddables,
    targetPath: `modules/${this.modulePath()}`
  });
  
  this.exportComplete.set(true);
  this.moduleExported.emit();
}

2. Module Export Service libs/libs-structural/modularity-lib/src/lib/services/module-export.service.ts

// Implement actual file generation
async exportModule(config: ExportConfig): Promise<ExportResult> {
  // 1. Generate TypeScript files
  const modelFiles = this.generateModels(config.entities);
  const actionFiles = this.generateActions(config.actions);
  const componentFiles = this.generateComponents(config.embeddables);
  
  // 2. Generate project.json
  const projectJson = this.generateProjectJson(config.moduleName);
  
  // 3. Generate tsconfig.lib.json (NO rootDir!)
  const tsconfig = this.generateTsConfig();
  
  // 4. Generate index.ts barrel
  const indexTs = this.generateBarrel(config);
  
  // 5. Write files to disk via backend
  await this.writeFiles(config.targetPath, {
    ...modelFiles,
    ...actionFiles,
    ...componentFiles,
    'project.json': projectJson,
    'tsconfig.lib.json': tsconfig,
    'src/index.ts': indexTs
  });
  
  // 6. Update tsconfig.base.json path mapping
  await this.updatePathMapping(config.moduleName, config.targetPath);
  
  return { success: true, path: config.targetPath };
}

Verification

# After implementing, run:
nx build <module-name>
# Should succeed without errors

Priority 2: Wire Action Flows to Singularity

Problem

Action builder creates flow definitions but doesn't persist them to Singularity backend.

Files to Modify

Action Flow Storage Service libs/libs-structural/modularity-lib/src/lib/services/action-flow-storage.service.ts

// Add Singularity persistence
async saveFlow(actionId: string, flow: ActionFlow): Promise<void> {
  // 1. Save locally (existing)
  this.localFlows.set(actionId, flow);
  
  // 2. NEW: Persist to Singularity backend
  await firstValueFrom(
    this.http.post('/api/singularity/flows', {
      name: flow.name,
      description: flow.description,
      blocks: flow.blocks,
      connections: flow.connections,
      category: 'module-action',
      universeId: this.contextService.currentUniverseId()
    })
  );
}

Backend Endpoint (already exists)

POST /singularity/flows in apis/backend-api/src/app/singularity/singularity.controller.ts


Priority 3: Implement Deployment Service

Problem

deployment.service.ts returns stub data; no actual deployment happens.

File to Implement

apis/backend-api/src/app/deployment/deployment.service.ts

@Injectable()
export class DeploymentService {
  constructor(
    private readonly spaceDatabaseService: SpaceDatabaseService,
    private readonly configService: ConfigService,
  ) {}

  async deployUniverse(
    tenantId: string,
    universeId: string,
    config: DeploymentConfig
  ): Promise<DeploymentResult> {
    const deploymentId = `deploy_${Date.now()}`;
    
    // 1. Fetch all universe data
    const pages = await this.fetchPages(tenantId, universeId);
    const modules = await this.fetchModules(tenantId, universeId);
    const widgets = await this.fetchWidgets(tenantId);
    const assets = await this.fetchAssets(tenantId, universeId);
    
    // 2. Bundle into deployable artifact
    const bundle = {
      manifest: { universeId, version: Date.now(), pages: pages.map(p => p.id) },
      pages: pages,
      modules: modules,
      widgets: widgets,
      assets: assets
    };
    
    // 3. Generate static output or serverless bundle
    const outputPath = await this.generateOutput(bundle, config);
    
    // 4. Upload to hosting provider
    const url = await this.uploadToHost(outputPath, config);
    
    // 5. Store deployment record
    await this.storeDeploymentRecord(tenantId, deploymentId, {
      universeId,
      url,
      status: 'deployed',
      config
    });
    
    return {
      success: true,
      deploymentId,
      url,
      subdomain: this.generateSubdomain(tenantId, universeId),
      status: 'deployed'
    };
  }
  
  private async uploadToHost(path: string, config: DeploymentConfig): Promise<string> {
    // Integration with Railway/Vercel/custom host
    // TODO: Implement based on hosting choice
  }
}

Priority 4: Audit Property Migration

Task

Find widgets still using manual styles instead of usePropertyStyles.

Command

# Find manual style patterns
grep -rn "ngStyle\|this\.styles\s*=\|get customStyles" \
  libs/libs-widgets/widgets/src/lib/components/ \
  --include="*.ts"

Expected Output

Widgets should ONLY have:

customStyles = usePropertyStyles(this.widget, this.activeBreakpoint, ['font', 'appearance']);

Conversion Pattern

// BEFORE (70+ lines)
get customStyles(): Record<string, string> {
  const props = this.widget()?.properties ?? {};
  return {
    fontSize: props.fontSize ? `${props.fontSize}px` : undefined,
    fontWeight: props.fontWeight ?? undefined,
    color: props.textColor ?? undefined,
    backgroundColor: props.backgroundColor ?? undefined,
    // ... 60 more lines
  };
}

// AFTER (1 line)
customStyles = usePropertyStyles(this.widget, this.activeBreakpoint, ['font', 'appearance']);

Priority 5: Verify Embeddable Wrapping

Task

Ensure all 5 widget pairs properly wrap (not re-implement) their embeddables.

Checklist

Widget Import Check Template Check
ButtonWidgetComponent imports: [UiButtonComponent] <ui-button .../>
InputWidgetComponent imports: [UiInputComponent] <ui-input .../>
CardWidgetComponent imports: [UiCardComponent] <ui-card .../>
CheckboxWidgetComponent imports: [UiCheckboxComponent] <ui-checkbox .../>
SelectWidgetComponent imports: [UiSelectComponent] <ui-select .../>

Verification Command

# For each widget, check imports
grep -A 10 "@Component" libs/libs-widgets/widgets/src/lib/components/button-widget/button-widget.component.ts | grep "imports"

Priority 6: Variant CRUD E2E Test

Task

Verify variant lifecycle works end-to-end.

Test File

apps/gograb-space-e2e/src/variant-crud.spec.ts

import { test, expect } from '@playwright/test';

test.describe('Variant CRUD', () => {
  const API = 'http://localhost:3000/api';
  const SPACE_ID = 'test-space';
  let widgetId: string;

  test.beforeAll(async ({ request }) => {
    // Create test widget
    const res = await request.post(`${API}/spaces/${SPACE_ID}/widgets`, {
      data: {
        name: 'Test Button',
        type: 'button',
        category: 'ui',
        variants: []
      }
    });
    widgetId = (await res.json()).data.id;
  });

  test('create variant', async ({ request }) => {
    const res = await request.post(`${API}/spaces/${SPACE_ID}/widgets/${widgetId}/variants`, {
      data: {
        name: 'Primary Blue',
        properties: { backgroundColor: '#3b82f6', color: '#ffffff' }
      }
    });
    expect(res.ok()).toBeTruthy();
    const data = await res.json();
    expect(data.data.variants).toContainEqual(
      expect.objectContaining({ name: 'Primary Blue' })
    );
  });

  test('list variants', async ({ request }) => {
    const res = await request.get(`${API}/spaces/${SPACE_ID}/widgets/${widgetId}/variants`);
    expect(res.ok()).toBeTruthy();
    const data = await res.json();
    expect(data.data.length).toBeGreaterThan(0);
  });

  test('update variant', async ({ request }) => {
    // Get current variants
    const list = await request.get(`${API}/spaces/${SPACE_ID}/widgets/${widgetId}/variants`);
    const variants = (await list.json()).data;
    const variantId = variants[0].id;

    // Update
    const res = await request.put(
      `${API}/spaces/${SPACE_ID}/widgets/${widgetId}/variants/${variantId}`,
      { data: { name: 'Updated Blue' } }
    );
    expect(res.ok()).toBeTruthy();
  });
});

Priority 7: Build Reference Space

Goal

Build a "Contact Form" space from scratch using all tools.

Steps

  1. Module Workbench (/modularity/workbench)

    • Models: Contact { email, message, createdAt }
    • State: contacts collection
    • Actions: CreateContact, ListContacts
    • UI: ContactFormEmbeddable, ContactListEmbeddable
    • Deploy to modules/contact-form/
  2. Backend Registration

    curl -X POST http://localhost:3000/api/spaces/test/modules \
      -H "Content-Type: application/json" \
      -d '{"name":"Contact Form","type":"module","scope":"shared","isActive":true}'
    
  3. Widget Creation (/widget-builder/workspace)

    • Create ContactFormWidget wrapping ContactFormEmbeddable
    • Add appearance property group
    • Create 2 variants: "Light" and "Dark"
  4. Page Composition (/editor/page/contact-us)

    • Drag ContactFormWidget
    • Configure properties
    • Bind submit to CreateContact action
  5. Universe Deployment (/modular-universe/universe/contact-space)

    • Place Contact Form module
    • Configure deployment settings
    • Deploy
  6. Verify

    • Visit deployed URL
    • Submit form
    • Check data persisted

Success Checklist


Timeline Estimate

Priority Task Estimate
P1 Deploy step implementation 4-6 hours
P2 Singularity flow wiring 2-3 hours
P3 Deployment service 6-8 hours
P4 Property migration audit 2-3 hours
P5 Embeddable wrapping verification 1-2 hours
P6 E2E tests 3-4 hours
P7 Reference space build 4-6 hours
Total 22-32 hours

Related Documentation:

Last Updated: December 9, 2025 - Session 10.2