2026 03 23 Proactive Visual Intelligence V1

Documentation for Plans

Proactive Visual Intelligence V1 — Implementation Plan (Delta)

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: Complete the remaining ~15% of PVI V1: wire structural LLM apply, automate the SDK manifest pipeline, fix the conflict ID bug, and add test coverage for all existing PVI code.

Architecture: ~85% of PVI V1 was already implemented in Session 263. This plan covers ONLY the remaining delta. It does NOT re-implement anything that already exists.

Tech Stack: Angular 20+ (standalone, OnPush, signals), RxJS, Vitest, NX, TypeScript compiler API

Spec: docs/superpowers/specs/2026-03-23-proactive-visual-intelligence-design.md


What Already Exists (Do NOT Re-Implement)

Component File Status
PVI library + all interfaces libs/libs-ai/proactive-visual-intelligence/src/lib/models/pvi.types.ts (139 lines) Complete
SDK manifest (10 components) libs/libs-ai/proactive-visual-intelligence/src/lib/data/sdk-manifest.json (772 lines) Complete (hardcoded)
Suggestion rule engine (15 rules) libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-rule-engine.ts (274 lines) Complete
Suggestion merge engine libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-merge-engine.ts (81 lines) Complete
Component tagging in enrichment bundler-filesystem.service.ts:155-158 via applyStudioTagging() Complete
Bridge client (PVI messages) preview-bridge-client.ts (PVI_ELEMENT_CONTEXT, CSS inject, dots) Complete
Bridge service (pviElementContext$) preview-bridge.service.ts:122-124 Complete
PviService (state + orchestration) vitto-studio/src/lib/services/pvi.service.ts (103 lines) Complete
PviSnapshotService (undo, max 50) vitto-studio/src/lib/services/pvi-snapshot.service.ts (77 lines) Complete
PviApplyService (style apply works) vitto-studio/src/lib/services/pvi-apply.service.ts (188 lines) Partial — structural TODO
PviToolbar + SuggestionPanel vitto-studio/src/lib/components/pvi/ Complete
Studio-dock-shell integration Wired at lines 298-301 Complete

What Remains (This Plan)

# Task Blocking? Effort
1 Fix conflict ID bug in suggestion-rule-engine.ts Yes 30 min
2 Wire structural LLM apply in pvi-apply.service.ts Yes 2-3 hours
3 Create 15 .intelligence.ts files + generator script No (improves quality) 4-5 hours
4 Add test coverage for all PVI code No (quality gate) 3-4 hours

Task 1: Fix Conflict ID Bug in Suggestion Rule Engine

Context: The rule engine generates IDs via nextId() which produces 'pvi-rule-1', 'pvi-rule-2', etc. But conflicts arrays reference symbolic names like 'pvi-rule-card-grid' and 'pvi-rule-table' which will never match the generated IDs. This means "Apply All" conflict detection (V2) will never skip conflicting suggestions.

Files:

Read libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-rule-engine.ts. Identify all rules and their conflict references. Note the nextId() function and its output format.

Replace the nextId() counter with stable rule IDs. Each rule should produce suggestions with deterministic IDs based on the rule name:

// Before (broken):
id: nextId(),  // produces 'pvi-rule-1', 'pvi-rule-2', etc.
conflicts: ['pvi-rule-card-grid'],  // will never match 'pvi-rule-2'

// After (fixed):
id: 'pvi-rule-table',
conflicts: ['pvi-rule-card-grid'],  // matches the card-grid rule's ID

Go through every rule and assign a stable, descriptive ID string. Ensure all conflicts arrays reference IDs that actually exist on other rules.

Search for pvi-rule- across the codebase. Check if PviService, PviApplyService, or any component uses the generated IDs for anything other than conflict matching.

git add libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-rule-engine.ts
git commit -m "[S257.X] fix(pvi): use stable symbolic IDs for conflict detection in rule engine"

Task 2: Wire Structural LLM Apply in PviApplyService

Context: pvi-apply.service.ts line ~100 has a TODO comment where structural changes should call the LLM to generate code. Currently it creates a snapshot then returns { success: true } without applying any actual change. We need to wire this to StudioVittoBridgeService.sendTask() to send the component's current code + the suggestion prompt to the LLM, then write the returned files to VFS.

Files:

Read libs/libs-structural/vitto-studio/src/lib/services/pvi-apply.service.ts fully. Find the structural apply TODO at line ~100. Understand the existing style apply flow as a pattern to follow.

Read libs/libs-structural/vitto-studio/src/lib/services/studio-vitto-bridge.service.ts. Find the sendTask(prompt, context) method signature and return type. It returns Observable<VittoTaskResult>.

Read libs/libs-structural/vitto-studio/src/lib/services/visual-editor.service.ts. The buildEditingContext() method at line 134 shows how to read component sources (TS, HTML, SCSS) from VFS. We need similar logic to build the LLM context.

Replace the TODO stub in pvi-apply.service.ts with:

applyStructural(suggestion: Suggestion, studioId: string): Observable<{ success: boolean; error?: string }> {
  const elementContext = this.pviService.elementContext();
  if (!elementContext || !suggestion.structuralChange) {
    return of({ success: false, error: 'No element context or structural change' });
  }

  // 1. Create snapshot before change
  const affectedFiles = suggestion.structuralChange.affectedFiles;
  this.snapshotService.createSnapshot(
    suggestion.label,
    this.getFileContents(affectedFiles),
  );

  // 2. Build LLM context from component sources
  const componentName = elementContext.componentName;
  const visualContext = this.visualEditor.editingContext();
  const context = {
    componentName,
    moduleSlug: visualContext?.moduleSlug ?? '',
    currentFiles: {
      ts: visualContext?.classSource ?? '',
      html: visualContext?.templateSource ?? '',
      scss: visualContext?.stylesSource ?? '',
    },
    sdkManifest: this.getRelevantManifestEntries(suggestion),
    instruction: suggestion.structuralChange.prompt,
  };

  // 3. Call LLM via StudioVittoBridgeService
  return this.bridgeService.sendTask(
    `Apply this modification to the ${componentName} component:\n\n${suggestion.structuralChange.prompt}\n\nCurrent component files are provided in context. Return ONLY the modified files as JSON array: [{ "path": "...", "content": "..." }]. The code MUST compile and use SDK components from the manifest.`,
    context,
  ).pipe(
    map(result => {
      // 4. Parse returned files and write to VFS
      const files = this.parseReturnedFiles(result);
      if (files.length === 0) {
        this.snapshotService.popForUndo();
        return { success: false, error: 'LLM returned no valid files' };
      }
      for (const file of files) {
        this.vfsService.updateFileContent(file.path, file.content);
      }
      // VFS change triggers automatic recompile via preview pipeline
      return { success: true };
    }),
    catchError(err => {
      // 5. Auto-rollback on failure
      this.snapshotService.popForUndo();
      return of({ success: false, error: err?.message ?? 'Structural apply failed' });
    }),
  );
}

private parseReturnedFiles(result: VittoTaskResult): Array<{ path: string; content: string }> {
  // Extract file array from LLM response
  // Handle: JSON array directly, or nested in result.content/result.output
  try {
    const data = typeof result === 'string' ? JSON.parse(result) : result;
    if (Array.isArray(data)) return data.filter(f => f.path && f.content);
    if (data.files && Array.isArray(data.files)) return data.files.filter(f => f.path && f.content);
    if (data.content) {
      const parsed = JSON.parse(data.content);
      if (Array.isArray(parsed)) return parsed.filter(f => f.path && f.content);
    }
    return [];
  } catch {
    return [];
  }
}

private getRelevantManifestEntries(suggestion: Suggestion): Record<string, unknown> {
  // Return only the manifest entries relevant to this suggestion
  const manifest = this.pviService.manifest;
  if (!manifest) return {};
  // Include components mentioned in the suggestion label
  const relevant: Record<string, unknown> = {};
  for (const [selector, intel] of Object.entries(manifest.components)) {
    if (suggestion.label.toLowerCase().includes(selector.replace('ggs-', ''))) {
      relevant[selector] = intel;
    }
  }
  return relevant;
}

Add to the PviApplyService constructor/inject calls:

Ensure imports use the correct path aliases.

The studio-dock-shell or live-preview component should show a shimmer overlay when a structural apply is in progress. Add a structuralApplying signal to PviApplyService:

readonly structuralApplying = signal<string | null>(null); // studioId being applied, or null

Set it before the LLM call, clear it on success or failure. The toolbar/preview can use this to show a loading indicator.

  1. Start dev servers
  2. Generate a platform
  3. Click an element → toolbar appears
  4. Click a structural suggestion (e.g., "Switch to ggs-table")
  5. Should see shimmer → LLM generates code → preview recompiles with the change
  6. If it fails, should see error message and original state preserved
git add libs/libs-structural/vitto-studio/src/lib/services/pvi-apply.service.ts
git commit -m "[S257.X] feat(pvi): wire structural LLM apply via StudioVittoBridgeService"

Task 3: SDK Manifest Pipeline — 15 .intelligence.ts Files + Generator Script

Context: The SDK manifest at libs/libs-ai/proactive-visual-intelligence/src/lib/data/sdk-manifest.json is currently a hand-created 42KB JSON file covering 10 components. We need to: (1) create .intelligence.ts source files in the ui-framework component directories, (2) build a generator script that compiles them into the manifest, (3) auto-generate the prompt partial. This ensures the manifest stays in sync with the actual components.

Files:

Read the following to get accurate inputs/outputs for the intelligence files:

For each component, create a file that exports const GGS_{NAME}_INTELLIGENCE: ComponentIntelligence. Import ComponentIntelligence from @libs-ai/proactive-visual-intelligence. Each file must include: selector, category, purpose, whenToUse, whenNotToUse, inputs (from actual component), outputs, variants, suggestions (all 6 categories), upgradesFrom, composedWith, themeTokens, examples.

Components: ggs-button, ggs-table, ggs-badge, ggs-input, ggs-select, ggs-textarea, ggs-card, ggs-crud-list-shell, ggs-crud-form-shell, ggs-crud-detail-shell, ggs-search-input, ggs-filter-panel, ggs-pagination, ggs-empty-state, ggs-loading-spinner.

Use the gold-standard JSDoc from ggs-sidebar.component.ts:5-21 as a quality reference for descriptions.

Create tools/scripts/generate-sdk-manifest.ts that:

  1. Scans libs/core/ui-framework/src/lib/components/*/ for *.intelligence.ts files
  2. For each file, uses ts.transpile() to convert to CommonJS, writes to a temp file, then require() it to extract the exported object. (Do NOT use eval() or new Function())
  3. Aggregates all component intelligence into a single SdkManifest JSON
  4. Writes libs/libs-ai/proactive-visual-intelligence/src/lib/data/sdk-manifest.json (replaces existing)
  5. Generates APIs/backend-vitto-api/src/prompts/module-gen/partials/ui-framework-components.partial.md
  6. Cleans up temp files

Add generate-sdk-manifest target to apps/gograb-space/project.json:

"generate-sdk-manifest": {
  "executor": "nx:run-commands",
  "options": {
    "command": "npx tsx tools/scripts/generate-sdk-manifest.ts"
  }
}

Run: npx tsx tools/scripts/generate-sdk-manifest.ts Expected: sdk-manifest.json regenerated with 15 components (up from 10). Prompt partial auto-generated with all 15 components.

Diff the old sdk-manifest.json (10 components) with the new one (15 components). Ensure the 10 existing components are preserved (data may be richer if the intelligence files add more detail).

git add libs/core/ui-framework/src/lib/components/*/*.intelligence.ts
git add tools/scripts/generate-sdk-manifest.ts
git add libs/libs-ai/proactive-visual-intelligence/src/lib/data/sdk-manifest.json
git add APIs/backend-vitto-api/src/prompts/module-gen/partials/ui-framework-components.partial.md
git add apps/gograb-space/project.json
git commit -m "[S257.X] feat(pvi): SDK manifest pipeline — 15 intelligence files + generator + auto-generated prompt partial"

Task 4: Add Test Coverage for All PVI Code

Context: All PVI services and engines (7 files, ~950 lines) have zero test coverage. This is a quality gate before V2 work begins.

Files to test:

Create libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-rule-engine.spec.ts (Vitest):

Test scenarios:

  1. List with hasNgFor but no ggs-table → suggests ggs-table (layout, high impact)
  2. Element with <button> in missingGgsComponents → suggests ggs-button (component)
  3. Element with <span> in missingGgsComponents → suggests ggs-badge (component)
  4. Element with boxShadow: 'none' → suggests shadow (style, includes StylePatch)
  5. List without ggs-empty-state → suggests empty state (ux)
  6. Fully-equipped component → few/no suggestions
  7. Suggestions sorted by impact (high first)
  8. Style suggestions include valid StylePatch with cssPreview and scssPatch
  9. Structural suggestions include StructuralChange with prompt
  10. Conflict IDs match between conflicting rules (after Task 1 fix)

Create libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-merge-engine.spec.ts (Vitest):

Test scenarios:

  1. LLM suggestion matching rule → LLM version wins
  2. LLM-only suggestion → added
  3. Rule-only suggestion → kept
  4. Fuzzy matching works (similar labels matched)
  5. Output sorted by impact then weights
  6. Empty inputs handled gracefully

Create libs/libs-structural/vitto-studio/src/lib/services/pvi-snapshot.service.spec.ts (Vitest or Jasmine):

Test scenarios:

  1. createSnapshot stores file contents
  2. popForUndo returns most recent snapshot
  3. Stack respects max 50 (FIFO eviction)
  4. canUndo signal is false when empty, true otherwise
  5. clear() resets everything

Create libs/libs-structural/vitto-studio/src/lib/services/pvi-apply.service.spec.ts:

Test scenarios for style apply:

  1. Calls bridge.sendPviMessage with PVI_INJECT_CSS
  2. Patches SCSS content correctly
  3. Creates snapshot before apply

Test scenarios for structural apply (after Task 2):

  1. Calls bridgeService.sendTask with correct prompt
  2. On success, writes files to VFS
  3. On error, rolls back from snapshot
  4. Sets/clears structuralApplying signal

Create libs/libs-structural/vitto-studio/src/lib/services/pvi.service.spec.ts:

Test scenarios:

  1. onElementContext runs rule engine and populates suggestions signal
  2. selectCategory filters suggestions by category
  3. clearSelection resets element context
  4. isToolbarVisible computed works correctly
npx vitest run libs/libs-ai/proactive-visual-intelligence/
npx nx test vitto-studio -- --testPathPattern=pvi

Expected: All tests PASS

git add libs/libs-ai/proactive-visual-intelligence/src/lib/engines/*.spec.ts
git add libs/libs-structural/vitto-studio/src/lib/services/pvi*.spec.ts
git commit -m "[S257.X] test(pvi): add test coverage for all PVI engines and services"

Post-Implementation Checklist