Archive Management: - Moved RALPH_LOOP, CANVAS, and dashboard implementation plans to archive/review/ for CEO review - Moved completed restructuring plan and protocol v1 to archive/historical/ - Moved old session summaries to archive/review/ New HQ Documentation (docs/hq/): - README.md: Overview of Atomizer-HQ multi-agent optimization team - PROJECT_STRUCTURE.md: Standard KB-integrated project layout with Hydrotech reference - KB_CONVENTIONS.md: Knowledge Base accumulation principles with generation tracking - AGENT_WORKFLOWS.md: Project lifecycle phases and agent handoffs (OP_09 integration) - STUDY_CONVENTIONS.md: Technical study execution standards and atomizer_spec.json format Index Update: - Reorganized docs/00_INDEX.md with HQ docs prominent - Updated structure to reflect new agent-focused organization - Maintained core documentation access for engineers No files deleted, only moved to appropriate archive locations.
16 KiB
Canvas V3 - Comprehensive Fix & Enhancement Plan
Created: January 16, 2026 Status: Planning Priority: High
Problem Analysis
Critical Bugs (Must Fix)
| Issue | Impact | Root Cause (Likely) |
|---|---|---|
| Atomizer Assistant broken | High - core feature unusable | WebSocket connection or chat hook error |
| Cannot delete connections | High - workflow editing blocked | Missing edge selection/delete handler |
| Drag & drop positioning wrong | Medium - UX frustration | Position calculation not accounting for scroll/zoom |
Data Loading Issues
| Issue | Impact | Root Cause (Likely) |
|---|---|---|
| Auto-connect missing | High - manual work required | loadFromConfig creates nodes but not edges |
| Missing elements (OPD extractor) | High - incomplete workflows | Incomplete parsing of optimization_config.json |
| Constraints not shown | High - incomplete workflows | Constraints not parsed from config |
| Algorithm not pre-selected | Medium - extra clicks | Algorithm node not created from config |
UI/UX Issues
| Issue | Impact | Root Cause (Likely) |
|---|---|---|
| Interface too small | Medium - wasted screen space | Fixed dimensions, not responsive |
| Insufficient contrast | Medium - accessibility | White text on light blue background |
| Font size too small | Low - readability | Hardcoded small font sizes |
Feature Requests
| Feature | Value | Complexity |
|---|---|---|
| Auto-complete with Claude | High - smart assistance | Medium |
| Templates/guidelines | Medium - onboarding | Low |
| Canvas ↔ Assistant integration | High - conversational control | High |
Implementation Strategy
Phased Approach
Phase 1: Critical Fixes (2 hours)
↓
Phase 2: Data Loading (3 hours)
↓
Phase 3: UI/UX Polish (2 hours)
↓
Phase 4: Claude Integration (3 hours)
↓
Phase 5: Testing & Commit (1 hour)
Phase 1: Critical Bug Fixes
1.1 Fix Atomizer Assistant Error
Investigation Steps:
- Check
ChatPanel.tsxfor error handling - Check
useCanvasChat.tshook for connection issues - Verify WebSocket endpoint
/api/chat/is working - Check if
useChat.tsbase hook has errors
Likely Fix:
- Add error boundary around chat component
- Add null checks for WebSocket connection
- Provide fallback UI when chat unavailable
1.2 Enable Connection Deletion
Current State: Edges can't be selected or deleted
Implementation:
// In AtomizerCanvas.tsx
<ReactFlow
edgesUpdatable={true}
edgesFocusable={true}
deleteKeyCode={['Backspace', 'Delete']}
onEdgeClick={(event, edge) => selectEdge(edge.id)}
/>
Store Update:
// In useCanvasStore.ts
deleteEdge: (edgeId: string) => {
set((state) => ({
edges: state.edges.filter((e) => e.id !== edgeId),
}));
},
1.3 Fix Drag & Drop Positioning
Problem: New nodes appear at wrong position (not where dropped)
Fix in AtomizerCanvas.tsx:
const onDrop = useCallback((event: DragEvent) => {
event.preventDefault();
if (!reactFlowInstance.current || !reactFlowWrapper.current) return;
const type = event.dataTransfer.getData('application/reactflow') as NodeType;
if (!type) return;
// Get correct position accounting for viewport transform
const bounds = reactFlowWrapper.current.getBoundingClientRect();
const position = reactFlowInstance.current.screenToFlowPosition({
x: event.clientX - bounds.left,
y: event.clientY - bounds.top,
});
addNode(type, position);
}, [addNode]);
Phase 2: Data Loading Improvements
2.1 Enhanced loadFromConfig Function
Goal: When loading a study, create ALL nodes AND edges automatically.
Current Problems:
- Nodes created but not connected
- Some extractors/constraints missing
- Algorithm not created
New Implementation Strategy:
loadFromConfig: (config: OptimizationConfig) => {
const nodes: Node[] = [];
const edges: Edge[] = [];
// Layout constants
const COLS = { model: 50, dvar: 50, solver: 250, extractor: 450, obj: 650, algo: 850 };
const ROW_HEIGHT = 100;
const START_Y = 100;
// Track IDs for connections
const nodeIds = {
model: '',
solver: '',
dvars: [] as string[],
extractors: {} as Record<string, string>, // extractor_id -> node_id
objectives: [] as string[],
constraints: [] as string[],
algorithm: '',
};
// 1. Create Model Node
if (config.nx_model) {
const id = `model_${Date.now()}`;
nodeIds.model = id;
nodes.push({
id,
type: 'model',
position: { x: COLS.model, y: START_Y },
data: {
type: 'model',
label: 'Model',
configured: true,
filePath: config.nx_model.sim_path || config.nx_model.prt_path,
fileType: config.nx_model.sim_path ? 'sim' : 'prt',
},
});
}
// 2. Create Solver Node
if (config.solver) {
const id = `solver_${Date.now()}`;
nodeIds.solver = id;
nodes.push({
id,
type: 'solver',
position: { x: COLS.solver, y: START_Y },
data: {
type: 'solver',
label: 'Solver',
configured: true,
solverType: `SOL${config.solver.solution_type}`,
},
});
// Connect Model → Solver
if (nodeIds.model) {
edges.push({
id: `e_model_solver`,
source: nodeIds.model,
target: nodeIds.solver,
animated: true,
});
}
}
// 3. Create Design Variables (connected to Model)
config.design_variables?.forEach((dv, i) => {
const id = `dvar_${i}_${Date.now()}`;
nodeIds.dvars.push(id);
nodes.push({
id,
type: 'designVar',
position: { x: COLS.dvar, y: START_Y + 150 + i * ROW_HEIGHT },
data: {
type: 'designVar',
label: dv.name,
configured: true,
expressionName: dv.nx_expression || dv.name,
minValue: dv.lower_bound,
maxValue: dv.upper_bound,
unit: dv.unit,
},
});
// Connect DVar → Model (or keep disconnected, they're inputs)
});
// 4. Create Extractors from objectives AND constraints
const allExtractors = new Set<string>();
config.objectives?.forEach(obj => allExtractors.add(obj.extractor_id));
config.constraints?.forEach(con => {
if (con.extractor_id) allExtractors.add(con.extractor_id);
});
let extractorRow = 0;
allExtractors.forEach((extractorId) => {
const id = `extractor_${extractorId}_${Date.now()}`;
nodeIds.extractors[extractorId] = id;
// Find extractor config
const objWithExt = config.objectives?.find(o => o.extractor_id === extractorId);
const conWithExt = config.constraints?.find(c => c.extractor_id === extractorId);
nodes.push({
id,
type: 'extractor',
position: { x: COLS.extractor, y: START_Y + extractorRow * ROW_HEIGHT },
data: {
type: 'extractor',
label: extractorId,
configured: true,
extractorId: extractorId,
extractorName: objWithExt?.name || conWithExt?.name || extractorId,
},
});
extractorRow++;
// Connect Solver → Extractor
if (nodeIds.solver) {
edges.push({
id: `e_solver_${extractorId}`,
source: nodeIds.solver,
target: id,
});
}
});
// 5. Create Objectives (connected to Extractors)
config.objectives?.forEach((obj, i) => {
const id = `obj_${i}_${Date.now()}`;
nodeIds.objectives.push(id);
nodes.push({
id,
type: 'objective',
position: { x: COLS.obj, y: START_Y + i * ROW_HEIGHT },
data: {
type: 'objective',
label: obj.name,
configured: true,
name: obj.name,
direction: obj.direction,
weight: obj.weight,
},
});
// Connect Extractor → Objective
const extractorNodeId = nodeIds.extractors[obj.extractor_id];
if (extractorNodeId) {
edges.push({
id: `e_ext_obj_${i}`,
source: extractorNodeId,
target: id,
});
}
});
// 6. Create Constraints (connected to Extractors)
config.constraints?.forEach((con, i) => {
const id = `con_${i}_${Date.now()}`;
nodeIds.constraints.push(id);
const objY = START_Y + (config.objectives?.length || 0) * ROW_HEIGHT;
nodes.push({
id,
type: 'constraint',
position: { x: COLS.obj, y: objY + i * ROW_HEIGHT },
data: {
type: 'constraint',
label: con.name,
configured: true,
name: con.name,
operator: con.type === 'upper' ? '<=' : con.type === 'lower' ? '>=' : '==',
value: con.upper_bound ?? con.lower_bound ?? con.target,
},
});
// Connect Extractor → Constraint
if (con.extractor_id) {
const extractorNodeId = nodeIds.extractors[con.extractor_id];
if (extractorNodeId) {
edges.push({
id: `e_ext_con_${i}`,
source: extractorNodeId,
target: id,
});
}
}
});
// 7. Create Algorithm Node
if (config.optimization) {
const id = `algo_${Date.now()}`;
nodeIds.algorithm = id;
nodes.push({
id,
type: 'algorithm',
position: { x: COLS.algo, y: START_Y },
data: {
type: 'algorithm',
label: 'Algorithm',
configured: true,
method: config.optimization.sampler || 'TPE',
maxTrials: config.optimization.n_trials || 100,
},
});
// Connect Objectives → Algorithm
nodeIds.objectives.forEach((objId, i) => {
edges.push({
id: `e_obj_algo_${i}`,
source: objId,
target: id,
});
});
}
// 8. Create Surrogate Node (if enabled)
if (config.surrogate?.enabled) {
const id = `surrogate_${Date.now()}`;
nodes.push({
id,
type: 'surrogate',
position: { x: COLS.algo, y: START_Y + 150 },
data: {
type: 'surrogate',
label: 'Surrogate',
configured: true,
enabled: true,
modelType: config.surrogate.type || 'MLP',
minTrials: config.surrogate.min_trials || 20,
},
});
}
// Apply to store
set({
nodes,
edges,
selectedNode: null,
validation: { valid: false, errors: [], warnings: [] },
});
};
2.2 Parse Full Config Structure
Ensure we handle:
nx_model.sim_path/prt_path/fem_pathsolver.solution_typedesign_variables[]with all fieldsobjectives[]withextractor_id,name,direction,weightconstraints[]withextractor_id,type,upper_bound,lower_boundoptimization.sampler,n_trialssurrogate.enabled,type,min_trialspost_processing[](for future)
Phase 3: UI/UX Polish
3.1 Responsive Full-Screen Canvas
CanvasView.tsx:
export function CanvasView() {
return (
<div className="h-screen w-screen flex flex-col overflow-hidden">
{/* Minimal header */}
<header className="flex-shrink-0 h-10 bg-dark-900 border-b border-dark-700 px-4 flex items-center justify-between">
<span className="text-sm font-medium text-white">Canvas Builder</span>
<div className="flex gap-2">
<button>Templates</button>
<button>Import</button>
<button>Clear</button>
</div>
</header>
{/* Canvas fills remaining space */}
<main className="flex-1 min-h-0">
<AtomizerCanvas />
</main>
</div>
);
}
3.2 Fix Contrast Issues
Problem: White text on light blue is hard to read
Solution: Use darker backgrounds or different text colors
/* Node backgrounds */
.bg-dark-850 /* #0A1525 - dark enough for white text */
/* Avoid light blue backgrounds with white text */
/* If using blue, use dark blue (#1e3a5f) or switch to light text */
/* Specific fixes */
.node-header {
background: #0F1E32; /* dark-800 */
color: #FFFFFF;
}
.node-content {
background: #0A1525; /* dark-850 */
color: #E2E8F0; /* light gray */
}
/* Badge/pill text */
.badge-primary {
background: rgba(0, 212, 230, 0.15); /* primary with low opacity */
color: #00D4E6; /* primary-400 */
border: 1px solid rgba(0, 212, 230, 0.3);
}
3.3 Increase Font Sizes
Current vs New:
| Element | Current | New |
|---|---|---|
| Node label | 12px | 14px |
| Node detail | 10px | 12px |
| Palette item | 12px | 14px |
| Panel headers | 14px | 16px |
| Config labels | 10px | 12px |
Phase 4: Claude Integration
4.1 Fix Chat Panel Connection
Error Handling:
function ChatPanel({ onClose }: ChatPanelProps) {
const { messages, isConnected, isThinking, error } = useCanvasChat();
if (error) {
return (
<div className="flex flex-col items-center justify-center h-full p-4">
<AlertCircle className="text-red-400 mb-2" size={24} />
<p className="text-red-400 text-sm text-center">{error}</p>
<button
onClick={reconnect}
className="mt-4 px-3 py-1.5 bg-dark-700 rounded text-sm"
>
Retry Connection
</button>
</div>
);
}
// ... rest of component
}
4.2 Auto-Complete with Claude
Concept: A button that sends current canvas state to Claude and asks for suggestions.
UI:
- Button: "Complete with Claude" next to Validate
- Opens chat panel with Claude's analysis
- Claude suggests: missing nodes, connections, configuration improvements
Implementation:
// In useCanvasChat.ts
const autoCompleteWithClaude = useCallback(async () => {
const intent = toIntent();
const message = `Analyze this Canvas workflow and suggest what's missing or could be improved:
\`\`\`json
${JSON.stringify(intent, null, 2)}
\`\`\`
Please:
1. Identify any missing required components
2. Suggest extractors that should be added based on the objectives
3. Recommend connections that should be made
4. Propose any configuration improvements
Be specific and actionable.`;
await sendMessage(message);
}, [toIntent, sendMessage]);
4.3 Canvas ↔ Assistant Integration (Future)
Vision: Claude can modify the canvas through conversation.
Commands:
- "Add a displacement extractor"
- "Connect the mass extractor to objective 1"
- "Set the algorithm to CMA-ES with 200 trials"
- "Load the bracket_v3 study"
Implementation Approach:
- Define canvas manipulation actions as Claude tools
- Parse Claude responses for action intents
- Execute actions via store methods
MCP Tools (New):
canvas_add_node- Add a node of specified typecanvas_remove_node- Remove a node by IDcanvas_connect- Connect two nodescanvas_disconnect- Remove a connectioncanvas_configure- Update node configurationcanvas_load_study- Load a study into canvas
Phase 5: Testing & Validation
Test Cases
| Test | Steps | Expected |
|---|---|---|
| Load study with connections | Import → Select study → Load | All nodes + edges appear |
| Delete connection | Click edge → Press Delete | Edge removed |
| Drag & drop position | Drag node to specific spot | Node appears at drop location |
| Chat panel opens | Click chat icon | Panel opens without error |
| Full screen canvas | Open /canvas | Canvas fills window |
| Contrast readable | View all nodes | All text legible |
Build Verification
cd atomizer-dashboard/frontend
npm run build
# Must pass without errors
Files to Modify
| File | Changes |
|---|---|
useCanvasStore.ts |
Enhanced loadFromConfig, deleteEdge |
AtomizerCanvas.tsx |
Edge deletion, drag/drop fix, responsive |
CanvasView.tsx |
Full-screen layout |
ChatPanel.tsx |
Error handling, reconnect |
useCanvasChat.ts |
Auto-complete function, error state |
BaseNode.tsx |
Font sizes, contrast |
NodePalette.tsx |
Font sizes |
NodeConfigPanel.tsx |
Font sizes, contrast |
Summary
Total Effort: ~11 hours across 5 phases
Priority Order:
- Fix Atomizer Assistant (blocking)
- Fix connection deletion (blocking editing)
- Fix data loading (core functionality)
- UI/UX polish (user experience)
- Claude integration (enhancement)
Success Criteria:
- All bugs from user report fixed
- Loading a study shows ALL elements with connections
- Canvas is responsive and readable
- Chat panel works without errors
- Build passes without errors
Plan created for Ralph Loop autonomous execution.