2026 03 20 Library Absorption Module Builder Migration

Documentation for Plans

Library Absorption & Module Builder Migration — Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Absorb the useful parts of 4 architecturally-stuck libraries (modularity-lib, gograb-ai-lib, module-generator, modular-universe) into vitto-studio and vitto-chat, delete dead code, create a vitto-chat module-builder plugin, and ensure all NX libs compile and route correctly with zero deprecated references.

Architecture: Move modularity-lib's model types + cascade services + workbench UI into vitto-studio. Relocate gograb-ai-lib's active services (WorkflowStateService, VittoMonitorService, Image/VideoGenerationService) and 22 dashboard components into vitto-hub. Create a new vitto-chat plugin that wires module-building intelligence into chat. Delete module-generator, module-browser-lib, cursor-agents, multi-agent-dashboard, and all deprecated services. Update all imports monorepo-wide.

Tech Stack: Angular 20+, standalone components, signals, RxJS, NestJS, NX monorepo, vitest

Session: S248 | Prereq Plans: Platform Infrastructure Evolution


Review Fixes Applied (from plan-document-reviewer)

C1 (Missing ModularityModule/Manifest): Add modularity.module.ts and modularity.manifest.ts to Phase 3 Task 3.1 — copy to vitto-studio and update apps/gograb-space/src/app/app.config.ts import.

C2 (Oversimplified routes): Task 3.1 Step 5 must copy the FULL modularity.routes.ts (212 lines) including all WorkbenchStepRoutes children (models, state, actions, actions/:actionId, ui, test, deploy), ai-scaffold, test-export, and legacy redirects. Do NOT write a simplified version.

C3 (export * circular dep risk): ALL re-export shims must use NAMED exports, never export *. Example: export { ModuleContext, createEmptyModuleContext, calculateModuleProgress } from '@libs-structural/vitto-studio';

C4 (Steps directory location): The steps/ directory is a SIBLING of module-workbench/ in modularity-lib, NOT a child. Mirror this structure in vitto-studio: components/steps/ not components/module-workbench/steps/.

C5 (inject() outside injection context): Task 2.2 and 2.4 must use class-level inject() with { optional: true }, NOT inject() inside method bodies. Fix: private readonly vfs = inject(VfsService, { optional: true }); as a class field.

C6 (Missing gograb-ai-lib consumers): Services that STAY in gograb-ai-lib (not moved): AIFactoryService, AIOrchestratorService, VibeCodingOrchestratorService, AIModuleMatcherService, IntelligenceCoordinatorService, UniversalWidgetGeneratorService, GlobalAiSettingsService, TrainingDataExtractorService, PromptManagerPortalService, AnalyticsService, BackendAuthAdapterService, AiChatConfigService, AiChatLearningService, OrchestrationClientService. Components that STAY: AiChatCoreComponent, AiConfigComponent, AiModelSelectorComponent, ImageGenerationComponent, VideoGenerationComponent, VibeCodingInterfaceComponent, AgentPanelComponent, ModuleGenerationPanelComponent, AiGuideComponent, AiInfoComponent, AiConnectionStatusComponent, ApiKeyManagerComponent.

C7 (Missing @modular-universe alias): Task 5.2 Step 8 must also remove @modular-universe from tsconfig.base.json (not just @libs-structural/modular-universe).

C8 (tsconfig paths location): Library path aliases (@libs-structural/*, @libs-ai/*) are in tsconfig.base.json DIRECTLY, NOT in config/tsconfig-paths/. Task 5.2 Step 8 must edit tsconfig.base.json.

I1 (AIModuleReceiverService): Do NOT delete this service. Move it to vitto-studio with full functionality (SSE reception, backend fetch, reception state signals, getRecentModules()). Rewire from WorkflowSSEClientService to WorkflowStateService.

I2 (git add -A): Replace git add -A in Task 5.1 Step 7 with git add libs/ apps/ APIs/.

I3 (VittoObservabilityDataService): Add to Task 3.2 — move vitto-observability-data.service.ts and vitto-observability.types.ts to vitto-hub alongside ObservatoryDataService.

I4 (EnhancedImageGenerationService): Stays in gograb-ai-lib (internal wrapper, no external consumers outside the lib).

I5 (Plugin quickActions not reactive): Document as known limitation. The ChatPlugin interface defines quickActions as a static QuickAction[]. Future work: make it Signal<QuickAction[]> or a getter.

I6 (flow-engine-lib consumers): 5 files in flow-engine-lib import from @libs-ai/gograb-ai-lib. These STAY as-is since gograb-ai-lib is NOT deleted and these services remain in it. Add to Task 5.1 audit as "verified — no change needed."

S4 (space-map-lib consumer): space-map-lib/world-node.component.ts imports AiModelSelectorComponent from gograb-ai-lib. This STAYS since AiModelSelectorComponent remains in gograb-ai-lib.


Critical Risks & Mitigations

Risk Impact Mitigation
Circular dependency when moving services between libs Build fails monorepo-wide Phase 1 moves ONLY models/types (no service deps). Services move in Phase 2 with re-export shims.
22 gograb-ai-lib dashboard components reference internal services Breaking those routes for gograb-dev/gograb-admin Phase 3 moves components + their services together. Re-export barrel in gograb-ai-lib during transition.
autoFlashState keys must not change Users lose persisted module data Keep identical IndexedDB keys when moving ModuleContextService.
modularity routes registered in app.routes.ts Route /modularity/workbench stops working Phase 2 re-exports from vitto-studio, app.routes.ts import path changes last.
WorkflowSSEClientService has one live external consumer (ai-module-receiver.service.ts) Module registration after generation breaks Phase 2 rewires AIModuleReceiverService to use WorkflowStateService instead before deleting WorkflowSSEClientService.

File Map

New Files

File Responsibility
libs/libs-structural/vitto-studio/src/lib/models/module-context.model.ts ModuleContext + factory functions (moved from modularity-lib)
libs/libs-structural/vitto-studio/src/lib/models/entity-definition.model.ts EntityDefinition + factories (moved)
libs/libs-structural/vitto-studio/src/lib/models/action-definition-v2.model.ts ActionDefinitionV2 + CRUD factory (moved)
libs/libs-structural/vitto-studio/src/lib/models/embeddable-v2.model.ts EmbeddableDefinitionV2 + factories (moved)
libs/libs-structural/vitto-studio/src/lib/models/state-definition.model.ts StateDefinition + factories (moved)
libs/libs-structural/vitto-studio/src/lib/models/action-flow.model.ts ActionFlow + block templates (moved)
libs/libs-structural/vitto-studio/src/lib/models/ai-metadata.model.ts AITrainableMetadata (moved)
libs/libs-structural/vitto-studio/src/lib/models/index.ts Models barrel
libs/libs-structural/vitto-studio/src/lib/adapters/phase-result-to-module-context.adapter.ts Vitto→ModuleContext seam (moved)
libs/libs-structural/vitto-studio/src/lib/services/module-context.service.ts Central module state + cascade logic (moved, refactored for VFS)
libs/libs-structural/vitto-studio/src/lib/services/module-validation.service.ts Quality gate (moved)
libs/libs-structural/vitto-studio/src/lib/services/module-export.service.ts Real code generation → VFS (moved, stubs replaced)
libs/libs-structural/vitto-studio/src/lib/services/module-storage.service.ts Module catalog (moved)
libs/libs-structural/vitto-studio/src/lib/services/module-sync.service.ts MongoDB sync (moved)
libs/libs-structural/vitto-studio/src/lib/services/flow-integration.service.ts Flow validation + code gen (moved)
libs/libs-structural/vitto-studio/src/lib/services/action-flow-storage.service.ts Per-action flow persistence (moved)
libs/libs-structural/vitto-studio/src/lib/services/contextual-block.service.ts Block palette (moved)
libs/libs-structural/vitto-studio/src/lib/services/entity-extraction.service.ts AI entity extraction (moved)
libs/libs-structural/vitto-studio/src/lib/services/embeddable-to-widget-mapper.service.ts Module→PageBuilder bridge (moved)
libs/libs-structural/vitto-studio/src/lib/services/modularity-integration.service.ts Cross-tool events (moved, deploy wired to VFS)
libs/libs-structural/vitto-studio/src/lib/services/workflow-state.service.ts Live SSE bridge to Vitto generation (moved from gograb-ai-lib)
libs/libs-structural/vitto-studio/src/lib/components/module-workbench/ Full workbench UI (moved from modularity-lib)
libs/libs-structural/vitto-studio/src/lib/components/module-workbench/steps/ 6 step components (moved)
libs/libs-structural/vitto-studio/src/lib/components/action-flow-builder/ Visual flow canvas (moved)
libs/libs-structural/vitto-studio/src/lib/components/modularity-dashboard/ Module dashboard (moved)
libs/libs-structural/vitto-studio/src/lib/components/module-card/ Module card (moved)
libs/libs-structural/vitto-studio/src/lib/components/ai-builder/ AI module builder (moved)
libs/libs-ai/vitto-chat/src/lib/plugins/module-builder.plugin.ts NEW: Module builder chat plugin
libs/libs-ai/vitto-hub/src/lib/components/dashboards/ Dashboard components (moved from gograb-ai-lib)
libs/libs-ai/vitto-hub/src/lib/services/vitto-monitor.service.ts VittoMonitorService (moved from gograb-ai-lib)
libs/libs-ai/vitto-hub/src/lib/services/observatory-data.service.ts ObservatoryDataService (moved from gograb-ai-lib)
libs/libs-ai/vitto-hub/src/lib/types/vitto.types.ts Vitto types (moved from gograb-ai-lib)

Deleted Files/Directories

Path Reason
libs/libs-ai/module-generator/ Entirely replaced by backend 6-phase pipeline
libs/libs-structural/module-browser-lib/ Only consumer was stranded ModularUniverseBuilderComponent
libs/libs-ai/cursor-agents/ Empty directory
libs/libs-ai/multi-agent-dashboard/ Empty directory
libs/libs-structural/modularity-lib/ All useful code absorbed into vitto-studio
libs/libs-structural/modular-universe/ Live renderers moved to universe-canvas, rest dead

Modified Files (key ones)

File Change
libs/libs-structural/vitto-studio/src/index.ts Add all new exports
libs/libs-ai/vitto-chat/src/index.ts Add module-builder plugin export
libs/libs-ai/vitto-chat/src/lib/plugins/plugin-presets.ts Register module-builder plugin
libs/libs-ai/vitto-hub/src/index.ts Add dashboard component + service exports
apps/gograb-space/src/app/app.routes.ts Change modularity import from modularity-lib → vitto-studio
apps/gograb-dev/src/app/app.routes.ts Change 17 dashboard imports from gograb-ai-lib → vitto-hub
apps/gograb-admin/src/app/app.routes.ts Change 3 dashboard imports from gograb-ai-lib → vitto-hub
apps/gograb-ai/src/app/app.routes.ts Change 2 component imports from gograb-ai-lib → vitto-hub
tsconfig.base.json or config/tsconfig-paths/ Remove path aliases for deleted libs
All files importing from @libs-structural/modularity-lib Update to @libs-structural/vitto-studio
All files importing from @libs-ai/module-generator Update to @libs-structural/vitto-studio (types only)
All files importing WorkflowStateService from @libs-ai/gograb-ai-lib Update to @libs-structural/vitto-studio
All files importing VittoMonitorService from @libs-ai/gograb-ai-lib Update to @libs-ai/vitto-hub

Phase 1: Move Models & Types (zero runtime risk)

Impact: Pure file moves. No behavior change. All imports updated. Models are pure TypeScript types + factory functions with zero service dependencies, so this is safe to do first.


Task 1.1: Move modularity-lib models to vitto-studio

Files:

mkdir -p libs/libs-structural/vitto-studio/src/lib/models

Copy each model file from libs/libs-structural/modularity-lib/src/lib/models/ to libs/libs-structural/vitto-studio/src/lib/models/. The files are pure types + factory functions with no Angular service imports, so they copy cleanly:

Update any internal cross-imports within the model files to use relative paths (they should already be relative).

Create libs/libs-structural/vitto-studio/src/lib/models/index.ts re-exporting all model files.

Add to libs/libs-structural/vitto-studio/src/index.ts:

// Module Builder Models
export * from './lib/models';

Update libs/libs-structural/modularity-lib/src/lib/models/index.ts to re-export from vitto-studio:

// DEPRECATED: These models have moved to @libs-structural/vitto-studio
// This re-export exists for backward compatibility during migration.
export * from '@libs-structural/vitto-studio';

This ensures ALL existing consumers keep working without any import changes yet.

Run: npx nx build vitto-studio --skip-nx-cache Expected: Build succeeds with models exported.

git add libs/libs-structural/vitto-studio/src/lib/models/ libs/libs-structural/vitto-studio/src/index.ts libs/libs-structural/modularity-lib/src/lib/models/index.ts
git commit -m "[S248.1] refactor: move modularity-lib models to vitto-studio with re-export shim"

Task 1.2: Move vitto.types.ts to vitto-hub

Files:

Copy libs/libs-ai/gograb-ai-lib/src/lib/types/vitto.types.ts to libs/libs-ai/vitto-hub/src/lib/types/vitto.types.ts.

Check: any imports from ../ or relative paths inside the types file that reference gograb-ai-lib internals. If found, inline the types or adjust imports.

Add to libs/libs-ai/vitto-hub/src/index.ts:

// Vitto Types
export * from './lib/types/vitto.types';

Update libs/libs-ai/gograb-ai-lib/src/lib/types/vitto.types.ts to:

// DEPRECATED: These types have moved to @libs-ai/vitto-hub
export * from '@libs-ai/vitto-hub';

Run: npx nx build vitto-hub --skip-nx-cache Expected: Build succeeds.

git add libs/libs-ai/vitto-hub/src/lib/types/ libs/libs-ai/vitto-hub/src/index.ts libs/libs-ai/gograb-ai-lib/src/lib/types/vitto.types.ts
git commit -m "[S248.1] refactor: move vitto.types.ts to vitto-hub with re-export shim"

Task 1.3: Move module-generator types to vitto-studio

Files:

Read libs/libs-ai/module-generator/src/lib/types/module-generation.types.ts. Extract ONLY:

Add these to a new file: libs/libs-structural/vitto-studio/src/lib/models/generated-module.model.ts

Add to libs/libs-structural/vitto-studio/src/lib/models/index.ts:

export * from './generated-module.model';

Update libs/libs-ai/module-generator/src/index.ts to re-export the types:

// DEPRECATED: Types moved to @libs-structural/vitto-studio
export { GeneratedModule, GeneratedCodeModule, generatedModuleToUniversalModule } from '@libs-structural/vitto-studio';

Run: npx nx build vitto-studio --skip-nx-cache Expected: Build succeeds.

git add libs/libs-structural/vitto-studio/src/lib/models/ libs/libs-ai/module-generator/src/index.ts
git commit -m "[S248.1] refactor: move GeneratedModule types to vitto-studio"

Phase 2: Move Services to vitto-studio (the core migration)

Impact: Services move with full functionality. Re-export shims ensure zero breakage. The key change is wiring ModuleExportService to VFS and replacing stubs with real code gen.


Task 2.1: Move PhaseResultToModuleContextAdapter

Files:

Copy libs/libs-structural/modularity-lib/src/lib/adapters/phase-result-to-module-context.adapter.ts to libs/libs-structural/vitto-studio/src/lib/adapters/.

Update model imports to use relative ../models/ paths instead of ../models/ (same, since we moved models already).

Copy libs/libs-structural/modularity-lib/src/lib/adapters/phase-result-to-module-context.adapter.spec.ts to libs/libs-structural/vitto-studio/src/lib/adapters/.

Add to libs/libs-structural/vitto-studio/src/index.ts:

export { PhaseResultToModuleContextAdapter } from './lib/adapters/phase-result-to-module-context.adapter';

Update modularity-lib adapter to re-export:

export { PhaseResultToModuleContextAdapter } from '@libs-structural/vitto-studio';

Run: npx vitest run libs/libs-structural/vitto-studio/src/lib/adapters/ --reporter=verbose Expected: PASS

git add libs/libs-structural/vitto-studio/src/lib/adapters/ libs/libs-structural/modularity-lib/src/lib/adapters/
git commit -m "[S248.1] refactor: move PhaseResultToModuleContextAdapter to vitto-studio"

Task 2.2: Move ModuleContextService (the cascade engine)

Files:

Copy libs/libs-structural/modularity-lib/src/lib/services/module-context.service.ts to libs/libs-structural/vitto-studio/src/lib/services/.

Update imports:

CRITICAL: The autoFlashState keys in this service MUST remain identical:

Verify these keys match the original. If they change, users lose all persisted module data.

Add optional class-level injections (NOT inside methods — Angular requires injection context):

// Class-level optional injections:
private readonly vfs = inject(VfsService, { optional: true });
private readonly exportService = inject(ModuleExportService, { optional: true });

// At the end of addEntity(), after all cascade operations:
private writeModuleToVfs(context: ModuleContext): void {
  if (!this.vfs || !this.exportService) return; // Not in studio context

  const files = this.exportService.generateModuleFiles(context);
  for (const file of files) {
    this.vfs.writeFile(file.path, file.content);
  }
}

This is optional injection — the service works without VFS (for non-studio contexts like the modularity dashboard).

Add to vitto-studio index.ts. Create re-export shim in modularity-lib services barrel.

Run: npx nx build vitto-studio --skip-nx-cache Expected: Build succeeds (with some services still referencing modularity-lib).

git add libs/libs-structural/vitto-studio/src/lib/services/module-context.service.ts libs/libs-structural/vitto-studio/src/index.ts libs/libs-structural/modularity-lib/src/lib/services/module-context.service.ts
git commit -m "[S248.1] refactor: move ModuleContextService to vitto-studio with VFS write hook"

Task 2.3: Move ModuleExportService — replace stubs with real code gen

Files:

This is the most important task. The current service has placeholder implementations. We replace them with code that generates the same Angular patterns as the Vitto 6-phase pipeline.

Copy libs/libs-structural/modularity-lib/src/lib/services/module-export.service.ts.

Identify all stub/placeholder code:

Replace the stub with code that generates a real Angular service:

generateActionClass(action: ActionDefinitionV2, context: ModuleContext): string {
  const entityName = this.getEntityName(action, context);
  const inputs = action.inputs.map(i => `${i.name}: ${this.tsType(i.type)}`).join(', ');
  const outputType = action.outputs.length > 0
    ? this.tsType(action.outputs[0].type)
    : 'void';

  return `import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { API_BASE_URL } from '@libs-foundational/core-angular';

@Injectable({ providedIn: 'root' })
export class ${action.name}Service {
  private readonly http = inject(HttpClient);
  private readonly apiUrl = inject(API_BASE_URL);

  execute(${inputs}): Observable<${outputType}> {
    ${this.generateHttpCall(action, entityName)}
  }
}
`;
}

private generateHttpCall(action: ActionDefinitionV2, entityName: string): string {
  const basePath = `\${this.apiUrl}/api/${entityName.toLowerCase()}s`;
  switch (action.category) {
    case 'crud':
      if (action.name.startsWith('getAll')) return `return this.http.get<${entityName}[]>(\`${basePath}\`);`;
      if (action.name.startsWith('get')) return `return this.http.get<${entityName}>(\`${basePath}/\${id}\`);`;
      if (action.name.startsWith('create')) return `return this.http.post<${entityName}>(\`${basePath}\`, data);`;
      if (action.name.startsWith('update')) return `return this.http.put<${entityName}>(\`${basePath}/\${id}\`, data);`;
      if (action.name.startsWith('delete')) return `return this.http.delete<void>(\`${basePath}/\${id}\`);`;
      return `return this.http.post(\`${basePath}/\${action.name}\`, {});`;
    default:
      return `return this.http.post(\`${basePath}/\${action.name}\`, { ${action.inputs.map(i => i.name).join(', ')} });`;
  }
}

New method that returns a complete { path, content }[] for a module:

generateModuleFiles(context: ModuleContext): Array<{ path: string; content: string }> {
  const slug = context.moduleSlug;
  const category = context.category || 'general';
  const basePath = `modules/${category}/${slug}`;
  const files: Array<{ path: string; content: string }> = [];

  // 1. Entity models
  for (const entity of context.entities) {
    files.push({
      path: `${basePath}/models/${entity.name.toLowerCase()}.model.ts`,
      content: generateEntityInterface(entity),
    });
  }

  // 2. Services (one per entity with real CRUD)
  for (const entity of context.entities) {
    const actions = context.actions.filter(a => a.sourceEntityId === entity.id);
    files.push({
      path: `${basePath}/services/${entity.name.toLowerCase()}.service.ts`,
      content: this.generateEntityService(entity, actions, context),
    });
  }

  // 3. Components (list + detail per entity)
  for (const entity of context.entities) {
    files.push(
      {
        path: `${basePath}/components/${entity.name.toLowerCase()}-list/${entity.name.toLowerCase()}-list.component.ts`,
        content: this.generateListComponent(entity, context),
      },
      {
        path: `${basePath}/components/${entity.name.toLowerCase()}-list/${entity.name.toLowerCase()}-list.component.html`,
        content: this.generateListTemplate(entity, context),
      },
      {
        path: `${basePath}/components/${entity.name.toLowerCase()}-list/${entity.name.toLowerCase()}-list.component.scss`,
        content: this.generateListStyles(),
      },
      {
        path: `${basePath}/components/${entity.name.toLowerCase()}-detail/${entity.name.toLowerCase()}-detail.component.ts`,
        content: this.generateDetailComponent(entity, context),
      },
      {
        path: `${basePath}/components/${entity.name.toLowerCase()}-detail/${entity.name.toLowerCase()}-detail.component.html`,
        content: this.generateDetailTemplate(entity, context),
      },
      {
        path: `${basePath}/components/${entity.name.toLowerCase()}-detail/${entity.name.toLowerCase()}-detail.component.scss`,
        content: this.generateDetailStyles(),
      },
    );
  }

  // 4. Routes
  files.push({
    path: `${basePath}/${slug}.routes.ts`,
    content: this.generateRoutesFile(context),
  });

  // 5. Index barrel
  files.push({
    path: `${basePath}/index.ts`,
    content: this.generateIndexFile(context),
  });

  return files;
}

The generated components must use the real SDK components from @libs-core/ui-framework:

private generateListTemplate(entity: EntityDefinition, context: ModuleContext): string {
  const displayFields = entity.fields.filter(f => f.uiHints?.showInList !== false && f.name !== 'id');
  const columns = displayFields.map(f => `{ field: '${f.name}', header: '${this.humanize(f.name)}' }`);

  return `<div class="module-page">
  <div class="page-header">
    <h1>${this.humanize(entity.pluralName || entity.name + 's')}</h1>
    <button ggs-button variant="primary" (click)="onCreate()">
      <i class="fas fa-plus"></i> Create ${entity.name}
    </button>
  </div>

  <ggs-table
    [data]="items()"
    [columns]="columns"
    [loading]="loading()"
    (rowClick)="onRowClick($event)">
  </ggs-table>
</div>`;
}

Create libs/libs-structural/vitto-studio/src/lib/services/__tests__/module-export.service.spec.ts:

describe('ModuleExportService', () => {
  it('should generate valid TypeScript service for CRUD entity', () => {
    const context = createEmptyModuleContext('Users', 'crud');
    const entity = createEntityWithDefaults('User');
    context.entities = [entity];
    context.actions = createCrudActionsForEntity(entity.id, 'User', 'Users', entity.fields);

    const files = service.generateModuleFiles(context);
    const serviceFile = files.find(f => f.path.includes('user.service.ts'));

    expect(serviceFile).toBeDefined();
    expect(serviceFile!.content).toContain('inject(HttpClient)');
    expect(serviceFile!.content).toContain('inject(API_BASE_URL)');
    expect(serviceFile!.content).not.toContain('// TODO');
    expect(serviceFile!.content).not.toContain('placeholder');
  });

  it('should generate list component using ggs-table', () => {
    const context = createEmptyModuleContext('Users', 'crud');
    const entity = createEntityWithDefaults('User');
    context.entities = [entity];

    const files = service.generateModuleFiles(context);
    const template = files.find(f => f.path.includes('-list.component.html'));

    expect(template).toBeDefined();
    expect(template!.content).toContain('ggs-table');
    expect(template!.content).toContain('ggs-button');
  });

  it('should generate routes file with real Angular routes', () => {
    const context = createEmptyModuleContext('Tasks', 'crud');
    const entity = createEntityWithDefaults('Task');
    context.entities = [entity];

    const files = service.generateModuleFiles(context);
    const routes = files.find(f => f.path.includes('.routes.ts'));

    expect(routes).toBeDefined();
    expect(routes!.content).toContain('Routes');
    expect(routes!.content).toContain('loadComponent');
  });
});

Run: npx vitest run libs/libs-structural/vitto-studio/src/lib/services/__tests__/module-export.service.spec.ts --reporter=verbose Expected: ALL PASS

git add libs/libs-structural/vitto-studio/src/lib/services/module-export.service.ts libs/libs-structural/vitto-studio/src/lib/services/__tests__/
git commit -m "[S248.1] feat: real code generation in ModuleExportService — no stubs, uses SDK components"

Task 2.4: Move remaining services (batch)

Files: All remaining services from modularity-lib to vitto-studio.

Move these services (copy + update imports + re-export shim):

Each service: update model imports to ../models/, update service cross-imports to relative paths.

Replace the TODO: Call backend API in deployToWorkspace(). Use class-level injections (already injected at class level, NOT inside the method):

// Class-level (already injected):
private readonly exportService = inject(ModuleExportService);
private readonly vfsService = inject(VfsService, { optional: true });

async deployToWorkspace(context: ModuleContext): Promise<void> {
  const files = this.exportService.generateModuleFiles(context);
  if (this.vfsService) {
    for (const file of files) {
      this.vfsService.writeFile(file.path, file.content);
    }
  }

  this.platformEvents$.next({
    type: 'GENERATION_COMPLETED',
    moduleId: context.moduleId,
    timestamp: Date.now(),
  });
}

This service uses setTimeout-based fake generation. It's superseded by WorkflowStateService. Do NOT move it.

The 14-keyword heuristic analysis is superseded by EntityExtractionService (Anthropic-backed). The AI Builder component will use EntityExtractionService directly.

The backend endpoints it calls (/api/modules/export, /api/modules/register-nx) don't exist. Dead code.

Move AIModuleReceiverService to vitto-studio (do NOT delete — it has substantial functionality: backend fetch, reception state signals, getRecentModules(), isReceiving computed).

Key change: replace the WorkflowSSEClientService.lastOrchestrationResult() effect with WorkflowStateService signals:

// Before (deprecated path):
// effect(() => {
//   const result = this.workflowSSEClient.lastOrchestrationResult();
//   if (result) this.receiveFromOrchestration(result);
// });

// After (active path via WorkflowStateService):
private readonly workflowState = inject(WorkflowStateService);

constructor() {
  effect(() => {
    const lastResult = this.workflowState.lastCompletedResult();
    if (lastResult) this.receiveFromOrchestration(lastResult);
  });
}

Also ensure StudioGenerationListenerService calls PhaseResultToModuleContextAdapter.adapt() on per-module SSE events so the workbench UI updates progressively:

// In StudioGenerationListenerService.handleModuleGenerated(), after writing files to VFS:
const moduleContext = this.adapter.adapt(event.data);
this.moduleContextService?.loadModuleContext(moduleContext);

Update libs/libs-structural/modularity-lib/src/lib/services/index.ts:

// DEPRECATED: All services moved to @libs-structural/vitto-studio
export * from '@libs-structural/vitto-studio';

Run: npx nx build vitto-studio --skip-nx-cache && npx nx build modularity-lib --skip-nx-cache Expected: Both build successfully.

git add libs/libs-structural/vitto-studio/src/lib/services/ libs/libs-structural/modularity-lib/src/lib/services/
git commit -m "[S248.1] refactor: move all modularity services to vitto-studio, delete dead services, wire deploy to VFS"

Task 2.5: Move WorkflowStateService to vitto-studio

Files:

Copy libs/libs-ai/gograb-ai-lib/src/lib/services/workflow-state.service.ts to libs/libs-structural/vitto-studio/src/lib/services/.

Check imports: if it imports from ../types/vitto.types, update to @libs-ai/vitto-hub (where we moved the types in Task 1.2). If it imports other gograb-ai-lib internals, inline them.

Export WorkflowStateService and all its types (WorkflowPhase, StartWorkflowRequest, WorkflowSSEEvent, ModuleManifest, ModuleGeneratedEvent) from vitto-studio.

Create re-export shim in gograb-ai-lib.

Run:

npx nx build vitto-studio --skip-nx-cache
npx nx build vitto-chat --skip-nx-cache
npx nx build vibe-universe --skip-nx-cache

Expected: All build (consuming via shim for now).

git add libs/libs-structural/vitto-studio/src/lib/services/workflow-state.service.ts libs/libs-ai/gograb-ai-lib/src/lib/services/workflow-state.service.ts libs/libs-structural/vitto-studio/src/index.ts
git commit -m "[S248.1] refactor: move WorkflowStateService to vitto-studio with re-export shim"

Phase 3: Move Components

Impact: Workbench UI moves to vitto-studio. Dashboard components move to vitto-hub. Routes updated.


Task 3.1: Move workbench components to vitto-studio

Files:

Copy these directories from libs/libs-structural/modularity-lib/src/lib/components/ to libs/libs-structural/vitto-studio/src/lib/components/:

In every copied .ts file:

The current component uses legacy @Input()/@Output() decorators. Convert to signal-based:

// Before:
@Input() actionId: string;
@Input() actionName: string;
@Input() moduleId: string;
@Output() flowChanged = new EventEmitter<ActionFlow>();

// After:
readonly actionId = input.required<string>();
readonly actionName = input<string>('');
readonly moduleId = input<string>('');
readonly flowChanged = output<ActionFlow>();

The AI Builder currently uses the deleted AIReactiveAnalysisService (14-keyword heuristic). Replace with:

// Replace AIReactiveAnalysisService with EntityExtractionService
private readonly entityExtraction = inject(EntityExtractionService);

analyzeDescription(text: string): void {
  this.entityExtraction.extract(text).subscribe(result => {
    this.detectedEntities.set(result.entities);
    this.analysisConfidence.set(result.confidence);
    this.analysisSource.set(result.source); // 'ai' or 'local'
  });
}

Create libs/libs-structural/vitto-studio/src/lib/routes/modularity.routes.ts:

import { Routes } from '@angular/router';

export const MODULARITY_ROUTES: Routes = [
  {
    path: '',
    loadComponent: () => import('../components/modularity-dashboard/modularity-dashboard.component')
      .then(m => m.ModularityDashboardComponent),
  },
  {
    path: 'ai-builder',
    loadComponent: () => import('../components/ai-builder/ai-builder.component')
      .then(m => m.AIModuleBuilderComponent),
  },
  {
    path: 'workbench',
    loadComponent: () => import('../components/module-workbench/module-workbench.component')
      .then(m => m.ModuleWorkbenchComponent),
  },
  {
    path: 'workbench/:moduleId',
    loadComponent: () => import('../components/module-workbench/module-workbench.component')
      .then(m => m.ModuleWorkbenchComponent),
  },
];
export { MODULARITY_ROUTES } from './lib/routes/modularity.routes';

Update libs/libs-structural/modularity-lib/src/index.ts to re-export routes:

export { MODULARITY_ROUTES as ModularityRoutes } from '@libs-structural/vitto-studio';

Run: npx nx build vitto-studio --skip-nx-cache Expected: Build succeeds.

git add libs/libs-structural/vitto-studio/src/lib/components/ libs/libs-structural/vitto-studio/src/lib/routes/ libs/libs-structural/vitto-studio/src/index.ts libs/libs-structural/modularity-lib/src/index.ts
git commit -m "[S248.1] feat: move workbench UI to vitto-studio, fix signal inputs, wire EntityExtractionService"

Task 3.2: Move dashboard components to vitto-hub

Files:

Copy libs/libs-ai/gograb-ai-lib/src/lib/services/vitto-monitor.service.ts → libs/libs-ai/vitto-hub/src/lib/services/. Update type imports to use local ../types/vitto.types. Create re-export shim in gograb-ai-lib.

Copy both services. Update imports. Create shims.

These are used by vitto-chat generation panels. Move to libs/libs-ai/vitto-hub/src/lib/services/.

Update the consumers in vitto-chat:

Create re-export shims in gograb-ai-lib.

Copy these component directories from libs/libs-ai/gograb-ai-lib/src/lib/components/ to libs/libs-ai/vitto-hub/src/lib/components/dashboards/:

Update all service imports within these components to reference the local services (now in vitto-hub).

Add all dashboard components + services to libs/libs-ai/vitto-hub/src/index.ts.

Update libs/libs-ai/gograb-ai-lib/src/lib/components/index.ts to re-export moved components from @libs-ai/vitto-hub.

npx nx build vitto-hub --skip-nx-cache
npx nx build vitto-chat --skip-nx-cache
npx nx build gograb-ai-lib --skip-nx-cache

Expected: All build.

git add libs/libs-ai/vitto-hub/ libs/libs-ai/gograb-ai-lib/src/lib/services/ libs/libs-ai/gograb-ai-lib/src/lib/components/
git commit -m "[S248.1] refactor: move 22 dashboard components + services to vitto-hub"

Task 3.3: Move PageModuleRenderer + PageContentRenderer to universe-canvas

Files:

These are the only active components from modular-universe. Move them to universe-canvas (where their only consumers live):

In canvas-section.component.ts and runtime-preview.component.ts: change imports from @libs-structural/modular-universe to relative paths.

npx nx build universe-canvas --skip-nx-cache

Expected: Build succeeds.

git add libs/libs-space/universe-canvas/src/lib/components/
git commit -m "[S248.1] refactor: move PageModuleRenderer + PageContentRenderer to universe-canvas"

Phase 4: Create vitto-chat Module Builder Plugin

Impact: New functionality. Vitto becomes a module-building copilot.


Task 4.1: Create the module-builder plugin

Files:

Create libs/libs-ai/vitto-chat/src/lib/plugins/module-builder.plugin.ts:

import { Signal } from '@angular/core';
import { ChatPlugin, QuickAction, SlashCommandDefinition } from './chat-plugin.interface';
import { VittoRichContext } from '../types/chat.types';

export interface ModuleBuilderPluginConfig {
  contextSignal: Signal<VittoRichContext | null>;
  moduleContextSignal?: Signal<any>;
  onEntityExtract?: (entities: string[]) => void;
  onModuleGenerate?: (description: string) => void;
  onModuleValidate?: () => void;
  onModuleExport?: () => void;
}

export function createModuleBuilderPlugin(config: ModuleBuilderPluginConfig): ChatPlugin {
  return {
    id: 'module-builder',
    label: 'Module Builder',
    icon: 'puzzle-piece',

    quickActions: buildQuickActions(config),
    slashCommands: buildSlashCommands(config),

    enrichMessage(msg) {
      const ctx = config.contextSignal();
      if (!ctx || ctx.contextType !== 'modularity') return msg;

      const moduleCtx = config.moduleContextSignal?.();
      if (!moduleCtx) return msg;

      return {
        ...msg,
        metadata: {
          ...msg.metadata,
          moduleBuilder: {
            moduleId: moduleCtx.moduleId,
            moduleName: moduleCtx.moduleName,
            entityCount: moduleCtx.entities?.length ?? 0,
            actionCount: moduleCtx.actions?.length ?? 0,
            embeddableCount: moduleCtx.embeddables?.length ?? 0,
            progress: moduleCtx.progress ?? 0,
            status: moduleCtx.status,
            entities: moduleCtx.entities?.map((e: any) => ({
              name: e.name,
              fieldCount: e.fields?.length ?? 0,
            })),
          },
        },
      };
    },

    onContextChange(ctx: VittoRichContext) {
      // No-op for now — could pre-load module context
    },

    onDrop(data) {
      if (data.type === 'module') {
        return {
          id: `msg-${Date.now()}`,
          role: 'user' as const,
          content: `Analyze this module and suggest improvements: ${data.name || data.id}`,
          timestamp: new Date(),
        };
      }
      if (data.type === 'entity') {
        return {
          id: `msg-${Date.now()}`,
          role: 'user' as const,
          content: `Help me design actions and UI for the ${data.name || data.id} entity`,
          timestamp: new Date(),
        };
      }
      return null;
    },
  };
}

function buildQuickActions(config: ModuleBuilderPluginConfig): QuickAction[] {
  const ctx = config.contextSignal();
  if (!ctx || ctx.contextType !== 'modularity') return [];

  const actions: QuickAction[] = [
    {
      id: 'mb-generate',
      label: 'Build a module',
      icon: 'wand-magic-sparkles',
      message: 'I want to create a new module. Help me define the entities and their relationships.',
    },
    {
      id: 'mb-extract-entities',
      label: 'Extract entities',
      icon: 'diagram-project',
      message: 'Extract entities from this description: ',
    },
  ];

  const moduleCtx = config.moduleContextSignal?.();
  if (moduleCtx) {
    actions.push(
      {
        id: 'mb-validate',
        label: 'Validate module',
        icon: 'check-double',
        handler: () => config.onModuleValidate?.(),
      },
      {
        id: 'mb-export',
        label: 'Export to VFS',
        icon: 'file-export',
        handler: () => config.onModuleExport?.(),
      },
    );
  }

  return actions;
}

function buildSlashCommands(config: ModuleBuilderPluginConfig): SlashCommandDefinition[] {
  return [
    {
      name: 'module',
      description: 'Module builder commands: create, entities, validate, export',
      usage: '/module create <description> | /module entities <text> | /module validate | /module export',
      category: 'generation',
      handler: (args: string) => {
        const [subcommand, ...rest] = args.trim().split(/\s+/);
        const payload = rest.join(' ');

        switch (subcommand) {
          case 'create':
            config.onModuleGenerate?.(payload);
            break;
          case 'entities':
            config.onEntityExtract?.(payload.split(',').map(s => s.trim()));
            break;
          case 'validate':
            config.onModuleValidate?.();
            break;
          case 'export':
            config.onModuleExport?.();
            break;
        }
      },
    },
  ];
}

Add to libs/libs-ai/vitto-chat/src/lib/plugins/plugin-presets.ts inside createStandardPluginSet():

// After generation plugin, before teach plugin:
if (!isConsumer) {
  plugins.push(
    createModuleBuilderPlugin({
      contextSignal: config.contextSignal,
      moduleContextSignal: config.moduleContextSignal,
      onEntityExtract: config.onEntityExtract,
      onModuleGenerate: config.onModuleGenerate,
      onModuleValidate: config.onModuleValidate,
      onModuleExport: config.onModuleExport,
    }),
  );
}

Update PluginPresetConfig interface to include the new optional fields.

Add to the welcome content registry initialization (wherever the host app registers contexts):

welcomeRegistry.register({
  contextType: 'modularity',
  subtitle: 'I can help you build modules — define entities, wire actions, and generate production code.',
  starters: [
    { label: 'Build a module', message: 'I want to create a new module for managing...', icon: 'wand-magic-sparkles' },
    { label: 'Extract entities', message: 'Extract entities from this: ', icon: 'diagram-project' },
    { label: 'Validate current module', message: '/module validate', icon: 'check-double' },
  ],
  capabilities: [
    { title: 'Entity Design', description: 'Extract entities from natural language and auto-generate CRUD', icon: 'database' },
    { title: 'Code Generation', description: 'Generate real Angular components using SDK (ggs-*) components', icon: 'code' },
    { title: 'Flow Builder', description: 'Design action flows with visual canvas and test execution', icon: 'route' },
    { title: 'Validation', description: 'Quality gate with weighted scoring before export', icon: 'shield-check' },
  ],
});

Add to libs/libs-ai/vitto-chat/src/index.ts:

export { createModuleBuilderPlugin, ModuleBuilderPluginConfig } from './lib/plugins/module-builder.plugin';

Run: npx nx build vitto-chat --skip-nx-cache Expected: Build succeeds.

git add libs/libs-ai/vitto-chat/src/lib/plugins/module-builder.plugin.ts libs/libs-ai/vitto-chat/src/lib/plugins/plugin-presets.ts libs/libs-ai/vitto-chat/src/index.ts
git commit -m "[S248.1] feat: create module-builder plugin for vitto-chat — entity extraction, validation, VFS export"

Phase 5: Update All Imports & Delete Dead Libs

Impact: Remove all re-export shims. Update every import to point directly to the new locations. Delete the old libraries entirely.


Task 5.1: Update all imports monorepo-wide

Search and replace across the monorepo:

# Find all files importing from @libs-structural/modularity-lib
grep -r "from '@libs-structural/modularity-lib'" --include='*.ts' -l

For each file: update import path from @libs-structural/modularity-lib → @libs-structural/vitto-studio.

Key files:

grep -r "from '@libs-ai/module-generator'" --include='*.ts' -l

For each file: update import path to @libs-structural/vitto-studio (types only).

grep -r "WorkflowStateService.*from '@libs-ai/gograb-ai-lib'" --include='*.ts' -l
grep -r "VittoMonitorService.*from '@libs-ai/gograb-ai-lib'" --include='*.ts' -l
grep -r "ImageGenerationService.*from '@libs-ai/gograb-ai-lib'" --include='*.ts' -l
grep -r "VideoGenerationService.*from '@libs-ai/gograb-ai-lib'" --include='*.ts' -l

Update:

Update lazy-load imports in:

grep -r "from '@libs-structural/modular-universe'" --include='*.ts' -l

Update PageModuleRenderer/PageContentRenderer consumers to import from @libs-space/universe-canvas.

npx nx run-many --target=build --projects=vitto-studio,vitto-chat,vitto-hub,gograb-space,gograb-dev,gograb-admin,gograb-ai --skip-nx-cache

Expected: All build successfully with zero @libs-structural/modularity-lib or @libs-ai/module-generator references remaining.

git add libs/ apps/ APIs/
git commit -m "[S248.1] refactor: update all imports from old libs to new locations"

Task 5.2: Delete dead libraries

rm -rf libs/libs-structural/modularity-lib/
rm -rf libs/libs-ai/module-generator/
rm -rf libs/libs-structural/modular-universe/
rm -rf libs/libs-structural/module-browser-lib/
rm -rf libs/libs-ai/cursor-agents/
rm -rf libs/libs-ai/multi-agent-dashboard/

Delete these dead services from libs/libs-ai/gograb-ai-lib/src/lib/services/:

Delete their exports from the services barrel.

Library paths are in tsconfig.base.json DIRECTLY (NOT in config/tsconfig-paths/ — that directory is only for @module/* paths). Remove these entries from tsconfig.base.json compilerOptions.paths:

Note: @libs-ai/multi-agent-dashboard does NOT have a tsconfig entry (confirmed by grep).

Run nx reset to clear the project graph cache.

Verify:

npx nx show projects | grep -E 'modularity-lib|module-generator|modular-universe|module-browser-lib|cursor-agents|multi-agent-dashboard'

Expected: No output (all removed).

npx nx run-many --target=build --all --skip-nx-cache 2>&1 | tail -20

Expected: All projects build. Zero references to deleted libs.

git add -A
git commit -m "[S248.1] chore: delete 6 deprecated libraries — modularity-lib, module-generator, modular-universe, module-browser-lib, cursor-agents, multi-agent-dashboard"

Phase 6: E2E Tests

Impact: Validates the entire migration works end-to-end.


Task 6.1: Module workbench E2E test

Files:

import { TestBed } from '@angular/core/testing';
import { ModuleContextService } from '../../../services/module-context.service';
import { createEmptyModuleContext, createEntityWithDefaults } from '../../../models';

describe('Module Workbench E2E: Entity Cascade', () => {
  let contextService: ModuleContextService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [ModuleContextService],
    });
    contextService = TestBed.inject(ModuleContextService);
  });

  it('should cascade entity addition to state, actions, embeddables, permissions, routes', () => {
    const context = createEmptyModuleContext('Tasks', 'crud');
    contextService.setContext(context);

    const entity = createEntityWithDefaults('Task');
    contextService.addEntity(entity);

    const updated = contextService.context();
    expect(updated!.entities).toHaveLength(1);
    expect(updated!.stateDefinitions).toHaveLength(1); // auto-created
    expect(updated!.actions.length).toBeGreaterThanOrEqual(5); // 5 CRUD actions
    expect(updated!.embeddables).toHaveLength(2); // list + card
    expect(updated!.permissions.length).toBeGreaterThanOrEqual(4); // read/create/update/delete
    expect(updated!.routes.length).toBeGreaterThanOrEqual(2); // list + detail
  });

  it('should cascade entity removal — clean up all associated resources', () => {
    const context = createEmptyModuleContext('Tasks', 'crud');
    contextService.setContext(context);

    const entity = createEntityWithDefaults('Task');
    contextService.addEntity(entity);
    contextService.removeEntity(entity.id);

    const updated = contextService.context();
    expect(updated!.entities).toHaveLength(0);
    expect(updated!.stateDefinitions).toHaveLength(0);
    expect(updated!.actions).toHaveLength(0);
    expect(updated!.embeddables).toHaveLength(0);
    expect(updated!.permissions).toHaveLength(0);
  });
});

Run: npx vitest run libs/libs-structural/vitto-studio/src/lib/components/module-workbench/__tests__/ --reporter=verbose Expected: ALL PASS

git add libs/libs-structural/vitto-studio/src/lib/components/module-workbench/__tests__/
git commit -m "[S248.1] test: add E2E tests for module workbench entity cascade"

Task 6.2: Module export E2E test

Files:

describe('ModuleExportService E2E: Full Pipeline', () => {
  it('should generate a complete, valid module from ModuleContext → files', () => {
    // Create a realistic module context
    const context = createEmptyModuleContext('ProjectManagement', 'crud');

    // Add two entities
    const project = createEntityWithDefaults('Project');
    project.fields.push(
      { id: 'f1', name: 'title', type: 'string', required: true, uiHints: { showInList: true } },
      { id: 'f2', name: 'status', type: 'enum', required: true, enumValues: ['active', 'completed', 'archived'] },
      { id: 'f3', name: 'dueDate', type: 'date', required: false },
    );
    context.entities.push(project);

    const task = createEntityWithDefaults('Task');
    task.fields.push(
      { id: 'f4', name: 'title', type: 'string', required: true },
      { id: 'f5', name: 'projectId', type: 'reference', referenceType: 'belongs-to', referenceEntityId: project.id },
      { id: 'f6', name: 'priority', type: 'number', required: true },
    );
    context.entities.push(task);

    // Generate CRUD actions
    context.actions = [
      ...createCrudActionsForEntity(project.id, 'Project', 'Projects', project.fields),
      ...createCrudActionsForEntity(task.id, 'Task', 'Tasks', task.fields),
    ];

    // Generate files
    const files = service.generateModuleFiles(context);

    // Verify file count: 2 models + 2 services + 2×3 components (ts+html+scss) + routes + index
    expect(files.length).toBeGreaterThanOrEqual(16);

    // Verify no stubs or TODOs
    for (const file of files) {
      expect(file.content).not.toContain('// TODO');
      expect(file.content).not.toContain('placeholder');
      expect(file.content).not.toContain('not implemented');
      expect(file.content.length).toBeGreaterThan(10);
    }

    // Verify services use real HttpClient
    const projectService = files.find(f => f.path.includes('project.service.ts'));
    expect(projectService!.content).toContain('HttpClient');
    expect(projectService!.content).toContain('API_BASE_URL');
    expect(projectService!.content).toContain('Observable');

    // Verify components use SDK
    const listTemplate = files.find(f => f.path.includes('project-list.component.html'));
    expect(listTemplate!.content).toContain('ggs-table');

    // Verify routes file
    const routesFile = files.find(f => f.path.includes('.routes.ts'));
    expect(routesFile!.content).toContain('loadComponent');
    expect(routesFile!.content).toContain('Routes');
  });
});

Run: npx vitest run libs/libs-structural/vitto-studio/src/lib/services/__tests__/module-export-e2e.spec.ts --reporter=verbose Expected: ALL PASS

git add libs/libs-structural/vitto-studio/src/lib/services/__tests__/module-export-e2e.spec.ts
git commit -m "[S248.1] test: add E2E test for full module export pipeline — no stubs verified"

Task 6.3: Module builder plugin E2E test

Files:

describe('Module Builder Plugin', () => {
  it('should produce quick actions only in modularity context', () => {
    const contextSignal = signal<VittoRichContext | null>(null);
    const plugin = createModuleBuilderPlugin({ contextSignal });

    // No context → no quick actions
    expect(plugin.quickActions).toHaveLength(0);

    // Set modularity context
    contextSignal.set({ contextType: 'modularity' as any });
    // Re-create to get new quick actions (they're computed at creation time)
    const plugin2 = createModuleBuilderPlugin({ contextSignal });
    expect(plugin2.quickActions!.length).toBeGreaterThan(0);
    expect(plugin2.quickActions!.some(a => a.id === 'mb-generate')).toBe(true);
  });

  it('should enrich messages with module context when in workbench', () => {
    const contextSignal = signal<VittoRichContext | null>({ contextType: 'modularity' as any });
    const moduleContextSignal = signal({
      moduleId: 'test-1',
      moduleName: 'Tasks',
      entities: [{ name: 'Task', fields: [] }],
      actions: [],
      embeddables: [],
      progress: 40,
      status: 'building',
    });

    const plugin = createModuleBuilderPlugin({ contextSignal, moduleContextSignal });
    const enriched = plugin.enrichMessage!({
      id: 'msg-1',
      role: 'user',
      content: 'Help me',
      timestamp: new Date(),
    });

    expect(enriched.metadata?.moduleBuilder).toBeDefined();
    expect(enriched.metadata.moduleBuilder.moduleName).toBe('Tasks');
    expect(enriched.metadata.moduleBuilder.entityCount).toBe(1);
    expect(enriched.metadata.moduleBuilder.progress).toBe(40);
  });

  it('should handle module drop with context message', () => {
    const contextSignal = signal<VittoRichContext | null>(null);
    const plugin = createModuleBuilderPlugin({ contextSignal });

    const result = plugin.onDrop!({ type: 'module', id: 'mod-1', name: 'Users' } as any);
    expect(result).not.toBeNull();
    expect(result!.content).toContain('Users');
  });

  it('should provide /module slash command', () => {
    const contextSignal = signal<VittoRichContext | null>(null);
    const onModuleGenerate = vi.fn();
    const plugin = createModuleBuilderPlugin({ contextSignal, onModuleGenerate });

    expect(plugin.slashCommands).toHaveLength(1);
    expect(plugin.slashCommands![0].name).toBe('module');

    plugin.slashCommands![0].handler('create A task management system');
    expect(onModuleGenerate).toHaveBeenCalledWith('A task management system');
  });
});

Run: npx vitest run libs/libs-ai/vitto-chat/src/lib/plugins/__tests__/module-builder.plugin.spec.ts --reporter=verbose Expected: ALL PASS

git add libs/libs-ai/vitto-chat/src/lib/plugins/__tests__/
git commit -m "[S248.1] test: add E2E tests for module-builder plugin"

Task 6.4: Adapter + validation integration test

Files:

describe('PhaseResultToModuleContextAdapter → ModuleValidationService E2E', () => {
  it('should produce a validatable ModuleContext from Vitto phase output', () => {
    const adapter = new PhaseResultToModuleContextAdapter();
    const validator = new ModuleValidationService();

    // Simulate Vitto 6-phase output
    const phaseResult = {
      modules: [{
        id: 'gen-1',
        name: 'UserManagement',
        description: 'Manage users and roles',
        category: 'system',
        entities: [{
          name: 'User',
          fields: [
            { name: 'id', type: 'String', required: true },
            { name: 'email', type: 'String', required: true },
            { name: 'name', type: 'String', required: true },
            { name: 'role', type: 'String', required: false },
          ],
        }],
        actions: [],
        generatedFiles: [],
      }],
      metadata: { generationId: 'test', timestamp: Date.now(), totalFiles: 0 },
    };

    const moduleContext = adapter.adapt(phaseResult);

    // Should have cascaded everything
    expect(moduleContext.entities.length).toBe(1);
    expect(moduleContext.stateDefinitions.length).toBe(1);
    expect(moduleContext.actions.length).toBeGreaterThanOrEqual(5);
    expect(moduleContext.embeddables.length).toBe(2);

    // Should pass basic validation
    const result = validator.validate(moduleContext);
    expect(result.score).toBeGreaterThan(40);
    expect(result.exportReadiness.canExportAsManifest).toBe(true);
  });
});

Run: npx vitest run libs/libs-structural/vitto-studio/src/lib/adapters/__tests__/ --reporter=verbose Expected: PASS

git add libs/libs-structural/vitto-studio/src/lib/adapters/__tests__/
git commit -m "[S248.1] test: add adapter→validation integration E2E test"

Phase 7: NX Libs & Component Cleanup

Impact: Ensures every NX lib compiles, all deprecated exports are removed, all project.json configs are correct, and the workspace is clean.


Task 7.1: Verify all NX projects build

npx nx run-many --target=build --all --skip-nx-cache 2>&1 | tee /tmp/build-output.txt
grep -E 'ERROR|FAIL|Cannot find' /tmp/build-output.txt

For each error: update the import path. The most likely issues are:

git add -A
git commit -m "[S248.1] fix: resolve remaining build errors from library migration"

Task 7.2: Clean up gograb-ai-lib — remove deprecated exports

Read libs/libs-ai/gograb-ai-lib/src/lib/services/index.ts. For each export, check:

Delete all lines in gograb-ai-lib that say // DEPRECATED: ... moved to .... By now, all consumers should import directly from the new location.

Same process for libs/libs-ai/gograb-ai-lib/src/lib/components/index.ts. Remove re-export shims for moved dashboard components.

Delete these unused direct LLM services (all bypassed by Vitto):

Only delete if grep -r confirms no active consumers (exclude demos-lib which is also deprecated).

npx nx build gograb-ai-lib --skip-nx-cache

Expected: Build succeeds with a significantly smaller footprint.

git add libs/libs-ai/gograb-ai-lib/
git commit -m "[S248.1] chore: clean deprecated exports and unused services from gograb-ai-lib"

Task 7.3: Update project.json implicit dependencies

Add implicit dependencies for the models and services it now owns:

{
  "implicitDependencies": ["core-angular", "core-models", "storage-system", "log-system", "platform-preview"]
}

Add implicit dependencies for dashboard components:

{
  "implicitDependencies": ["core-angular", "core-models", "vitto-core"]
}

Add implicit dependency on vitto-studio (for module-builder plugin types):

{
  "implicitDependencies": ["core-angular", "vitto-core", "vitto-studio"]
}
grep -r 'modularity-lib\|module-generator\|modular-universe\|module-browser-lib\|cursor-agents\|multi-agent-dashboard' --include='project.json' -l

For each file: remove the deleted lib from implicitDependencies.

nx reset
npx nx graph --file=/tmp/graph.html

Open /tmp/graph.html to verify no broken edges.

git add -A
git commit -m "[S248.1] chore: update project.json dependencies for library migration"

Task 7.4: Final verification — lint, build, test

npx nx run-many --target=lint --projects=vitto-studio,vitto-chat,vitto-hub --skip-nx-cache

Fix any lint errors.

npx nx run-many --target=test --projects=vitto-studio,vitto-chat,vitto-hub --skip-nx-cache

All tests must pass.

npx nx run-many --target=build --all --skip-nx-cache

All projects must build.

grep -r "@libs-structural/modularity-lib" --include='*.ts' -l | grep -v node_modules | grep -v dist
grep -r "@libs-ai/module-generator" --include='*.ts' -l | grep -v node_modules | grep -v dist
grep -r "@libs-structural/modular-universe" --include='*.ts' -l | grep -v node_modules | grep -v dist
grep -r "@libs-structural/module-browser-lib" --include='*.ts' -l | grep -v node_modules | grep -v dist

Expected: No output for any of these.

git add -A
git commit -m "[S248.1] chore: final verification — all libs build, test, lint clean"

Rollback Strategy

Phase Rollback
Phase 1 (models) Remove vitto-studio/models/, restore modularity-lib models barrel
Phase 2 (services) Remove vitto-studio services, restore modularity-lib services barrel
Phase 3 (components) Remove vitto-studio components, restore modularity-lib + gograb-ai-lib components
Phase 4 (plugin) Remove module-builder.plugin.ts, revert plugin-presets.ts
Phase 5 (imports) Restore old import paths (git revert the commit)
Phase 5 (deletions) git checkout HEAD~1 -- libs/libs-structural/modularity-lib/ libs/libs-ai/module-generator/ ...

Each phase can be reverted independently because re-export shims are kept until Phase 5.


Success Criteria

Metric Verification
Zero references to deleted libs grep -r "@libs-structural/modularity-lib" --include='*.ts' returns empty
All NX projects build npx nx run-many --target=build --all succeeds
All tests pass npx nx run-many --target=test --projects=vitto-studio,vitto-chat,vitto-hub succeeds
Module workbench accessible at /modularity/workbench Navigate in browser, verify step components render
Entity cascade works Add entity in workbench, verify state+actions+embeddables auto-created
Code generation produces real code Export module, verify no stubs/TODOs in generated files
vitto-chat shows module-builder actions in modularity context Open chat in workbench, verify quick actions appear
Dashboard routes work in gograb-dev Navigate to /intelligence/*, verify components render
gograb-ai-lib is significantly smaller Compare line count before/after