Browse Source

docs(03): create phase plans for GenerateImage node

Phase 03: Generate Node Refactor
- 3 plans, 9 tasks
- NanoBanana → GenerateImage (image models only)
- GenerateVideo deferred to Phase 6
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
bdd995e83f
  1. 130
      .planning/phases/03-generate-node-refactor/03-01-PLAN.md
  2. 162
      .planning/phases/03-generate-node-refactor/03-02-PLAN.md
  3. 147
      .planning/phases/03-generate-node-refactor/03-03-PLAN.md

130
.planning/phases/03-generate-node-refactor/03-01-PLAN.md

@ -7,10 +7,10 @@ type: execute
<objective> <objective>
Rename NanoBanana node to GenerateImage and add multi-provider model selector. Rename NanoBanana node to GenerateImage and add multi-provider model selector.
Purpose: Transform the hardcoded Gemini-only node into a multi-provider image generation node that filters to image-capable models only. Purpose: Transform the Gemini-only node into a multi-provider image generation node.
Output: GenerateImage node component with provider/model dropdown (image models only), updated types, store functions renamed. Output: GenerateImageNode component with provider/model dropdowns filtering to image-capable models only.
Note: GenerateVideo node will be added in Phase 6 (Video & Polish) as a separate node type. Note: Video generation will use a separate GenerateVideo node in Phase 6.
</objective> </objective>
<execution_context> <execution_context>
@ -22,127 +22,93 @@ Note: GenerateVideo node will be added in Phase 6 (Video & Polish) as a separate
@.planning/PROJECT.md @.planning/PROJECT.md
@.planning/ROADMAP.md @.planning/ROADMAP.md
@.planning/STATE.md @.planning/STATE.md
# Prior phase context:
@.planning/phases/01-provider-infrastructure/01-02-SUMMARY.md @.planning/phases/01-provider-infrastructure/01-02-SUMMARY.md
@.planning/phases/02-model-discovery/02-03-SUMMARY.md @.planning/phases/02-model-discovery/02-03-SUMMARY.md
# Key source files:
@src/components/nodes/NanoBananaNode.tsx @src/components/nodes/NanoBananaNode.tsx
@src/types/index.ts @src/types/index.ts
@src/store/workflowStore.ts @src/store/workflowStore.ts
@src/lib/providers/types.ts @src/lib/providers/types.ts
**Tech stack available:** Zustand, React, ProviderModel interface, /api/models endpoint **Tech available:** ProviderModel interface, /api/models endpoint, provider registry
**Established patterns:** Provider self-registration, localStorage settings persistence **Patterns:** Provider self-registration, localStorage settings persistence
**Constraining decisions:** **Constraints:** Keep "nanoBanana" node type string for backward compatibility
- Keep nanoBanana node type internally for backward compatibility
- Gemini models use internal names (nano-banana, nano-banana-pro)
</context> </context>
<tasks> <tasks>
<task type="auto"> <task type="auto">
<name>Task 1: Update types for multi-provider model selection</name> <name>Task 1: Add SelectedModel type and update NanoBananaNodeData</name>
<files>src/types/index.ts</files> <files>src/types/index.ts</files>
<action> <action>
1. Add new types for selected model: Add SelectedModel interface:
- `SelectedModel` interface with { provider: ProviderType, modelId: string, displayName: string } ```typescript
- Keep existing ModelType for backward compatibility export interface SelectedModel {
provider: ProviderType;
2. Update NanoBananaNodeData interface: modelId: string;
- Add optional `selectedModel?: SelectedModel` field displayName: string;
- Keep existing `model: ModelType` field for Gemini backward compatibility }
```
3. Add "gemini" to ProviderType union if not already present (it should be the Gemini provider identifier)
Update NanoBananaNodeData to add optional selectedModel field (keep existing model field for Gemini backward compatibility).
WHY: NanoBananaNodeData needs to store either legacy Gemini model selection OR new multi-provider selection. The optional selectedModel field enables graceful migration.
Add "gemini" to ProviderType if not present.
</action> </action>
<verify>npx tsc --noEmit passes</verify> <verify>npx tsc --noEmit</verify>
<done>Types compile, SelectedModel interface exists, NanoBananaNodeData has selectedModel field</done> <done>SelectedModel type exists, NanoBananaNodeData has selectedModel field, types compile</done>
</task> </task>
<task type="auto"> <task type="auto">
<name>Task 2: Rename component to GenerateImageNode with model selector UI</name> <name>Task 2: Create GenerateImageNode with provider/model selector</name>
<files>src/components/nodes/NanoBananaNode.tsx, src/components/nodes/index.ts</files> <files>src/components/nodes/NanoBananaNode.tsx, src/components/nodes/index.ts</files>
<action> <action>
1. Rename file from NanoBananaNode.tsx to GenerateImageNode.tsx 1. Rename NanoBananaNode.tsx to GenerateImageNode.tsx
2. Rename component function to GenerateImageNode
2. Rename component from NanoBananaNode to GenerateImageNode 3. Add provider dropdown showing enabled providers (gemini always, others if configured)
4. Add model dropdown:
3. Add model selector UI that shows: - For gemini: hardcoded Nano Banana / Nano Banana Pro
- Provider dropdown (gemini, replicate, fal) - only show enabled providers - For replicate/fal: fetch from /api/models?provider=X&capabilities=text-to-image,image-to-image
- Model dropdown (fetched from /api/models for selected provider) 5. Filter to image capabilities only (text-to-image, image-to-image)
- **Filter to image capabilities only**: text-to-image, image-to-image 6. Keep aspect ratio/resolution controls for Gemini, hide for others
- For Gemini, show existing hardcoded models (Nano Banana, Nano Banana Pro) 7. Update index.ts exports:
- For other providers, fetch models dynamically with capability filter
4. When provider/model changes:
- Update node data with selectedModel: { provider, modelId, displayName }
- For Gemini, also update legacy model field for backward compatibility
5. Keep existing aspect ratio/resolution controls for Gemini models
- Hide them for non-Gemini models (parameters will come from provider schema later)
6. Update index.ts to export GenerateImageNode and alias as NanoBananaNode:
```typescript ```typescript
export { GenerateImageNode } from "./GenerateImageNode"; export { GenerateImageNode } from "./GenerateImageNode";
export { GenerateImageNode as NanoBananaNode } from "./GenerateImageNode"; export { GenerateImageNode as NanoBananaNode } from "./GenerateImageNode";
``` ```
WHY: The component rename reflects its image-specific purpose. Video generation will be a separate GenerateVideoNode in Phase 6. The alias maintains backward compatibility.
</action> </action>
<verify>npm run build succeeds, component renders in browser</verify> <verify>npm run build</verify>
<done>GenerateImageNode.tsx exists, shows provider selector, model dropdown shows only image-capable models</done> <done>GenerateImageNode renders with provider/model dropdowns, shows only image models</done>
</task> </task>
<task type="auto"> <task type="auto">
<name>Task 3: Update store and canvas references</name> <name>Task 3: Update store and canvas references</name>
<files>src/store/workflowStore.ts, src/components/WorkflowCanvas.tsx</files> <files>src/store/workflowStore.ts, src/components/WorkflowCanvas.tsx, src/components/ConnectionDropMenu.tsx, src/components/FloatingActionBar.tsx</files>
<action> <action>
1. In workflowStore.ts: 1. workflowStore.ts: Rename helper functions (keep aliases for compatibility):
- Rename saveNanoBananaDefaults to saveGenerateImageDefaults (keep old name as alias) - loadNanoBananaDefaults → loadGenerateImageDefaults
- Rename loadNanoBananaDefaults to loadGenerateImageDefaults (keep old name as alias) - saveNanoBananaDefaults → saveGenerateImageDefaults
- Update localStorage key comment but keep actual key for migration 2. Update createDefaultNodeData "nanoBanana" case to set default selectedModel for Gemini
- Update createDefaultNodeData for "nanoBanana" case to include default selectedModel for Gemini 3. WorkflowCanvas.tsx: Update nodeTypes to use GenerateImageNode
4. Update imports in ConnectionDropMenu.tsx, FloatingActionBar.tsx
2. In WorkflowCanvas.tsx:
- Update nodeTypes registration to use GenerateImageNode
- Keep "nanoBanana" as the type key (internal type name unchanged for now)
- Update minimap color mapping if it references "nanoBanana"
3. In any file using NanoBananaNode import:
- Update import to use GenerateImageNode
- Files: ConnectionDropMenu.tsx, FloatingActionBar.tsx, etc.
WHY: Internal references update to new names while keeping the node type string "nanoBanana" for workflow file compatibility.
</action> </action>
<verify>npm run build succeeds, existing workflows load correctly</verify> <verify>npm run build succeeds, app loads without errors</verify>
<done>All imports updated, store functions renamed with aliases, workflows still load</done> <done>All references updated, existing workflows load correctly</done>
</task> </task>
</tasks> </tasks>
<verification> <verification>
Before declaring plan complete: - [ ] `npm run build` succeeds
- [ ] `npm run build` succeeds without errors
- [ ] `npx tsc --noEmit` passes type checking
- [ ] GenerateImageNode renders with provider dropdown - [ ] GenerateImageNode renders with provider dropdown
- [ ] Model selector shows only image-capable models (text-to-image, image-to-image) - [ ] Model selector shows only image-capable models
- [ ] Gemini models show in model selector - [ ] Existing workflows with nanoBanana nodes load
- [ ] Existing workflows with nanoBanana nodes still load
</verification> </verification>
<success_criteria> <success_criteria>
- All tasks completed
- All verification checks pass
- GenerateImageNode component exists with multi-provider UI - GenerateImageNode component exists with multi-provider UI
- Model selector filters to image capabilities only - Model selector filters to text-to-image and image-to-image only
- Types support both legacy Gemini and new multi-provider selection - Backward compatibility maintained
- Backward compatibility maintained for existing workflows
</success_criteria> </success_criteria>
<output> <output>
After completion, create `.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md` Create `.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md`
</output> </output>

162
.planning/phases/03-generate-node-refactor/03-02-PLAN.md

@ -5,12 +5,10 @@ type: execute
--- ---
<objective> <objective>
Add provider-specific execution logic to the generate API route for image generation. Implement provider-specific image generation in API route.
Purpose: Enable the generate endpoint to route requests to the correct provider (Gemini, Replicate, fal.ai) based on the selected model, filtering to image outputs only. Purpose: Route generation requests to correct provider (Gemini, Replicate, fal.ai) based on selected model.
Output: Updated /api/generate route that dispatches to provider-specific image generation logic. Output: Working generate endpoint that dispatches to provider implementations.
Note: Video generation will use a separate endpoint in Phase 6.
</objective> </objective>
<execution_context> <execution_context>
@ -20,156 +18,82 @@ Note: Video generation will use a separate endpoint in Phase 6.
<context> <context>
@.planning/PROJECT.md @.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md @.planning/STATE.md
# Prior plan context:
@.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md @.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md
# Key source files:
@src/app/api/generate/route.ts @src/app/api/generate/route.ts
@src/lib/providers/types.ts @src/lib/providers/types.ts
@src/lib/providers/replicate.ts @src/lib/providers/replicate.ts
@src/lib/providers/fal.ts @src/lib/providers/fal.ts
@src/lib/providers/index.ts
**Tech stack available:** ProviderInterface with generate() method, provider registry **Tech available:** ProviderInterface.generate(), provider registry
**Established patterns:** API routes return { success, data/error }, provider self-registration **Constraints:** Preserve existing Gemini logic, image output only
**Constraining decisions:**
- Gemini generation logic already works, preserve it
- Provider implementations have generate() method defined in ProviderInterface
</context> </context>
<tasks> <tasks>
<task type="auto"> <task type="auto">
<name>Task 1: Implement generate() method in Replicate provider</name> <name>Task 1: Implement Replicate provider generate() method</name>
<files>src/lib/providers/replicate.ts</files> <files>src/lib/providers/replicate.ts</files>
<action> <action>
1. Implement the generate() method that currently throws "not implemented": Implement generate() that currently throws "not implemented":
- Accept GenerationInput with model, prompt, images, parameters 1. POST to https://api.replicate.com/v1/predictions with { version: modelId, input: { prompt } }
- Call Replicate's prediction API: POST https://api.replicate.com/v1/predictions 2. Poll GET /predictions/{id} until status is "succeeded" or "failed"
- Handle input format: { version: model.id, input: { prompt, image?, ...parameters } } 3. Fetch output image URL and convert to base64 data URL
- Poll for completion (Replicate is async) or use webhook pattern 4. Return GenerationOutput with type: "image"
- Return GenerationOutput with image/video data 5. Skip image inputs for now (Phase 5 adds URL server)
2. Handle image input:
- If images provided, check if Replicate model expects URLs
- For now, skip image input (Phase 5 adds image URL server)
- Log warning if images provided but skipped
3. Fetch output:
- Poll GET /predictions/{id} until status is "succeeded" or "failed"
- Convert output URL to base64 data URL
- Return in GenerationOutput format with type: "image"
4. Filter to image models only:
- Only accept models with text-to-image or image-to-image capabilities
- Return error if video model passed (video generation is Phase 6)
WHY: Replicate requires polling for async predictions. The generate() method encapsulates this complexity. Image-only filter keeps this endpoint focused.
</action> </action>
<verify>TypeScript compiles, generate() method exists and doesn't throw immediately</verify> <verify>TypeScript compiles</verify>
<done>Replicate provider has working generate() method that calls prediction API for image models</done> <done>Replicate generate() calls prediction API and returns image</done>
</task> </task>
<task type="auto"> <task type="auto">
<name>Task 2: Implement generate() method in fal.ai provider</name> <name>Task 2: Implement fal.ai provider generate() method</name>
<files>src/lib/providers/fal.ts</files> <files>src/lib/providers/fal.ts</files>
<action> <action>
1. Implement the generate() method: Implement generate():
- Accept GenerationInput with model, prompt, images, parameters 1. POST to https://fal.run/{modelId} with { prompt, ...parameters }
- Call fal.ai's run endpoint: POST https://fal.run/{model.id} 2. Use "Key {apiKey}" auth header
- Include prompt and parameters in request body 3. Extract image URL from response, convert to base64
- fal.ai is synchronous (waits for result) 4. Return GenerationOutput with type: "image"
2. Handle auth:
- Use "Key {apiKey}" header format (already established)
- API key optional but rate limited without it
3. Parse response:
- Extract image URL from response
- Convert to base64 data URL for consistency
- Handle images array output format
4. Filter to image models only:
- Only accept models with text-to-image or image-to-image capabilities
- Return error if video model passed (video generation is Phase 6)
WHY: fal.ai's synchronous API is simpler than Replicate. The generate() method normalizes the response format. Image-only filter keeps this endpoint focused.
</action> </action>
<verify>TypeScript compiles, generate() method exists</verify> <verify>TypeScript compiles</verify>
<done>fal.ai provider has working generate() method that calls fal.run endpoint for image models</done> <done>fal.ai generate() calls fal.run and returns image</done>
</task> </task>
<task type="auto"> <task type="auto">
<name>Task 3: Update generate API route to dispatch by provider</name> <name>Task 3: Update generate API route for multi-provider dispatch</name>
<files>src/app/api/generate/route.ts</files> <files>src/app/api/generate/route.ts</files>
<action> <action>
1. Update request type to accept either: 1. Accept both formats:
- Legacy format: { model: ModelType, prompt, images, aspectRatio, resolution, useGoogleSearch } - Legacy: { model, prompt, images, aspectRatio, resolution }
- New format: { selectedModel: SelectedModel, prompt, images, parameters } - New: { selectedModel, prompt, images, parameters }
2. If selectedModel provided and provider !== "gemini":
2. Add provider dispatch logic: - Get provider from registry
```typescript - Get API key from request headers (X-Replicate-API-Key, X-Fal-API-Key)
if (body.selectedModel) { - Call provider.generate()
// New multi-provider path - Return result
const { provider, modelId } = body.selectedModel; 3. Otherwise use existing Gemini logic
if (provider === "gemini") { 4. Extract Gemini code into helper function for clarity
// Use existing Gemini logic
} else {
// Get provider and call generate()
const providerImpl = getProvider(provider);
const model = await providerImpl.getModel(modelId);
const result = await providerImpl.generate({
model,
prompt: body.prompt,
images: body.images,
parameters: body.parameters
});
return NextResponse.json(result);
}
} else {
// Legacy Gemini path (existing code)
}
```
3. Extract Gemini-specific logic into helper function for clarity
4. Ensure API key handling:
- For Gemini: use GEMINI_API_KEY from env
- For Replicate/fal: get from provider.getApiKey() (localStorage via client header)
5. Add client headers for API keys:
- Accept X-Replicate-API-Key and X-Fal-API-Key headers
- Pass to provider implementations
WHY: The route becomes a dispatcher that preserves existing Gemini behavior while enabling new providers.
</action> </action>
<verify>npm run build succeeds, existing Gemini generation still works</verify> <verify>npm run build, test Gemini generation still works</verify>
<done>Generate route dispatches to correct provider, Gemini still works as before</done> <done>API route dispatches to correct provider, Gemini unchanged</done>
</task> </task>
</tasks> </tasks>
<verification> <verification>
Before declaring plan complete: - [ ] `npm run build` succeeds
- [ ] `npm run build` succeeds without errors - [ ] Gemini generation works as before
- [ ] Existing Gemini generation works (test with existing workflow) - [ ] Replicate provider has generate() implementation
- [ ] Replicate provider generate() method implemented - [ ] fal.ai provider has generate() implementation
- [ ] fal.ai provider generate() method implemented
- [ ] API route accepts both legacy and new request formats
</verification> </verification>
<success_criteria> <success_criteria>
- Generate API routes requests to correct provider
- All tasks completed - Provider generate() methods implemented
- All verification checks pass - Existing Gemini functionality preserved
- Generate API routes to correct provider based on selectedModel
- Gemini generation unchanged for backward compatibility
- Replicate and fal.ai providers have generate() implementations
</success_criteria> </success_criteria>
<output> <output>
After completion, create `.planning/phases/03-generate-node-refactor/03-02-SUMMARY.md` Create `.planning/phases/03-generate-node-refactor/03-02-SUMMARY.md`
</output> </output>

147
.planning/phases/03-generate-node-refactor/03-03-PLAN.md

@ -5,10 +5,10 @@ type: execute
--- ---
<objective> <objective>
Ensure backward compatibility for existing workflows containing nanoBanana nodes. Ensure backward compatibility for existing workflows.
Purpose: Existing saved workflows must load and execute correctly without modification. Purpose: Existing saved workflows must load and execute without modification.
Output: Migration logic, verified backward compatibility, updated workflow execution. Output: Migration logic, working execution with both legacy and new formats.
</objective> </objective>
<execution_context> <execution_context>
@ -18,137 +18,88 @@ Output: Migration logic, verified backward compatibility, updated workflow execu
<context> <context>
@.planning/PROJECT.md @.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md @.planning/STATE.md
# Prior plan context:
@.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md @.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md
@.planning/phases/03-generate-node-refactor/03-02-SUMMARY.md @.planning/phases/03-generate-node-refactor/03-02-SUMMARY.md
# Key source files:
@src/store/workflowStore.ts @src/store/workflowStore.ts
@src/types/index.ts
@src/components/nodes/GenerateImageNode.tsx @src/components/nodes/GenerateImageNode.tsx
**Tech stack available:** Zustand, workflow JSON persistence **Constraints:** Node type "nanoBanana" unchanged, legacy ModelType must work
**Established patterns:** Workflow load/save in workflowStore
**Constraining decisions:**
- Node type "nanoBanana" must remain unchanged in workflow files
- Legacy ModelType ("nano-banana", "nano-banana-pro") must still work
</context> </context>
<tasks> <tasks>
<task type="auto"> <task type="auto">
<name>Task 1: Add migration logic for loaded workflows</name> <name>Task 1: Add workflow migration logic</name>
<files>src/store/workflowStore.ts</files> <files>src/store/workflowStore.ts</files>
<action> <action>
1. In loadWorkflow() or where workflow JSON is parsed, add migration: In workflow loading (loadWorkflow or where JSON is parsed):
- When loading a nanoBanana node without selectedModel field 1. For nanoBanana nodes without selectedModel, derive it from legacy model field:
- Automatically set selectedModel based on legacy model field: ```typescript
```typescript if (node.type === "nanoBanana" && node.data.model && !node.data.selectedModel) {
if (node.type === "nanoBanana" && node.data.model && !node.data.selectedModel) { node.data.selectedModel = {
node.data.selectedModel = { provider: "gemini",
provider: "gemini", modelId: node.data.model,
modelId: node.data.model, displayName: node.data.model === "nano-banana" ? "Nano Banana" : "Nano Banana Pro"
displayName: node.data.model === "nano-banana" ? "Nano Banana" : "Nano Banana Pro" };
}; }
} ```
``` 2. Keep both fields synced on save for bidirectional compatibility
2. Keep legacy model field synced for saves:
- When selectedModel is Gemini, also update the model field
- This ensures old versions can still read the workflow
3. Add version field to workflow if not present:
- Set workflowVersion in saved JSON for future migrations
- Not strictly necessary but good practice
WHY: Migration on load allows old workflows to work without manual editing. Syncing both fields maintains bidirectional compatibility.
</action> </action>
<verify>Load an existing workflow file, check node data has both model and selectedModel</verify> <verify>Load existing workflow file, check node has selectedModel populated</verify>
<done>Old workflows load with selectedModel populated, new saves include both fields</done> <done>Old workflows load with selectedModel auto-populated</done>
</task> </task>
<task type="auto"> <task type="auto">
<name>Task 2: Update executeWorkflow for multi-provider generation</name> <name>Task 2: Update executeWorkflow for multi-provider</name>
<files>src/store/workflowStore.ts</files> <files>src/store/workflowStore.ts</files>
<action> <action>
1. In executeWorkflow()'s nanoBanana case: In executeWorkflow nanoBanana case:
- Check if node has selectedModel 1. Check for selectedModel
- If selectedModel exists and provider !== "gemini", pass API keys in headers: 2. If non-Gemini provider, add API key headers from providerSettings:
```typescript ```typescript
const headers: Record<string, string> = { const headers = { "Content-Type": "application/json" };
"Content-Type": "application/json" if (nodeData.selectedModel?.provider === "replicate") {
}; const config = get().providerSettings.providers.find(p => p.id === "replicate");
if (nodeData.selectedModel?.provider === "replicate") { if (config?.apiKey) headers["X-Replicate-API-Key"] = config.apiKey;
const settings = get().providerSettings; }
const replicateConfig = settings.providers.find(p => p.id === "replicate"); // Similar for fal
if (replicateConfig?.apiKey) { ```
headers["X-Replicate-API-Key"] = replicateConfig.apiKey; 3. Include selectedModel in request body
} 4. Handle GenerationOutput response format
}
// Similar for fal
```
2. Build request payload:
- Include selectedModel in request body for new providers
- Keep existing fields for Gemini backward compatibility
3. Handle response format:
- Provider generate() returns GenerationOutput with outputs array
- Extract first image output as outputImage
- Validate output type is "image" (video handled by separate node in Phase 6)
WHY: The store's execution logic needs to pass provider API keys (stored client-side) to the server route.
</action> </action>
<verify>Execute workflow with Gemini model, verify generation still works</verify> <verify>Execute workflow with Gemini model</verify>
<done>Workflow execution handles both legacy and new model selection formats</done> <done>Execution works for both legacy and new model selection</done>
</task> </task>
<task type="auto"> <task type="auto">
<name>Task 3: Update GenerateImageNode to handle legacy data</name> <name>Task 3: Handle legacy data in GenerateImageNode UI</name>
<files>src/components/nodes/GenerateImageNode.tsx</files> <files>src/components/nodes/GenerateImageNode.tsx</files>
<action> <action>
1. When component mounts or data changes: 1. On mount/data change: if model exists but not selectedModel, derive selectedModel
- If model exists but not selectedModel, derive selectedModel from model 2. Ensure dropdowns reflect correct provider/model for migrated nodes
- Display the correct provider/model in dropdowns 3. Save defaults in both formats for compatibility
2. Ensure UI state reflects actual node data:
- Provider dropdown shows "gemini" for legacy nodes
- Model dropdown shows correct Gemini model name
3. When saving defaults, save in both formats:
- saveGenerateImageDefaults should save both model and selectedModel
- Ensures new nodes get correct defaults
WHY: The UI must correctly display legacy nodes that were migrated on load.
</action> </action>
<verify>Open workflow with old nanoBanana node, verify dropdowns show correct selection</verify> <verify>Open workflow with old nanoBanana node, verify correct selection shown</verify>
<done>GenerateImageNode displays correct provider/model for both legacy and new nodes</done> <done>UI correctly displays legacy nodes</done>
</task> </task>
</tasks> </tasks>
<verification> <verification>
Before declaring plan complete: - [ ] `npm run build` succeeds
- [ ] `npm run build` succeeds without errors - [ ] Existing workflow loads without errors
- [ ] Load existing workflow with nanoBanana nodes - nodes display correctly - [ ] Legacy Gemini execution works
- [ ] Execute existing workflow - Gemini generation works - [ ] New provider selection persists correctly
- [ ] Create new Generate node - defaults are correct
- [ ] Save and reload workflow - data persists correctly
</verification> </verification>
<success_criteria> <success_criteria>
- Existing workflows load and execute unchanged
- All tasks completed - Migration auto-populates selectedModel
- All verification checks pass - Both formats work in same workflow
- Existing workflows load and execute without errors
- Migration logic populates selectedModel from legacy model field
- Both old and new format nodes work in same workflow
- Phase 3 complete - Phase 3 complete
</success_criteria> </success_criteria>
<output> <output>
After completion, create `.planning/phases/03-generate-node-refactor/03-03-SUMMARY.md` Create `.planning/phases/03-generate-node-refactor/03-03-SUMMARY.md`
</output> </output>

Loading…
Cancel
Save