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:
Modify:
libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-rule-engine.tsStep 1: Read the current rule engine
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.
- Step 2: Change ID generation to use stable symbolic names
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.
- Step 3: Verify no other code depends on the old ID format
Search for pvi-rule- across the codebase. Check if PviService, PviApplyService, or any component uses the generated IDs for anything other than conflict matching.
- Step 4: Commit
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:
Modify:
libs/libs-structural/vitto-studio/src/lib/services/pvi-apply.service.tsRead:
libs/libs-structural/vitto-studio/src/lib/services/studio-vitto-bridge.service.ts(forsendTaskAPI)Read:
libs/libs-structural/vitto-studio/src/lib/services/vfs.service.ts(for file update API)Read:
libs/libs-structural/vitto-studio/src/lib/services/pvi.service.ts(for context)Step 1: Read the current PviApplyService
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.
- Step 2: Read StudioVittoBridgeService.sendTask API
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>.
- Step 3: Read VisualEditorService for component source access
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.
- Step 4: Implement structural apply
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;
}
- Step 5: Inject required dependencies
Add to the PviApplyService constructor/inject calls:
StudioVittoBridgeService(forsendTask)VisualEditorService(for component source context)VfsService(forupdateFileContent)
Ensure imports use the correct path aliases.
- Step 6: Wire the shimmer state
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.
- Step 7: Test manually
- Start dev servers
- Generate a platform
- Click an element → toolbar appears
- Click a structural suggestion (e.g., "Switch to ggs-table")
- Should see shimmer → LLM generates code → preview recompiles with the change
- If it fails, should see error message and original state preserved
- Step 8: Commit
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:
Create: 15
.intelligence.tsfiles inlibs/core/ui-framework/src/lib/components/Create:
tools/scripts/generate-sdk-manifest.tsModify:
apps/gograb-space/project.json(add NX target)Output:
libs/libs-ai/proactive-visual-intelligence/src/lib/data/sdk-manifest.json(regenerated)Output:
APIs/backend-vitto-api/src/prompts/module-gen/partials/ui-framework-components.partial.md(auto-generated)Step 1: Read 3-4 component .ts files to understand their actual APIs
Read the following to get accurate inputs/outputs for the intelligence files:
libs/core/ui-framework/src/lib/components/ggs-button/ggs-button.component.ts(56 lines)libs/core/ui-framework/src/lib/components/ggs-table/ggs-table.component.ts(244 lines)libs/core/ui-framework/src/lib/components/ggs-crud-list-shell/ggs-crud-list-shell.component.tslibs/core/ui-framework/src/lib/components/ggs-filter-panel/ggs-filter-panel.component.ts(310 lines)Step 2: Create the 15 .intelligence.ts 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.
- Step 3: Create the manifest generator script
Create tools/scripts/generate-sdk-manifest.ts that:
- Scans
libs/core/ui-framework/src/lib/components/*/for*.intelligence.tsfiles - For each file, uses
ts.transpile()to convert to CommonJS, writes to a temp file, thenrequire()it to extract the exported object. (Do NOT useeval()ornew Function()) - Aggregates all component intelligence into a single
SdkManifestJSON - Writes
libs/libs-ai/proactive-visual-intelligence/src/lib/data/sdk-manifest.json(replaces existing) - Generates
APIs/backend-vitto-api/src/prompts/module-gen/partials/ui-framework-components.partial.md - Cleans up temp files
- Step 4: Add NX target
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"
}
}
- Step 5: Run the generator, verify output
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.
- Step 6: Verify no regressions
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).
- Step 7: Commit
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:
libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-rule-engine.ts(274 lines)libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-merge-engine.ts(81 lines)libs/libs-structural/vitto-studio/src/lib/services/pvi.service.ts(103 lines)libs/libs-structural/vitto-studio/src/lib/services/pvi-apply.service.ts(188 lines)libs/libs-structural/vitto-studio/src/lib/services/pvi-snapshot.service.ts(77 lines)Step 1: Write tests for suggestion-rule-engine.ts
Create libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-rule-engine.spec.ts (Vitest):
Test scenarios:
- List with
hasNgForbut no ggs-table → suggests ggs-table (layout, high impact) - Element with
<button>in missingGgsComponents → suggests ggs-button (component) - Element with
<span>in missingGgsComponents → suggests ggs-badge (component) - Element with
boxShadow: 'none'→ suggests shadow (style, includes StylePatch) - List without ggs-empty-state → suggests empty state (ux)
- Fully-equipped component → few/no suggestions
- Suggestions sorted by impact (high first)
- Style suggestions include valid StylePatch with cssPreview and scssPatch
- Structural suggestions include StructuralChange with prompt
- Conflict IDs match between conflicting rules (after Task 1 fix)
- Step 2: Write tests for suggestion-merge-engine.ts
Create libs/libs-ai/proactive-visual-intelligence/src/lib/engines/suggestion-merge-engine.spec.ts (Vitest):
Test scenarios:
- LLM suggestion matching rule → LLM version wins
- LLM-only suggestion → added
- Rule-only suggestion → kept
- Fuzzy matching works (similar labels matched)
- Output sorted by impact then weights
- Empty inputs handled gracefully
- Step 3: Write tests for pvi-snapshot.service.ts
Create libs/libs-structural/vitto-studio/src/lib/services/pvi-snapshot.service.spec.ts (Vitest or Jasmine):
Test scenarios:
createSnapshotstores file contentspopForUndoreturns most recent snapshot- Stack respects max 50 (FIFO eviction)
canUndosignal is false when empty, true otherwiseclear()resets everything
- Step 4: Write tests for pvi-apply.service.ts
Create libs/libs-structural/vitto-studio/src/lib/services/pvi-apply.service.spec.ts:
Test scenarios for style apply:
- Calls bridge.sendPviMessage with PVI_INJECT_CSS
- Patches SCSS content correctly
- Creates snapshot before apply
Test scenarios for structural apply (after Task 2):
- Calls bridgeService.sendTask with correct prompt
- On success, writes files to VFS
- On error, rolls back from snapshot
- Sets/clears structuralApplying signal
- Step 5: Write tests for pvi.service.ts
Create libs/libs-structural/vitto-studio/src/lib/services/pvi.service.spec.ts:
Test scenarios:
onElementContextruns rule engine and populates suggestions signalselectCategoryfilters suggestions by categoryclearSelectionresets element contextisToolbarVisiblecomputed works correctly
- Step 6: Run all tests
npx vitest run libs/libs-ai/proactive-visual-intelligence/
npx nx test vitto-studio -- --testPathPattern=pvi
Expected: All tests PASS
- Step 7: Commit
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
- Conflict IDs in rule engine are stable and cross-referenced correctly
- Structural apply calls LLM and writes returned files to VFS
- Auto-rollback works when structural apply fails
- Shimmer state shows during structural apply
- SDK manifest regenerates from .intelligence.ts files (15 components)
- Prompt partial auto-generated with 15 components
- All PVI tests pass (engines + services)
- No regressions: existing style apply still works
- No regressions: existing click-to-select still works
- Manual E2E: Generate platform → click element → apply structural suggestion → verify change → undo