Files

464 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VIEW-006: Impact Radar
**View Type:** 🎨 Canvas Overlay (enhances existing canvas with highlighting)
## Overview
Before you change something, see everywhere it's used and what might break. A pre-change impact analysis tool that shows the blast radius of modifications. **Highlights persist on the canvas until dismissed!**
**Estimate:** 3-4 days
**Priority:** MEDIUM
**Complexity:** Medium
**Dependencies:** VIEW-000 (Foundation), PREREQ-004 (Canvas Highlighting API)
---
## The Problem
When you want to change something in a Noodl project:
- You don't know everywhere a component is used
- You don't know what depends on a specific output
- Changing a component's interface might break 5 other places
- Renaming a variable might affect components you forgot about
- "I'll just change this real quick" → breaks production
---
## The Solution
An impact analysis that:
1. Shows everywhere a component/node is used
2. Shows what depends on specific outputs
3. Warns about breaking changes
4. Lets you preview "what if I change this?"
5. Provides a checklist of things to update
---
## User Stories
1. **As a developer refactoring**, I want to know everywhere this component is used before I change its interface.
2. **As a developer renaming**, I want to see all places that reference this name so I don't break anything.
3. **As a developer removing**, I want to know if anything depends on this before I delete it.
4. **As a developer planning**, I want to understand the impact of a proposed change before implementing it.
---
## UI Design
### Component Impact View
```
┌─────────────────────────────────────────────────────────────────┐
│ Impact Radar [Refresh] │
├─────────────────────────────────────────────────────────────────┤
│ Analyzing: AuthFlow Component │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📊 USAGE SUMMARY │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Used in 3 components across 2 pages │ │
│ │ │ │
│ │ Impact level: 🟡 MEDIUM │ │
│ │ Reason: Changes affect multiple pages │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 📍 USAGE LOCATIONS │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌─ Login Page ────────────────────────────────────────────┐ │ │
│ │ │ Instance: authFlow_1 [→ Jump] │ │ │
│ │ │ │ │ │
│ │ │ Connected inputs: │ │ │
│ │ │ • onLoginRequest ← LoginButton.onClick │ │ │
│ │ │ • redirectUrl ← "/dashboard" (static) │ │ │
│ │ │ │ │ │
│ │ │ Connected outputs: │ │ │
│ │ │ • currentUser → NavBar.user, ProfileWidget.user │ │ │
│ │ │ • onSuccess → Navigate("/dashboard") │ │ │
│ │ │ • authError → ErrorToast.message │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─ Settings Page ─────────────────────────────────────────┐ │ │
│ │ │ Instance: authFlow_2 [→ Jump] │ │ │
│ │ │ │ │ │
│ │ │ Connected inputs: │ │ │
│ │ │ • onLogoutRequest ← LogoutButton.onClick │ │ │
│ │ │ │ │ │
│ │ │ Connected outputs: │ │ │
│ │ │ • onSuccess → Navigate("/login") │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─ App Shell ─────────────────────────────────────────────┐ │ │
│ │ │ Instance: authFlow_global [→ Jump] │ │ │
│ │ │ │ │ │
│ │ │ Connected outputs: │ │ │
│ │ │ • isAuthenticated → RouteGuard.condition │ │ │
│ │ │ • currentUser → (passed to 12 child components) │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ⚠️ CHANGE IMPACT ANALYSIS │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ If you MODIFY these outputs: │ │
│ │ │ │
│ │ • currentUser Impact: 🔴 HIGH │ │
│ │ └─ Used by 14 consumers across 3 components │ │
│ │ └─ Breaking changes will affect: NavBar, ProfileWidget, │ │
│ │ UserSettings, ChatHeader, and 10 more... │ │
│ │ │ │
│ │ • onSuccess Impact: 🟡 MEDIUM │ │
│ │ └─ Used by 3 consumers │ │
│ │ └─ Navigation flows depend on this signal │ │
│ │ │ │
│ │ • authError Impact: 🟢 LOW │ │
│ │ └─ Used by 2 consumers │ │
│ │ └─ Only affects error display │ │
│ │ │ │
│ │ If you REMOVE these inputs: │ │
│ │ │ │
│ │ • redirectUrl Impact: 🟡 MEDIUM │ │
│ │ └─ Login Page passes static value │ │
│ │ └─ Would need to handle internally or change caller │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 📋 CHANGE CHECKLIST │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ If changing currentUser output structure: │ │
│ │ □ Update Login Page → NavBar connection │ │
│ │ □ Update Login Page → ProfileWidget connection │ │
│ │ □ Update App Shell → RouteGuard connection │ │
│ │ □ Update App Shell → 12 child components │ │
│ │ □ Test login flow on Login Page │ │
│ │ □ Test auth guard on protected routes │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Port-Specific Impact
```
┌─────────────────────────────────────────────────────────────────┐
│ Impact Radar: currentUser output │
├─────────────────────────────────────────────────────────────────┤
│ │
│ This output feeds into: │
│ │
│ ┌─────────────────────┐ │
│ │ AuthFlow │ │
│ │ currentUser output │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌─────────────┼─────────────┬─────────────┐ │
│ ▼ ▼ ▼ ▼ │
│ ┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ NavBar │ │ Profile │ │ Settings │ │ +9 more │ │
│ │ .user │ │ Widget │ │ .user │ │ │ │
│ └────────┘ │ .user │ └──────────┘ └──────────┘ │
│ └──────────┘ │
│ │
│ Properties accessed: │
│ • .name (used in 8 places) │
│ • .email (used in 3 places) │
│ • .avatar (used in 4 places) │
│ • .role (used in 2 places) │
│ │
│ ⚠️ If you change the structure of currentUser: │
│ All 17 usages will need to be verified │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Quick Impact Badge (for Components Panel)
```
┌────────────────────────────────────────┐
│ 🧩 AuthFlow (×3) 🔴 │
│ Used in 3 places, HIGH impact │
└────────────────────────────────────────┘
```
---
## Technical Design
### Data Model
```typescript
interface ImpactAnalysis {
target: {
type: 'component' | 'node' | 'port';
component?: ComponentModel;
node?: NodeGraphNode;
port?: string;
};
summary: {
usageCount: number;
impactLevel: 'low' | 'medium' | 'high' | 'critical';
reason: string;
};
usages: ComponentUsageDetail[];
portImpacts: PortImpact[];
changeChecklist: ChecklistItem[];
}
interface ComponentUsageDetail {
component: ComponentModel; // Where it's used
instanceNodeId: string; // The instance node
instanceLabel: string;
connectedInputs: {
port: string;
connections: {
fromNode: NodeGraphNode;
fromPort: string;
isStatic: boolean;
staticValue?: unknown;
}[];
}[];
connectedOutputs: {
port: string;
connections: {
toNode: NodeGraphNode;
toPort: string;
transitiveUsages?: number; // How many things depend on this downstream
}[];
}[];
}
interface PortImpact {
port: string;
direction: 'input' | 'output';
impactLevel: 'low' | 'medium' | 'high' | 'critical';
consumerCount: number;
consumers: {
component: string;
node: string;
port: string;
}[];
propertiesAccessed?: string[]; // For object outputs, what properties are used
}
interface ChecklistItem {
action: string;
component: string;
priority: 'required' | 'recommended' | 'optional';
completed: boolean;
}
```
### Building Impact Analysis
```typescript
function analyzeImpact(
project: ProjectModel,
target: ImpactTarget
): ImpactAnalysis {
if (target.type === 'component') {
return analyzeComponentImpact(project, target.component);
} else if (target.type === 'node') {
return analyzeNodeImpact(project, target.component, target.node);
} else {
return analyzePortImpact(project, target.component, target.node, target.port);
}
}
function analyzeComponentImpact(
project: ProjectModel,
component: ComponentModel
): ImpactAnalysis {
// Find all usages
const usages = findComponentUsages(project, component.fullName);
// Analyze each port
const portImpacts: PortImpact[] = [];
// Outputs - what depends on them?
component.getPorts().filter(p => p.plug === 'output').forEach(port => {
const consumers = findPortConsumers(project, component, port.name, usages);
portImpacts.push({
port: port.name,
direction: 'output',
impactLevel: calculateImpactLevel(consumers.length),
consumerCount: consumers.length,
consumers,
propertiesAccessed: analyzePropertyAccess(consumers)
});
});
// Inputs - what provides them?
component.getPorts().filter(p => p.plug === 'input').forEach(port => {
const providers = findPortProviders(project, component, port.name, usages);
portImpacts.push({
port: port.name,
direction: 'input',
impactLevel: calculateImpactLevel(providers.length),
consumerCount: providers.length,
consumers: providers
});
});
// Calculate overall impact
const maxImpact = Math.max(...portImpacts.map(p => impactScore(p.impactLevel)));
// Generate checklist
const checklist = generateChecklist(component, usages, portImpacts);
return {
target: { type: 'component', component },
summary: {
usageCount: usages.length,
impactLevel: scoreToLevel(maxImpact),
reason: generateImpactReason(usages, portImpacts)
},
usages: usages.map(u => buildUsageDetail(project, component, u)),
portImpacts,
changeChecklist: checklist
};
}
function calculateImpactLevel(consumerCount: number): ImpactLevel {
if (consumerCount === 0) return 'low';
if (consumerCount <= 2) return 'low';
if (consumerCount <= 5) return 'medium';
if (consumerCount <= 10) return 'high';
return 'critical';
}
```
---
## Implementation Phases
### Phase 1: Usage Finding (1 day)
1. Build on VIEW-000's `findComponentUsages()`
2. Add detailed connection analysis per usage
3. Track which ports are connected where
4. Count transitive dependencies
**Verification:**
- [ ] Finds all component usages
- [ ] Connection details accurate
- [ ] Transitive counts correct
### Phase 2: Impact Calculation (0.5-1 day)
1. Calculate impact level per port
2. Analyze property access patterns
3. Generate overall impact summary
4. Create impact reasons
**Verification:**
- [ ] Impact levels sensible
- [ ] Property analysis works
- [ ] Summary helpful
### Phase 3: UI - Usage Display (1 day)
1. Create `ImpactRadarView` component
2. Show usage locations with details
3. Display per-port impact
4. Add navigation to usages
**Verification:**
- [ ] Usages displayed clearly
- [ ] Port impacts visible
- [ ] Navigation works
### Phase 4: Checklist Generation (0.5 day)
1. Generate change checklist from analysis
2. Allow marking items complete
3. Prioritize checklist items
**Verification:**
- [ ] Checklist comprehensive
- [ ] Priorities sensible
- [ ] Can mark complete
### Phase 5: Polish & Integration (0.5-1 day)
1. Add impact badges to Components Panel
2. Context menu "Show Impact"
3. Quick impact preview on hover
4. Performance optimization
**Verification:**
- [ ] Badges show in panel
- [ ] Context menu works
- [ ] Performance acceptable
---
## Files to Create
```
packages/noodl-editor/src/editor/src/views/AnalysisPanel/
└── ImpactRadarView/
├── index.ts
├── ImpactRadarView.tsx
├── ImpactRadarView.module.scss
├── UsageSummary.tsx
├── UsageLocation.tsx
├── PortImpactList.tsx
├── ChangeChecklist.tsx
├── ImpactBadge.tsx
└── useImpactAnalysis.ts
packages/noodl-editor/src/editor/src/utils/graphAnalysis/
└── impact.ts
```
---
## Success Criteria
- [ ] Shows all places component is used
- [ ] Impact level calculation sensible
- [ ] Port-level analysis accurate
- [ ] Checklist helpful for changes
- [ ] Navigation to usages works
- [ ] Renders in < 1s
---
## Future Enhancements
- **"What if" simulation** - Preview changes without making them
- **Diff preview** - Show what would change in each usage
- **Auto-update** - Automatically update simple changes across usages
- **Impact history** - Track changes and their actual impact over time
---
## Risks & Mitigations
| Risk | Mitigation |
|------|------------|
| Too many usages to display | Pagination, grouping, filtering |
| Property analysis misses patterns | Start simple, expand based on real usage |
| Impact levels feel wrong | Make configurable, learn from feedback |
---
## Dependencies
- VIEW-000 Foundation
- VIEW-001 (optional, for visual representation)
## Blocks
- None (independent view)