docs: Major documentation overhaul - restructure folders, update tagline, add Getting Started guide

- Restructure docs/ folder (remove numeric prefixes):
  - 04_USER_GUIDES -> guides/
  - 05_API_REFERENCE -> api/
  - 06_PHYSICS -> physics/
  - 07_DEVELOPMENT -> development/
  - 08_ARCHIVE -> archive/
  - 09_DIAGRAMS -> diagrams/

- Replace tagline 'Talk, don't click' with 'LLM-driven optimization framework' in 9 files

- Create comprehensive docs/GETTING_STARTED.md:
  - Prerequisites and quick setup
  - Project structure overview
  - First study tutorial (Claude or manual)
  - Dashboard usage guide
  - Neural acceleration introduction

- Rewrite docs/00_INDEX.md with correct paths and modern structure

- Archive obsolete files:
  - 01_PROTOCOLS.md -> archive/historical/01_PROTOCOLS_legacy.md
  - 03_GETTING_STARTED.md -> archive/historical/
  - ATOMIZER_PODCAST_BRIEFING.md -> archive/marketing/

- Update timestamps to 2026-01-20 across all key files

- Update .gitignore to exclude docs/generated/

- Version bump: ATOMIZER_CONTEXT v1.8 -> v2.0
This commit is contained in:
2026-01-20 10:03:45 -05:00
parent 37f73cc2be
commit ea437d360e
103 changed files with 8980 additions and 327 deletions

View File

@@ -750,7 +750,7 @@ The system prompt is structured to maximize KV-cache hits:
```
[SECTION 1: STABLE - Never changes]
- Atomizer identity and capabilities
- Core principles (talk don't click)
- Core principles (LLM-driven optimization)
- Tool schemas and definitions
- Base protocol routing table
@@ -780,7 +780,7 @@ You are assisting with **Atomizer**, an LLM-first FEA optimization framework.
- Neural acceleration (600-1000x speedup)
## Principles
1. Talk, don't click - users describe goals in plain language
1. LLM-driven - users describe goals in plain language
2. Never modify master models - work on copies
3. Always validate before running
4. Document everything

View File

@@ -50,7 +50,7 @@ Implement an **Interview Mode** that systematically gathers engineering requirem
| Principle | Implementation |
|-----------|----------------|
| **Talk, don't click** | Natural conversation, not forms |
| **Conversational interface** | Natural conversation, not forms |
| **Intelligence first** | Auto-detect what's possible, ask about intent |
| **No assumptions** | Ask instead of guessing on critical decisions |
| **Adaptive depth** | Simple studies = fewer questions |

View File

@@ -0,0 +1,737 @@
# Claude + Canvas Integration V2
## The Vision
**Side-by-side LLM + Canvas** where:
1. **Claude talks → Canvas updates in real-time** (user sees nodes appear/change)
2. **User tweaks Canvas → Claude sees changes** (bi-directional sync)
3. **Full Claude Code-level power** through the dashboard chat
4. **Interview-driven study creation** entirely through chat
The user can:
- Describe what they want in natural language
- Watch the canvas build itself
- Make quick manual tweaks
- Continue the conversation with Claude seeing their changes
- Have Claude execute protocols, create files, run optimizations
---
## Current State vs Target
### What We Have Now
```
┌──────────────────┐ ┌──────────────────┐
│ Chat Panel │ │ Canvas │
│ (Power Mode) │ │ (SpecRenderer) │
├──────────────────┤ ├──────────────────┤
│ - Anthropic API │ │ - Loads spec │
│ - Write tools │ │ - User edits │
│ - spec_modified │--->│ - Auto-refresh │
│ events │ │ on event │
└──────────────────┘ └──────────────────┘
│ │
│ No real-time │
│ canvas state │
│ in Claude context │
└──────────────────────┘
```
**Gaps:**
1. Claude doesn't see current canvas state in real-time
2. No interview engine for guided study creation
3. Limited tool set (no file ops, no protocol execution)
4. No streaming for tool calls
5. Mode switching requires reconnection
### What We Want
```
┌───────────────────────────────────────────────────────────────────┐
│ ATOMIZER DASHBOARD │
├────────────────────────────┬──────────────────────────────────────┤
│ │ │
│ CHAT PANEL │ CANVAS │
│ (Atomizer Assistant) │ (SpecRenderer) │
│ │ │
│ ┌──────────────────────┐ │ ┌────────────────────────────────┐ │
│ │ "Create a bracket │ │ │ │ │
│ │ optimization with │ │ │ [DV: thickness] │ │
│ │ mass and stiffness" │ │ │ │ │ │
│ └──────────────────────┘ │ │ ▼ │ │
│ │ │ │ [Model Node] │ │
│ ▼ │ │ │ │ │
│ ┌──────────────────────┐ │ │ ▼ │ │
│ │ 🔧 Adding thickness │ │ │ [Ext: mass]──>[Obj: min] │ │
│ │ 🔧 Adding mass ext │◄─┼──┤ [Ext: disp]──>[Obj: min] │ │
│ │ 🔧 Adding objective │ │ │ │ │
│ │ │ │ │ (nodes appear in real-time) │ │
│ │ ✓ Study configured! │ │ │ │ │
│ └──────────────────────┘ │ └────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────┐ │ User can click any node to edit │
│ │ Claude sees the │ │ Claude sees user's edits │
│ │ canvas state and │◄─┼──────────────────────────────────────│
│ │ user's manual edits │ │ │
│ └──────────────────────┘ │ │
└────────────────────────────┴──────────────────────────────────────┘
```
---
## Architecture
### 1. WebSocket Hub (Bi-directional Sync)
```
┌─────────────────────┐
│ WebSocket Hub │
│ (Single Connection)│
└─────────┬───────────┘
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Chat Panel │ │ Canvas │ │ Spec Store │
│ │ │ │ │ │
│ - Send messages │ │ - User edits │ │ - Single source │
│ - Receive text │ │ - Node add/del │ │ of truth │
│ - See tool calls│ │ - Edge changes │ │ - Validates │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Message Types:
Client → Server:
{ type: "message", content: "..." } # Chat message
{ type: "canvas_edit", patch: {...} } # User made canvas change
{ type: "set_study", study_id: "..." } # Switch study
{ type: "ping" } # Heartbeat
Server → Client:
{ type: "text", content: "...", done: false } # Streaming text
{ type: "tool_start", tool: "...", input: {...} }
{ type: "tool_result", tool: "...", result: "..." }
{ type: "spec_updated", spec: {...} } # Full spec after change
{ type: "canvas_patch", patch: {...} } # Incremental update
{ type: "done" } # Response complete
{ type: "pong" } # Heartbeat response
```
### 2. Enhanced Claude Agent
The `AtomizerClaudeAgent` needs to be more like **Claude Code**:
```python
class AtomizerClaudeAgent:
"""Full-power Claude agent with Claude Code-like capabilities"""
def __init__(self, study_id: Optional[str] = None):
self.client = anthropic.Anthropic()
self.study_id = study_id
self.spec_store = SpecStore(study_id) # Real-time spec access
self.interview_state = None # For guided creation
self.tools = self._define_full_tools()
async def chat_stream(
self,
message: str,
conversation: List[Dict],
canvas_state: Optional[Dict] = None # Current canvas from frontend
) -> AsyncGenerator[Dict, None]:
"""Stream responses with tool calls"""
# Build context with current canvas state
system = self._build_system_prompt(canvas_state)
# Stream the response
with self.client.messages.stream(
model="claude-sonnet-4-20250514",
max_tokens=8192,
system=system,
messages=conversation + [{"role": "user", "content": message}],
tools=self.tools
) as stream:
for event in stream:
if event.type == "content_block_delta":
if event.delta.type == "text_delta":
yield {"type": "text", "content": event.delta.text}
elif event.type == "content_block_start":
if event.content_block.type == "tool_use":
yield {
"type": "tool_start",
"tool": event.content_block.name,
"input": {} # Will be completed
}
# Handle tool calls after stream
response = stream.get_final_message()
for block in response.content:
if block.type == "tool_use":
result = await self._execute_tool(block.name, block.input)
yield {
"type": "tool_result",
"tool": block.name,
"result": result["result"],
"spec_changed": result.get("spec_changed", False)
}
# If spec changed, send the updated spec
if result.get("spec_changed"):
yield {
"type": "spec_updated",
"spec": self.spec_store.get_dict()
}
```
### 3. Full Tool Set
Claude needs more tools to match Claude Code power:
```python
FULL_TOOLS = [
# === READ TOOLS ===
"read_study_config", # Read atomizer_spec.json
"query_trials", # Query optimization database
"list_studies", # List available studies
"read_file", # Read any file in study
"list_files", # List files in study directory
"read_nx_expressions", # Get NX model expressions
# === WRITE TOOLS (Spec Modification) ===
"add_design_variable", # Add DV to spec
"add_extractor", # Add extractor (built-in or custom)
"add_objective", # Add objective
"add_constraint", # Add constraint
"update_spec_field", # Update any spec field by path
"remove_node", # Remove any node by ID
"update_canvas_layout", # Reposition nodes for better layout
# === STUDY MANAGEMENT ===
"create_study", # Create new study directory + spec
"clone_study", # Clone existing study
"validate_spec", # Validate current spec
"migrate_config", # Migrate legacy config to spec v2
# === OPTIMIZATION CONTROL ===
"start_optimization", # Start optimization run
"stop_optimization", # Stop running optimization
"get_optimization_status",# Check if running, trial count
# === FILE OPERATIONS ===
"write_file", # Write file to study directory
"create_directory", # Create directory in study
# === NX INTEGRATION ===
"introspect_model", # Get model info (expressions, features)
"suggest_design_vars", # AI-suggest design variables from model
# === INTERVIEW/GUIDED CREATION ===
"start_interview", # Begin guided study creation
"process_answer", # Process user's interview answer
"get_interview_state", # Get current interview progress
]
```
### 4. Interview Engine Integration
The interview happens **through chat**, not a separate UI:
```python
class InterviewEngine:
"""Guided study creation through conversation"""
PHASES = [
("model", "Let's set up your model. What's the path to your NX simulation file?"),
("objectives", "What do you want to optimize? (e.g., minimize mass, minimize displacement)"),
("design_vars", "Which parameters can I vary? I can suggest some based on your model."),
("constraints", "Any constraints to respect? (e.g., max stress, min frequency)"),
("method", "I recommend {method} for this problem. Should I configure it?"),
("review", "Here's the complete configuration. Ready to create the study?"),
]
def __init__(self, spec_store: SpecStore):
self.spec_store = spec_store
self.current_phase = 0
self.collected_data = {}
def get_current_question(self) -> str:
phase_name, question = self.PHASES[self.current_phase]
# Customize question based on collected data
if phase_name == "method":
method = self._recommend_method()
question = question.format(method=method)
return question
def process_answer(self, answer: str) -> Dict:
"""Process answer and build spec incrementally"""
phase_name, _ = self.PHASES[self.current_phase]
# Extract structured data from answer
extracted = self._extract_for_phase(phase_name, answer)
self.collected_data[phase_name] = extracted
# Update spec with extracted data
spec_update = self._apply_to_spec(phase_name, extracted)
# Advance to next phase
self.current_phase += 1
return {
"phase": phase_name,
"extracted": extracted,
"spec_update": spec_update,
"next_question": self.get_current_question() if self.current_phase < len(self.PHASES) else None,
"complete": self.current_phase >= len(self.PHASES)
}
```
Claude uses the interview through tools:
```python
async def _tool_start_interview(self, params: Dict) -> str:
"""Start guided study creation"""
self.interview_state = InterviewEngine(self.spec_store)
return {
"status": "started",
"first_question": self.interview_state.get_current_question()
}
async def _tool_process_answer(self, params: Dict) -> str:
"""Process user's answer in interview"""
if not self.interview_state:
return {"error": "No interview in progress"}
result = self.interview_state.process_answer(params["answer"])
if result["spec_update"]:
# Spec was updated - this will trigger canvas update
return {
"status": "updated",
"spec_changed": True,
"next_question": result["next_question"],
"complete": result["complete"]
}
return result
```
---
## Frontend Implementation
### 1. Unified WebSocket Hook
```typescript
// hooks/useAtomizerSocket.ts
export function useAtomizerSocket(studyId: string | undefined) {
const [spec, setSpec] = useState<AtomizerSpec | null>(null);
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [isThinking, setIsThinking] = useState(false);
const [currentTool, setCurrentTool] = useState<string | null>(null);
const ws = useRef<WebSocket | null>(null);
// Single WebSocket connection for everything
useEffect(() => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = import.meta.env.DEV ? 'localhost:8001' : window.location.host;
ws.current = new WebSocket(`${protocol}//${host}/api/atomizer/ws`);
ws.current.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'text':
// Streaming text from Claude
setMessages(prev => {
const last = prev[prev.length - 1];
if (last?.role === 'assistant' && !last.complete) {
return [...prev.slice(0, -1), {
...last,
content: last.content + data.content
}];
}
return [...prev, {
id: Date.now().toString(),
role: 'assistant',
content: data.content,
complete: false
}];
});
break;
case 'tool_start':
setCurrentTool(data.tool);
// Add tool indicator to chat
setMessages(prev => [...prev, {
id: Date.now().toString(),
role: 'tool',
tool: data.tool,
status: 'running'
}]);
break;
case 'tool_result':
setCurrentTool(null);
// Update tool message with result
setMessages(prev => prev.map(m =>
m.role === 'tool' && m.tool === data.tool && m.status === 'running'
? { ...m, status: 'complete', result: data.result }
: m
));
break;
case 'spec_updated':
// Canvas gets the new spec - this is the magic!
setSpec(data.spec);
break;
case 'done':
setIsThinking(false);
// Mark last message as complete
setMessages(prev => prev.map((m, i) =>
i === prev.length - 1 ? { ...m, complete: true } : m
));
break;
}
};
// Set study context
if (studyId) {
ws.current.onopen = () => {
ws.current?.send(JSON.stringify({
type: 'set_study',
study_id: studyId
}));
};
}
return () => ws.current?.close();
}, [studyId]);
// Send message
const sendMessage = useCallback((content: string) => {
if (!ws.current) return;
setIsThinking(true);
setMessages(prev => [...prev, {
id: Date.now().toString(),
role: 'user',
content
}]);
ws.current.send(JSON.stringify({
type: 'message',
content
}));
}, []);
// Notify Claude about canvas edits
const notifyCanvasEdit = useCallback((patch: any) => {
ws.current?.send(JSON.stringify({
type: 'canvas_edit',
patch
}));
}, []);
return {
spec,
messages,
isThinking,
currentTool,
sendMessage,
notifyCanvasEdit
};
}
```
### 2. Integrated Canvas View
```typescript
// pages/CanvasView.tsx (revised)
export function CanvasView() {
const { '*': studyId } = useParams();
// Single hook manages everything
const {
spec,
messages,
isThinking,
currentTool,
sendMessage,
notifyCanvasEdit
} = useAtomizerSocket(studyId);
// When user edits canvas, notify Claude
const handleSpecChange = useCallback((newSpec: AtomizerSpec) => {
// This is called by SpecRenderer when user makes edits
notifyCanvasEdit({
type: 'spec_replace',
spec: newSpec
});
}, [notifyCanvasEdit]);
return (
<div className="h-screen flex">
{/* Canvas - receives spec from WebSocket */}
<div className="flex-1">
<SpecRenderer
spec={spec}
onChange={handleSpecChange} // User edits flow back
highlightNode={currentTool ? getAffectedNode(currentTool) : undefined}
/>
</div>
{/* Chat Panel */}
<div className="w-96 border-l">
<ChatPanel
messages={messages}
isThinking={isThinking}
currentTool={currentTool}
onSend={sendMessage}
/>
</div>
</div>
);
}
```
### 3. Visual Feedback for Tool Calls
When Claude calls a tool, the canvas shows visual feedback:
```typescript
// components/canvas/SpecRenderer.tsx
function SpecRenderer({ spec, highlightNode, onChange }) {
// When a tool is targeting a node, highlight it
const getNodeStyle = (nodeId: string) => {
if (highlightNode === nodeId) {
return {
boxShadow: '0 0 0 3px #f59e0b', // Amber glow
animation: 'pulse 1s infinite'
};
}
return {};
};
// When new nodes are added, animate them
const [newNodes, setNewNodes] = useState<Set<string>>(new Set());
useEffect(() => {
if (spec) {
const currentIds = new Set([
...spec.design_variables.map(d => d.id),
...spec.extractors.map(e => e.id),
...spec.objectives.map(o => o.id),
...spec.constraints.map(c => c.id)
]);
// Find truly new nodes
const added = [...currentIds].filter(id => !prevIds.current.has(id));
if (added.length > 0) {
setNewNodes(new Set(added));
setTimeout(() => setNewNodes(new Set()), 1000); // Clear animation
}
prevIds.current = currentIds;
}
}, [spec]);
// Render with animations
return (
<ReactFlow nodes={nodes.map(n => ({
...n,
style: {
...getNodeStyle(n.id),
...(newNodes.has(n.id) ? { animation: 'slideIn 0.5s ease-out' } : {})
}
}))} />
);
}
```
---
## User Experience Flow
### Flow 1: Create Study Through Chat
```
User: "Create a bracket optimization. I want to minimize mass while keeping
stress below 200 MPa. The thickness can vary from 2mm to 10mm."
Claude: [Internal: Parse intent, no interview needed - user gave full spec]
🔧 Creating study "bracket_optimization"...
🔧 Adding design variable: thickness [2mm - 10mm]
🔧 Adding extractor: mass (BDF mass extraction)
🔧 Adding extractor: max_stress (von Mises stress)
🔧 Adding objective: minimize mass
🔧 Adding constraint: stress <= 200 MPa
✓ Study created! I've configured:
- 1 design variable (thickness)
- 2 extractors (mass, stress)
- 1 objective (minimize mass)
- 1 constraint (max stress)
The canvas shows your optimization setup. You can:
- Click any node to adjust settings
- Ask me to add more variables or constraints
- Say "run" to start optimization
[Canvas shows: DV→Model→Extractors→Objectives/Constraints, all connected]
```
### Flow 2: Interview-Guided Creation
```
User: "Help me set up a new optimization"
Claude: Let's set up your optimization step by step.
First, what's the path to your NX simulation file?
(You can drag & drop or type the path)
User: "studies/M1_Mirror/model.sim"
Claude: 🔧 Loading model...
[tool: introspect_model]
Found your model! It has 15 expressions. I noticed these look like
good design variables:
- rib_thickness (current: 8mm)
- mirror_depth (current: 50mm)
- support_angle (current: 45°)
What are you trying to optimize?
- Minimize mass?
- Minimize wavefront error (WFE)?
- Both? (I can set up multi-objective)
[Canvas shows: Model node appears]
User: "minimize WFE and mass"
Claude: 🔧 Adding Zernike WFE extractor
🔧 Adding mass extractor
🔧 Adding objectives (NSGA-II multi-objective)
Got it - multi-objective optimization for WFE and mass.
Which parameters should I vary?
I suggest: rib_thickness, mirror_depth, support_angle
[Canvas shows: Model→WFE Extractor→Obj, Model→Mass Extractor→Obj]
User: "Yes, use those, but keep support_angle fixed"
Claude: 🔧 Adding design variable: rib_thickness [4mm - 16mm]
🔧 Adding design variable: mirror_depth [30mm - 80mm]
Perfect. Any constraints?
- Maximum mass?
- Maximum stress?
- Minimum frequency?
[Canvas shows: 2 DVs connected to Model]
User: "max stress 150 MPa"
Claude: 🔧 Adding stress extractor
🔧 Adding constraint: stress <= 150 MPa
✓ Configuration complete! Ready to optimize?
[Canvas shows: Full optimization flow]
```
### Flow 3: User Edits Canvas, Claude Sees
```
[User clicks thickness DV node, changes max from 10mm to 15mm]
[WebSocket sends: { type: "canvas_edit", patch: { path: "design_variables.0.bounds.max", value: 15 } }]
Claude: [Receives canvas edit notification in context]
User: "Is this range reasonable?"
Claude: I see you changed thickness max to 15mm. That's quite thick for a
bracket - typical ranges are 2-10mm.
With 15mm max, you might see:
- Higher mass designs explored
- Potentially better stiffness
- Longer optimization time (larger search space)
Want me to adjust or keep it?
```
---
## Implementation Plan
### Phase 1: Unified WebSocket (1 week)
1. **Backend**: Create `/api/atomizer/ws` endpoint
- Single connection for chat + spec sync
- Streaming response support
- Canvas edit notifications
2. **Frontend**: Create `useAtomizerSocket` hook
- Replaces `useChat` + `useSpecWebSocket`
- Single source of truth for spec state
3. **Integration**: Wire SpecRenderer to socket
- Receive spec updates from Claude's tools
- Send edit notifications back
### Phase 2: Enhanced Tools (1 week)
1. Add remaining write tools
2. Implement `introspect_model` for NX expression discovery
3. Add `create_study` for new study creation
4. Add file operation tools
### Phase 3: Interview Engine (1 week)
1. Implement `InterviewEngine` class
2. Add interview tools to Claude
3. Test guided creation flow
4. Add smart defaults and recommendations
### Phase 4: Polish (1 week)
1. Visual feedback for tool calls
2. Node highlight during modification
3. Animation for new nodes
4. Error recovery and reconnection
5. Performance optimization
---
## Success Metrics
1. **Creation Time**: User can create complete study in <3 minutes through chat
2. **Edit Latency**: Canvas updates within 200ms of Claude's tool call
3. **Sync Reliability**: 100% of user edits reflected in Claude's context
4. **Interview Success**: 90% of studies created through interview are valid
---
## Key Differences from Current Implementation
| Current | Target |
|---------|--------|
| Separate chat/canvas WebSockets | Single unified WebSocket |
| Claude doesn't see canvas state | Real-time canvas state in context |
| Manual spec refresh | Automatic spec push on changes |
| No interview engine | Guided creation through chat |
| Limited tools | Full Claude Code-like tool set |
| Mode switching breaks connection | Seamless power mode |
---
*This is the architecture that makes Atomizer truly powerful - where Claude and Canvas work together as one system.*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,693 @@
# Dashboard Claude Code Integration Plan
**Date**: January 16, 2026
**Status**: 🟢 IMPLEMENTED
**Priority**: CRITICAL
**Implemented**: January 16, 2026
---
## Problem Statement
The dashboard chat assistant is **fundamentally underpowered** compared to Claude Code CLI. Users expect the same level of intelligence, proactivity, and capability when interacting with the dashboard as they get from the terminal.
### Current Experience (Terminal - Claude Code CLI)
```
User: "Add 10 new design variables to the M1 mirror study"
Claude Code:
1. Reads optimization_config.json
2. Understands the current structure
3. Adds 10 variables with intelligent defaults
4. ACTUALLY MODIFIES the file
5. Shows the diff
6. Can immediately run/test
```
### Current Experience (Dashboard Chat)
```
User: "Add 10 new design variables"
Dashboard Chat:
1. Calls MCP tool canvas_add_node
2. Returns JSON instruction
3. Frontend SHOULD apply it but doesn't
4. Nothing visible happens
5. User frustrated
```
---
## Root Cause Analysis
### Issue 1: MCP Tools Don't Actually Modify Anything
The current MCP tools (`canvas_add_node`, etc.) just return instructions like:
```json
{
"success": true,
"modification": {
"action": "add_node",
"nodeType": "designVar",
"data": {...}
}
}
```
The **frontend is supposed to receive and apply these**, but:
- WebSocket message handling may not process tool results
- No automatic application of modifications
- User sees "success" message but nothing changes
### Issue 2: Claude API vs Claude Code CLI
| Capability | Claude API (Dashboard) | Claude Code CLI (Terminal) |
|------------|------------------------|---------------------------|
| Read files | Via MCP tool | Native |
| Write files | Via MCP tool (limited) | Native |
| Run commands | Via MCP tool (limited) | Native |
| Edit in place | NO | YES |
| Git operations | NO | YES |
| Multi-step reasoning | Limited | Full |
| Tool chaining | Awkward | Natural |
| Context window | 200k | Unlimited (summarization) |
### Issue 3: Model Capability Gap
Dashboard uses Claude API (likely Sonnet or Haiku for cost). Terminal uses **Opus 4.5** with full Claude Code capabilities.
---
## Proposed Solution: Claude Code CLI Backend
Instead of MCP tools calling Python scripts, **spawn actual Claude Code CLI sessions** in the backend that have full power.
### Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ DASHBOARD FRONTEND │
├─────────────────────────────────────────────────────────────────┤
│ Canvas Builder │ Chat Panel │ Study Views │ Results │
└────────┬────────────────┬─────────────────────────────────────┬─┘
│ │ │
│ WebSocket │ REST API │
▼ ▼ │
┌─────────────────────────────────────────────────────────────────┐
│ BACKEND (FastAPI) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CLAUDE CODE SESSION MANAGER │ │
│ │ │ │
│ │ - Spawns claude CLI processes │ │
│ │ - Maintains conversation context │ │
│ │ - Streams output back to frontend │ │
│ │ - Has FULL Atomizer codebase access │ │
│ │ - Uses Opus 4.5 model │ │
│ │ - Can edit files, run commands, modify studies │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ATOMIZER CODEBASE │ │
│ │ │ │
│ │ studies/ optimization_engine/ │ │
│ │ M1_Mirror/ extractors/ │ │
│ │ optimization_config.json runner.py │ │
│ │ run_optimization.py ... │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Key Changes
1. **Backend spawns Claude Code CLI** instead of calling Claude API
2. **Full file system access** - Claude can read/write any file
3. **Full command execution** - Run Python, git, npm, etc.
4. **Opus 4.5 model** - Same intelligence as terminal
5. **Stream output** - Real-time feedback to user
6. **Canvas sync** - After Claude modifies files, canvas reloads from config
---
## Implementation Plan
### Phase 1: Claude Code CLI Session Manager
**File**: `atomizer-dashboard/backend/api/services/claude_code_session.py`
```python
"""
Claude Code CLI Session Manager
Spawns actual Claude Code CLI processes with full Atomizer access.
This gives dashboard users the same power as terminal users.
"""
import asyncio
import json
import os
import subprocess
from pathlib import Path
from typing import AsyncGenerator, Dict, List, Optional
ATOMIZER_ROOT = Path(__file__).parent.parent.parent.parent.parent
class ClaudeCodeSession:
"""
Manages a Claude Code CLI session.
Unlike MCP tools, this spawns the actual claude CLI which has:
- Full file system access
- Full command execution
- Opus 4.5 model
- All Claude Code capabilities
"""
def __init__(self, session_id: str, study_id: Optional[str] = None):
self.session_id = session_id
self.study_id = study_id
self.canvas_state: Optional[Dict] = None # Current canvas state from frontend
self.working_dir = ATOMIZER_ROOT
if study_id:
study_path = ATOMIZER_ROOT / "studies" / study_id
if study_path.exists():
self.working_dir = study_path
def set_canvas_state(self, canvas_state: Dict):
"""Update canvas state from frontend"""
self.canvas_state = canvas_state
async def send_message(self, message: str) -> AsyncGenerator[str, None]:
"""
Send message to Claude Code CLI and stream response.
Uses claude CLI with:
- --print for output
- --dangerously-skip-permissions for full access (controlled environment)
- Runs from Atomizer root to get CLAUDE.md context automatically
- Study-specific context injected into prompt
"""
# Build study-specific context
study_context = self._build_study_context() if self.study_id else ""
# The user's message with study context prepended
full_message = f"""## Current Study Context
{study_context}
## User Request
{message}
Remember: You have FULL power to edit files. Make the actual changes, don't just describe them."""
# Write prompt to a temp file (better than stdin for complex prompts)
prompt_file = ATOMIZER_ROOT / f".claude-prompt-{self.session_id}.md"
prompt_file.write_text(full_message)
try:
# Spawn claude CLI from ATOMIZER_ROOT so it picks up CLAUDE.md
# This gives it full Atomizer context automatically
process = await asyncio.create_subprocess_exec(
"claude",
"--print",
"--dangerously-skip-permissions", # Full access in controlled env
"-p", str(prompt_file), # Read prompt from file
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=str(ATOMIZER_ROOT), # Run from root to get CLAUDE.md
env={
**os.environ,
"ATOMIZER_STUDY": self.study_id or "",
"ATOMIZER_STUDY_PATH": str(self.working_dir),
}
)
# Stream output
stdout, stderr = await process.communicate()
if stdout:
yield stdout.decode()
if stderr and process.returncode != 0:
yield f"\n[Error]: {stderr.decode()}"
finally:
# Clean up prompt file
if prompt_file.exists():
prompt_file.unlink()
def _build_system_prompt(self) -> str:
"""Build Atomizer-aware system prompt with full context"""
# Load CLAUDE.md for Atomizer system instructions
claude_md_path = ATOMIZER_ROOT / "CLAUDE.md"
claude_md_content = ""
if claude_md_path.exists():
claude_md_content = claude_md_path.read_text()
# Load study-specific context
study_context = ""
if self.study_id:
study_context = self._build_study_context()
prompt = f"""# Atomizer Dashboard Assistant
You are running as the Atomizer Dashboard Assistant with FULL Claude Code CLI capabilities.
You have the same power as a terminal Claude Code session.
## Atomizer System Instructions
{claude_md_content[:8000]} # Truncate if too long
## Your Capabilities
You can and MUST:
- Read and EDIT any file in the codebase
- Modify optimization_config.json directly
- Update run_optimization.py
- Run Python scripts
- Execute git commands
- Create new studies
- Modify existing studies
When the user asks to add design variables, objectives, or other config changes:
1. Read the current config file
2. Make the actual modifications using Edit tool
3. Save the file
4. Report what you changed with a diff
DO NOT just return instructions - ACTUALLY MAKE THE CHANGES.
## Current Context
**Atomizer Root**: {ATOMIZER_ROOT}
**Working Directory**: {self.working_dir}
{study_context}
## Important Paths
- Studies: {ATOMIZER_ROOT / 'studies'}
- Extractors: {ATOMIZER_ROOT / 'optimization_engine' / 'extractors'}
- Protocols: {ATOMIZER_ROOT / 'docs' / 'protocols'}
## After Making Changes
After modifying any study files:
1. Confirm the changes were saved
2. Show the relevant diff
3. The dashboard canvas will auto-refresh to reflect your changes
"""
return prompt
def _build_study_context(self) -> str:
"""Build detailed context for the active study"""
context = f"## Active Study: {self.study_id}\n\n"
# Find and read optimization_config.json
config_path = self.working_dir / "1_setup" / "optimization_config.json"
if not config_path.exists():
config_path = self.working_dir / "optimization_config.json"
if config_path.exists():
import json
try:
config = json.loads(config_path.read_text())
context += f"**Config File**: `{config_path}`\n\n"
# Design variables summary
dvs = config.get("design_variables", [])
if dvs:
context += "### Design Variables\n\n"
context += "| Name | Min | Max | Baseline | Unit |\n"
context += "|------|-----|-----|----------|------|\n"
for dv in dvs[:15]:
name = dv.get("name", dv.get("expression_name", "?"))
min_v = dv.get("min", dv.get("lower", "?"))
max_v = dv.get("max", dv.get("upper", "?"))
baseline = dv.get("baseline", "-")
unit = dv.get("units", dv.get("unit", "-"))
context += f"| {name} | {min_v} | {max_v} | {baseline} | {unit} |\n"
if len(dvs) > 15:
context += f"\n*... and {len(dvs) - 15} more*\n"
context += "\n"
# Objectives
objs = config.get("objectives", [])
if objs:
context += "### Objectives\n\n"
for obj in objs:
name = obj.get("name", "?")
direction = obj.get("direction", "minimize")
weight = obj.get("weight", 1)
context += f"- **{name}**: {direction} (weight: {weight})\n"
context += "\n"
# Extraction method (for Zernike)
ext_method = config.get("extraction_method", {})
if ext_method:
context += "### Extraction Method\n\n"
context += f"- Type: {ext_method.get('type', '?')}\n"
context += f"- Class: {ext_method.get('class', '?')}\n"
if ext_method.get("inner_radius"):
context += f"- Inner Radius: {ext_method.get('inner_radius')}\n"
context += "\n"
# Zernike settings
zernike = config.get("zernike_settings", {})
if zernike:
context += "### Zernike Settings\n\n"
context += f"- Modes: {zernike.get('n_modes', '?')}\n"
context += f"- Filter Low Orders: {zernike.get('filter_low_orders', '?')}\n"
context += f"- Subcases: {zernike.get('subcases', [])}\n"
context += "\n"
# Algorithm
method = config.get("method", config.get("optimization", {}).get("sampler", "TPE"))
max_trials = config.get("max_trials", config.get("optimization", {}).get("n_trials", 100))
context += f"### Algorithm\n\n"
context += f"- Method: {method}\n"
context += f"- Max Trials: {max_trials}\n\n"
except Exception as e:
context += f"*Error reading config: {e}*\n\n"
else:
context += "*No optimization_config.json found*\n\n"
# Check for run_optimization.py
run_opt_path = self.working_dir / "run_optimization.py"
if run_opt_path.exists():
context += f"**Run Script**: `{run_opt_path}` (exists)\n\n"
# Check results
db_path = self.working_dir / "3_results" / "study.db"
if db_path.exists():
context += "**Results Database**: exists\n"
# Could query trial count here
else:
context += "**Results Database**: not found (no optimization run yet)\n"
return context
```
### Phase 2: WebSocket Handler for Claude Code
**File**: `atomizer-dashboard/backend/api/routes/claude_code.py`
```python
"""
Claude Code WebSocket Routes
Provides WebSocket endpoint that connects to actual Claude Code CLI.
"""
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from api.services.claude_code_session import ClaudeCodeSession
import uuid
router = APIRouter()
# Active sessions
sessions: Dict[str, ClaudeCodeSession] = {}
@router.websocket("/ws/{study_id}")
async def claude_code_websocket(websocket: WebSocket, study_id: str = None):
"""
WebSocket for full Claude Code CLI access.
This gives dashboard users the SAME power as terminal users.
"""
await websocket.accept()
session_id = str(uuid.uuid4())[:8]
session = ClaudeCodeSession(session_id, study_id)
sessions[session_id] = session
try:
while True:
data = await websocket.receive_json()
if data.get("type") == "message":
content = data.get("content", "")
# Stream response from Claude Code CLI
async for chunk in session.send_message(content):
await websocket.send_json({
"type": "text",
"content": chunk,
})
await websocket.send_json({"type": "done"})
# After response, trigger canvas refresh
await websocket.send_json({
"type": "refresh_canvas",
"study_id": study_id,
})
except WebSocketDisconnect:
sessions.pop(session_id, None)
```
### Phase 3: Frontend - Use Claude Code Endpoint
**File**: `atomizer-dashboard/frontend/src/hooks/useClaudeCode.ts`
```typescript
/**
* Hook for Claude Code CLI integration
*
* Connects to backend that spawns actual Claude Code CLI processes.
* This gives full power: file editing, command execution, etc.
*/
export function useClaudeCode(studyId?: string) {
const [messages, setMessages] = useState<Message[]>([]);
const [isThinking, setIsThinking] = useState(false);
const wsRef = useRef<WebSocket | null>(null);
// Reload canvas after Claude makes changes
const { loadFromConfig } = useCanvasStore();
useEffect(() => {
// Connect to Claude Code WebSocket
const ws = new WebSocket(`ws://${location.host}/api/claude-code/ws/${studyId || ''}`);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'text') {
// Stream Claude's response
appendToLastMessage(data.content);
}
else if (data.type === 'done') {
setIsThinking(false);
}
else if (data.type === 'refresh_canvas') {
// Claude made file changes - reload canvas from config
reloadCanvasFromStudy(data.study_id);
}
};
wsRef.current = ws;
return () => ws.close();
}, [studyId]);
const sendMessage = async (content: string) => {
setIsThinking(true);
addMessage({ role: 'user', content });
addMessage({ role: 'assistant', content: '', isStreaming: true });
wsRef.current?.send(JSON.stringify({
type: 'message',
content,
}));
};
return { messages, isThinking, sendMessage };
}
```
### Phase 4: Canvas Auto-Refresh
When Claude modifies `optimization_config.json`, the canvas should automatically reload:
```typescript
// In AtomizerCanvas.tsx or useCanvasChat.ts
const reloadCanvasFromStudy = async (studyId: string) => {
// Fetch fresh config from backend
const response = await fetch(`/api/studies/${studyId}/config`);
const config = await response.json();
// Reload canvas
loadFromConfig(config);
// Notify user
showNotification('Canvas updated with Claude\'s changes');
};
```
### Phase 5: Smart Prompting for Canvas Context
When user sends a message from canvas view, include canvas state:
```typescript
const sendCanvasMessage = (userMessage: string) => {
const canvasContext = generateCanvasMarkdown();
const enrichedMessage = `
## Current Canvas State
${canvasContext}
## User Request
${userMessage}
When making changes, modify the actual optimization_config.json file.
After changes, the canvas will auto-refresh.
`;
sendMessage(enrichedMessage);
};
```
---
## Expected Behavior After Implementation
### Example 1: Add Design Variables
```
User: "Add 10 new design variables for hole diameters, range 5-25mm"
Claude Code (in dashboard):
1. Reads studies/M1_Mirror/.../optimization_config.json
2. Adds 10 entries to design_variables array:
- hole_diameter_1: [5, 25] mm
- hole_diameter_2: [5, 25] mm
- ... (10 total)
3. WRITES the file
4. Reports: "Added 10 design variables to optimization_config.json"
5. Frontend receives "refresh_canvas" signal
6. Canvas reloads and shows 10 new nodes
7. User sees actual changes
```
### Example 2: Modify Optimization
```
User: "Change the algorithm to CMA-ES with 500 trials and add a stress constraint < 200 MPa"
Claude Code (in dashboard):
1. Reads config
2. Changes method: "TPE" -> "CMA-ES"
3. Changes max_trials: 100 -> 500
4. Adds constraint: {name: "stress_limit", operator: "<=", value: 200, unit: "MPa"}
5. WRITES the file
6. Reports changes
7. Canvas refreshes with updated algorithm node and new constraint node
```
### Example 3: Complex Multi-File Changes
```
User: "Add a new Zernike extractor for the secondary mirror and connect it to a new objective"
Claude Code (in dashboard):
1. Reads config
2. Adds extractor to extractors array
3. Adds objective connected to extractor
4. If needed, modifies run_optimization.py to import new extractor
5. WRITES all modified files
6. Canvas refreshes with new extractor and objective nodes, properly connected
```
---
## Implementation Checklist
### Phase 1: Backend Claude Code Session
- [x] Create `claude_code_session.py` with session manager
- [x] Implement `send_message()` with CLI spawning
- [x] Build Atomizer-aware system prompt
- [x] Handle study context (working directory)
- [x] Stream output properly
### Phase 2: WebSocket Route
- [x] Create `/api/claude-code/ws/{study_id}` endpoint
- [x] Handle message routing
- [x] Implement `refresh_canvas` signal
- [x] Session cleanup on disconnect
### Phase 3: Frontend Hook
- [x] Create `useClaudeCode.ts` hook
- [x] Connect to Claude Code WebSocket
- [x] Handle streaming responses
- [x] Handle canvas refresh signals
### Phase 4: Canvas Auto-Refresh
- [x] Add `reloadCanvasFromStudy()` function
- [x] Wire refresh signal to canvas store
- [x] Add loading state during refresh
- [x] Show notification on refresh (system message)
### Phase 5: Chat Panel Integration
- [x] Update ChatPanel to use `useClaudeCode`
- [x] Include canvas context in messages
- [x] Add "Claude Code" indicator in UI (mode toggle)
- [x] Show when Claude is editing files
### Phase 6: Testing
- [ ] Test adding design variables
- [ ] Test modifying objectives
- [ ] Test complex multi-file changes
- [ ] Test canvas refresh after changes
- [ ] Test error handling
## Implementation Notes
### Files Created/Modified
**Backend:**
- `atomizer-dashboard/backend/api/services/claude_code_session.py` - New session manager
- `atomizer-dashboard/backend/api/routes/claude_code.py` - New WebSocket routes
- `atomizer-dashboard/backend/api/main.py` - Added claude_code router
**Frontend:**
- `atomizer-dashboard/frontend/src/hooks/useClaudeCode.ts` - New hook for Claude Code CLI
- `atomizer-dashboard/frontend/src/components/canvas/AtomizerCanvas.tsx` - Added mode toggle
- `atomizer-dashboard/frontend/src/components/chat/ChatMessage.tsx` - Added system message support
---
## Security Considerations
Claude Code CLI with `--dangerously-skip-permissions` has full system access. Mitigations:
1. **Sandboxed environment**: Dashboard runs on user's machine, not public server
2. **Study-scoped working directory**: Claude starts in study folder
3. **Audit logging**: Log all file modifications
4. **User confirmation**: Option to require approval for destructive operations
---
## Cost Considerations
Using Opus 4.5 via Claude Code CLI is more expensive than Sonnet API. Options:
1. **Default to Sonnet, upgrade on request**: "Use full power mode" button
2. **Per-session token budget**: Warn user when approaching limit
3. **Cache common operations**: Pre-generate responses for common tasks
---
## Success Criteria
1. **Parity with terminal**: Dashboard chat can do everything Claude Code CLI can
2. **Real modifications**: Files actually change, not just instructions
3. **Canvas sync**: Canvas reflects file changes immediately
4. **Intelligent defaults**: Claude makes smart choices without asking clarifying questions
5. **Proactive behavior**: Claude anticipates needs and handles edge cases
---
*This document to be implemented by Claude Code CLI*

View File

@@ -0,0 +1,863 @@
# SaaS-Level Atomizer Roadmap (Revised)
## Executive Summary
This roadmap transforms Atomizer into a **SaaS-grade, LLM-assisted structural optimization platform** with the core innovation being **side-by-side Claude + Canvas** integration where:
1. **Claude talks → Canvas updates in real-time** (user sees nodes appear/change)
2. **User tweaks Canvas → Claude sees changes** (bi-directional sync)
3. **Full Claude Code-level power** through the dashboard chat
4. **Interview-driven study creation** entirely through conversation
**Vision**: An engineer opens Atomizer, describes their optimization goal, watches the canvas build itself, makes quick tweaks, and starts optimization—all through natural conversation with full visual feedback.
---
## The Core Innovation: Unified Claude + Canvas
The power of Atomizer is the **side-by-side experience**:
```
┌───────────────────────────────────────────────────────────────────┐
│ ATOMIZER DASHBOARD │
├────────────────────────────┬──────────────────────────────────────┤
│ CHAT PANEL │ CANVAS │
│ (Atomizer Assistant) │ (SpecRenderer) │
│ │ │
│ "Create a bracket │ [DV: thickness] │
│ optimization with │ │ │
│ mass and stiffness" │ ▼ │
│ │ │ [Model Node] │
│ ▼ │ │ │
│ 🔧 Adding thickness │ ▼ │
│ 🔧 Adding mass ext ◄───┼──►[Ext: mass]──>[Obj: min mass] │
│ 🔧 Adding stiffness ◄───┼──►[Ext: disp]──>[Obj: min disp] │
│ ✓ Study configured! │ │
│ │ (nodes appear in real-time) │
│ User clicks a node ───────┼──► Claude sees the edit │
│ │ │
└────────────────────────────┴──────────────────────────────────────┘
```
---
## Current State vs Target
### What We Have
| Component | Status | Notes |
|-----------|--------|-------|
| Power Mode WebSocket | ✅ Implemented | `/ws/power` endpoint with write tools |
| Write Tools | ✅ Implemented | add_design_variable, add_extractor, etc. |
| spec_modified Events | ✅ Implemented | Frontend receives notifications |
| Canvas Auto-reload | ✅ Implemented | Triggers on spec_modified |
| Streaming Responses | ❌ Missing | Currently waits for full response |
| Canvas State → Claude | ❌ Missing | Claude doesn't see current canvas |
| Interview Engine | ❌ Missing | No guided creation |
| Unified WebSocket | ❌ Missing | Separate connections for chat/spec |
### Target Architecture
```
┌─────────────────────┐
│ Unified WebSocket │
│ /api/atomizer/ws │
└─────────┬───────────┘
Bi-directional Sync
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Chat Panel │ │ Canvas │ │ Spec Store │
│ │ │ │ │ │
│ Send messages │ │ User edits → │ │ Single source │
│ Stream text │ │ Notify Claude │ │ of truth │
│ See tool calls │ │ Receive updates │ │ Validates all │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
---
## Phase 1: Unified WebSocket Hub (1-2 weeks)
### Goal: Single connection for chat + canvas + spec sync
### 1.1 Backend: Unified WebSocket Endpoint
```python
# atomizer-dashboard/backend/api/routes/atomizer_ws.py
@router.websocket("/api/atomizer/ws")
async def atomizer_websocket(websocket: WebSocket):
"""
Unified WebSocket for Atomizer Dashboard.
Handles:
- Chat messages with streaming responses
- Spec modifications with real-time canvas updates
- Canvas edit notifications from user
- Study context switching
"""
await websocket.accept()
agent = AtomizerClaudeAgent()
conversation: List[Dict] = []
current_spec: Optional[Dict] = None
try:
while True:
data = await websocket.receive_json()
if data["type"] == "message":
# Chat message - stream response
async for event in agent.chat_stream(
message=data["content"],
conversation=conversation,
canvas_state=current_spec # Claude sees current canvas!
):
await websocket.send_json(event)
# If spec changed, update our local copy
if event.get("type") == "spec_updated":
current_spec = event["spec"]
elif data["type"] == "canvas_edit":
# User made a manual edit - update spec and tell Claude
current_spec = apply_patch(current_spec, data["patch"])
# Next message to Claude will include updated spec
elif data["type"] == "set_study":
# Switch study context
agent.set_study(data["study_id"])
current_spec = agent.load_spec()
await websocket.send_json({
"type": "spec_updated",
"spec": current_spec
})
except WebSocketDisconnect:
pass
```
### 1.2 Enhanced Claude Agent with Streaming
```python
class AtomizerClaudeAgent:
"""Full-power Claude agent with Claude Code-like capabilities"""
async def chat_stream(
self,
message: str,
conversation: List[Dict],
canvas_state: Optional[Dict] = None
) -> AsyncGenerator[Dict, None]:
"""Stream responses with tool calls"""
# Build system prompt with current canvas state
system = self._build_system_prompt()
if canvas_state:
system += self._format_canvas_context(canvas_state)
messages = conversation + [{"role": "user", "content": message}]
# Use streaming API
with self.client.messages.stream(
model="claude-sonnet-4-20250514",
max_tokens=8192,
system=system,
messages=messages,
tools=self.tools
) as stream:
current_text = ""
for event in stream:
if event.type == "content_block_delta":
if hasattr(event.delta, "text"):
current_text += event.delta.text
yield {"type": "text", "content": event.delta.text, "done": False}
# Get final message for tool calls
response = stream.get_final_message()
# Process tool calls
for block in response.content:
if block.type == "tool_use":
yield {"type": "tool_start", "tool": block.name, "input": block.input}
result = await self._execute_tool(block.name, block.input)
yield {"type": "tool_result", "tool": block.name, "result": result["preview"]}
if result.get("spec_changed"):
yield {"type": "spec_updated", "spec": self.spec_store.get()}
yield {"type": "done"}
def _format_canvas_context(self, spec: Dict) -> str:
"""Format current canvas state for Claude's context"""
lines = ["\n## Current Canvas State\n"]
if spec.get("design_variables"):
lines.append(f"**Design Variables ({len(spec['design_variables'])}):**")
for dv in spec["design_variables"]:
lines.append(f" - {dv['name']}: [{dv['bounds']['min']}, {dv['bounds']['max']}]")
if spec.get("extractors"):
lines.append(f"\n**Extractors ({len(spec['extractors'])}):**")
for ext in spec["extractors"]:
lines.append(f" - {ext['name']} ({ext['type']})")
if spec.get("objectives"):
lines.append(f"\n**Objectives ({len(spec['objectives'])}):**")
for obj in spec["objectives"]:
lines.append(f" - {obj['name']} ({obj['direction']})")
if spec.get("constraints"):
lines.append(f"\n**Constraints ({len(spec['constraints'])}):**")
for con in spec["constraints"]:
lines.append(f" - {con['name']} {con['operator']} {con['threshold']}")
lines.append("\nThe user can see this canvas. When you modify it, they see changes in real-time.")
return "\n".join(lines)
```
### 1.3 Frontend: Unified Hook
```typescript
// hooks/useAtomizerSocket.ts
export function useAtomizerSocket(studyId?: string) {
const [spec, setSpec] = useState<AtomizerSpec | null>(null);
const [messages, setMessages] = useState<Message[]>([]);
const [isThinking, setIsThinking] = useState(false);
const [streamingText, setStreamingText] = useState("");
const [activeTool, setActiveTool] = useState<string | null>(null);
const ws = useRef<WebSocket | null>(null);
useEffect(() => {
const url = `ws://localhost:8001/api/atomizer/ws`;
ws.current = new WebSocket(url);
ws.current.onopen = () => {
if (studyId) {
ws.current?.send(JSON.stringify({ type: "set_study", study_id: studyId }));
}
};
ws.current.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case "text":
setStreamingText(prev => prev + data.content);
break;
case "tool_start":
setActiveTool(data.tool);
break;
case "tool_result":
setActiveTool(null);
break;
case "spec_updated":
setSpec(data.spec); // Canvas updates automatically!
break;
case "done":
// Finalize message
setMessages(prev => [...prev, {
role: "assistant",
content: streamingText
}]);
setStreamingText("");
setIsThinking(false);
break;
}
};
return () => ws.current?.close();
}, [studyId]);
const sendMessage = useCallback((content: string) => {
setIsThinking(true);
setMessages(prev => [...prev, { role: "user", content }]);
ws.current?.send(JSON.stringify({ type: "message", content }));
}, []);
const notifyCanvasEdit = useCallback((path: string, value: any) => {
ws.current?.send(JSON.stringify({
type: "canvas_edit",
patch: { path, value }
}));
}, []);
return {
spec,
messages,
streamingText,
isThinking,
activeTool,
sendMessage,
notifyCanvasEdit
};
}
```
---
## Phase 2: Full Tool Set (1-2 weeks)
### Goal: Claude Code-level power through dashboard
### 2.1 Tool Categories
```python
ATOMIZER_TOOLS = {
# === SPEC MODIFICATION (Already Implemented) ===
"add_design_variable": "Add a design variable to the optimization",
"add_extractor": "Add a physics extractor (mass, stress, displacement, custom)",
"add_objective": "Add an optimization objective",
"add_constraint": "Add a constraint",
"update_spec_field": "Update any field in the spec by JSON path",
"remove_node": "Remove a node from the spec",
# === READ/QUERY ===
"read_study_config": "Read the full atomizer_spec.json",
"query_trials": "Query optimization trial data",
"list_studies": "List all available studies",
"get_optimization_status": "Check if optimization is running",
# === STUDY MANAGEMENT (New) ===
"create_study": "Create a new study directory with atomizer_spec.json",
"clone_study": "Clone an existing study as a starting point",
"validate_spec": "Validate the current spec for errors",
# === NX INTEGRATION (New) ===
"introspect_model": "Analyze NX model for expressions and features",
"suggest_design_vars": "AI-suggest design variables from model",
"list_model_expressions": "List all expressions in the NX model",
# === OPTIMIZATION CONTROL (New) ===
"start_optimization": "Start the optimization run",
"stop_optimization": "Stop a running optimization",
# === INTERVIEW (New) ===
"start_interview": "Begin guided study creation interview",
"get_interview_progress": "Get current interview state",
}
```
### 2.2 Create Study Tool
```python
async def _tool_create_study(self, params: Dict) -> Dict:
"""Create a new study with atomizer_spec.json"""
study_name = params["name"]
study_dir = STUDIES_DIR / study_name
# Create directory structure
study_dir.mkdir(parents=True, exist_ok=True)
(study_dir / "1_setup").mkdir(exist_ok=True)
(study_dir / "2_iterations").mkdir(exist_ok=True)
(study_dir / "3_results").mkdir(exist_ok=True)
# Create initial spec
spec = {
"meta": {
"version": "2.0",
"study_name": study_name,
"created_at": datetime.now().isoformat(),
"created_by": "claude_agent"
},
"model": {
"sim": {"path": params.get("model_path", ""), "solver": "nastran"}
},
"design_variables": [],
"extractors": [],
"objectives": [],
"constraints": [],
"optimization": {
"algorithm": {"type": "TPE"},
"budget": {"max_trials": 100}
},
"canvas": {"edges": [], "layout_version": "2.0"}
}
# Save spec
spec_path = study_dir / "atomizer_spec.json"
with open(spec_path, "w") as f:
json.dump(spec, f, indent=2)
# Update agent's study context
self.study_id = study_name
self.study_dir = study_dir
return {
"preview": f"✓ Created study '{study_name}' at {study_dir}",
"spec_changed": True,
"study_id": study_name
}
```
### 2.3 NX Introspection Tool
```python
async def _tool_introspect_model(self, params: Dict) -> Dict:
"""Analyze NX model for design variable candidates"""
model_path = params.get("model_path") or self._find_model_path()
if not model_path or not Path(model_path).exists():
return {"preview": "✗ Model file not found", "spec_changed": False}
# Use NX session to get expressions
expressions = await self._get_nx_expressions(model_path)
# Classify expressions as potential DVs
candidates = []
for expr in expressions:
score = self._score_dv_candidate(expr)
if score > 0.5:
candidates.append({
"name": expr["name"],
"value": expr["value"],
"formula": expr.get("formula", ""),
"score": score,
"suggested_bounds": self._suggest_bounds(expr)
})
# Sort by score
candidates.sort(key=lambda x: x["score"], reverse=True)
return {
"preview": f"Found {len(expressions)} expressions, {len(candidates)} are DV candidates",
"expressions": expressions,
"candidates": candidates[:10], # Top 10
"spec_changed": False
}
def _score_dv_candidate(self, expr: Dict) -> float:
"""Score expression as design variable candidate"""
score = 0.0
name = expr["name"].lower()
# Geometric parameters score high
if any(kw in name for kw in ["thickness", "width", "height", "radius", "diameter", "depth"]):
score += 0.4
# Numeric with reasonable value
if isinstance(expr["value"], (int, float)) and expr["value"] > 0:
score += 0.2
# Not a formula (pure number)
if not expr.get("formula") or expr["formula"] == str(expr["value"]):
score += 0.2
# Common design parameter names
if any(kw in name for kw in ["rib", "web", "flange", "support", "angle"]):
score += 0.2
return min(score, 1.0)
```
---
## Phase 3: Interview Engine (1-2 weeks)
### Goal: Guided study creation through conversation
### 3.1 Interview Engine
```python
class InterviewEngine:
"""Guided study creation through conversation"""
PHASES = [
("welcome", "What kind of optimization do you want to set up?"),
("model", "What's the path to your NX simulation file (.sim)?"),
("objectives", "What do you want to optimize? (e.g., minimize mass, minimize displacement)"),
("design_vars", "Which parameters should vary? I can suggest some from your model."),
("constraints", "Any constraints to respect? (e.g., max stress ≤ 200 MPa)"),
("method", "I recommend {method} for this. Sound good?"),
("review", "Here's your configuration. Ready to create the study?"),
]
def __init__(self):
self.phase_index = 0
self.collected = {}
self.spec_builder = SpecBuilder()
def get_current_question(self) -> str:
phase, question = self.PHASES[self.phase_index]
# Dynamic question customization
if phase == "method":
method = self._recommend_method()
question = question.format(method=method)
elif phase == "design_vars" and self.collected.get("model_expressions"):
candidates = self.collected["model_expressions"][:5]
question += f"\n\nI found these candidates: {', '.join(c['name'] for c in candidates)}"
return question
def process_answer(self, answer: str) -> Dict:
"""Process user's answer and advance interview"""
phase, _ = self.PHASES[self.phase_index]
# Extract structured data based on phase
extracted = self._extract_for_phase(phase, answer)
self.collected[phase] = extracted
# Build spec incrementally
spec_changes = self.spec_builder.apply(phase, extracted)
# Advance
self.phase_index += 1
complete = self.phase_index >= len(self.PHASES)
return {
"phase_completed": phase,
"extracted": extracted,
"spec_changes": spec_changes,
"next_question": None if complete else self.get_current_question(),
"complete": complete,
"spec": self.spec_builder.get_spec() if complete else None
}
def _extract_for_phase(self, phase: str, answer: str) -> Dict:
"""Extract structured data from natural language answer"""
if phase == "model":
# Extract file path
return {"path": self._extract_path(answer)}
elif phase == "objectives":
# Extract objectives
objectives = []
if "mass" in answer.lower() or "weight" in answer.lower():
direction = "minimize" if "minimize" in answer.lower() or "reduce" in answer.lower() else "minimize"
objectives.append({"name": "mass", "direction": direction})
if "displacement" in answer.lower() or "stiff" in answer.lower():
objectives.append({"name": "max_displacement", "direction": "minimize"})
if "stress" in answer.lower():
objectives.append({"name": "max_stress", "direction": "minimize"})
if "wfe" in answer.lower() or "wavefront" in answer.lower():
objectives.append({"name": "wfe", "direction": "minimize"})
return {"objectives": objectives}
elif phase == "constraints":
# Extract constraints
constraints = []
import re
# Pattern: "stress < 200 MPa" or "max stress <= 200"
stress_match = re.search(r'stress[^0-9]*([<>=]+)\s*(\d+)', answer.lower())
if stress_match:
constraints.append({
"name": "max_stress",
"operator": stress_match.group(1),
"threshold": float(stress_match.group(2))
})
return {"constraints": constraints}
return {"raw": answer}
def _recommend_method(self) -> str:
"""Recommend optimization method based on collected info"""
objectives = self.collected.get("objectives", {}).get("objectives", [])
if len(objectives) > 1:
return "NSGA-II (multi-objective)"
return "TPE (Bayesian optimization)"
```
### 3.2 Interview Tool Integration
```python
async def _tool_start_interview(self, params: Dict) -> Dict:
"""Start guided study creation"""
self.interview = InterviewEngine()
question = self.interview.get_current_question()
return {
"preview": f"Starting interview.\n\n{question}",
"interview_started": True,
"spec_changed": False
}
async def _tool_interview_answer(self, params: Dict) -> Dict:
"""Process interview answer"""
if not self.interview:
return {"preview": "No interview in progress", "spec_changed": False}
result = self.interview.process_answer(params["answer"])
response = f"Got it: {result['phase_completed']}\n\n"
if result["spec_changes"]:
response += "Updated configuration:\n"
for change in result["spec_changes"]:
response += f"{change}\n"
if result["next_question"]:
response += f"\n{result['next_question']}"
elif result["complete"]:
response += "\n✓ Interview complete! Creating study..."
# Auto-create the study
self.spec_store.set(result["spec"])
return {
"preview": response,
"spec_changed": result["complete"],
"complete": result["complete"]
}
```
---
## Phase 4: Visual Polish (1 week)
### Goal: Beautiful, responsive canvas updates
### 4.1 Tool Call Visualization
```typescript
// components/chat/ToolCallIndicator.tsx
function ToolCallIndicator({ tool, status }: { tool: string; status: 'running' | 'complete' }) {
const icons: Record<string, JSX.Element> = {
add_design_variable: <Variable className="w-4 h-4" />,
add_extractor: <Cpu className="w-4 h-4" />,
add_objective: <Target className="w-4 h-4" />,
add_constraint: <Lock className="w-4 h-4" />,
create_study: <FolderPlus className="w-4 h-4" />,
introspect_model: <Search className="w-4 h-4" />,
};
return (
<div className={`flex items-center gap-2 px-3 py-2 rounded-lg ${
status === 'running'
? 'bg-amber-500/10 text-amber-400 border border-amber-500/20'
: 'bg-green-500/10 text-green-400 border border-green-500/20'
}`}>
{status === 'running' ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Check className="w-4 h-4" />
)}
{icons[tool] || <Wrench className="w-4 h-4" />}
<span className="text-sm font-medium">
{formatToolName(tool)}
</span>
</div>
);
}
```
### 4.2 Canvas Node Animation
```typescript
// components/canvas/AnimatedNode.tsx
function AnimatedNode({ data, isNew, isHighlighted }) {
return (
<motion.div
initial={isNew ? { scale: 0, opacity: 0 } : false}
animate={{
scale: 1,
opacity: 1,
boxShadow: isHighlighted
? '0 0 0 3px rgba(245, 158, 11, 0.5)'
: 'none'
}}
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
className="node-container"
>
{/* Node content */}
</motion.div>
);
}
```
### 4.3 Connection Line Animation
```typescript
// Draw animated line when Claude adds an edge
function AnimatedEdge({ source, target, isNew }) {
return (
<motion.path
d={getBezierPath({ source, target })}
initial={isNew ? { pathLength: 0, opacity: 0 } : false}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 0.5, ease: 'easeOut' }}
stroke="currentColor"
strokeWidth={2}
fill="none"
/>
);
}
```
---
## User Experience Flows
### Flow 1: Quick Creation (Experienced User)
```
User: "Create bracket optimization, minimize mass, thickness 2-10mm, max stress 200 MPa"
Claude: [Parses complete intent]
🔧 Creating study "bracket_optimization"
🔧 Adding design variable: thickness [2mm - 10mm]
🔧 Adding extractor: mass
🔧 Adding extractor: max_stress
🔧 Adding objective: minimize mass
🔧 Adding constraint: stress ≤ 200 MPa
✓ Created! Canvas shows your setup. Click any node to adjust.
[Canvas animates: DV appears → Model → Extractors → Objectives/Constraints]
[Total time: ~5 seconds]
```
### Flow 2: Guided Interview (New User)
```
User: "Help me set up an optimization"
Claude: What kind of optimization do you want to set up?
(e.g., bracket stiffness, mirror WFE, beam stress)
User: "Mirror surface quality optimization"
Claude: Got it - mirror optimization!
What's the path to your NX simulation file?
(You can drag & drop or paste the path)
User: "studies/M1_Mirror/model.sim"
Claude: 🔧 Loading model...
Found your model with 15 expressions. These look like good design variables:
- rib_thickness (8mm)
- mirror_depth (50mm)
- support_angle (45°)
What are you trying to optimize?
[Canvas: Model node appears]
User: "Minimize wavefront error and mass"
Claude: 🔧 Adding Zernike WFE extractor
🔧 Adding mass extractor
🔧 Adding objectives (multi-objective)
Multi-objective optimization! I recommend NSGA-II.
Which parameters should vary?
[Canvas: Extractors and objectives appear, connected to model]
User: "Use rib_thickness and mirror_depth, 50% range on each"
Claude: 🔧 Adding rib_thickness [4mm - 12mm]
🔧 Adding mirror_depth [25mm - 75mm]
Any constraints?
[Canvas: DVs appear, connected to model]
User: "Keep stress under 150 MPa"
Claude: 🔧 Adding stress extractor
🔧 Adding constraint: stress ≤ 150 MPa
✓ Configuration complete!
Your setup:
- 2 design variables
- 3 extractors (WFE, mass, stress)
- 2 objectives (multi-objective)
- 1 constraint
Ready to run? Say "start" or adjust the canvas first.
[Canvas: Full optimization flow visible]
```
### Flow 3: User Edits, Claude Adapts
```
[User clicks rib_thickness node, changes max from 12mm to 20mm]
User: "Is this a good range?"
Claude: I see you expanded rib_thickness to [4mm - 20mm].
That's a wide range! Considerations:
- Thicker ribs = more mass, more stiffness
- 20mm might exceed typical manufacturing limits
- Wider range = more trials to explore
If you're exploring, this is fine. For production,
I'd suggest [4mm - 15mm].
Want me to adjust, or keep your change?
```
---
## Implementation Priority
### Week 1-2: Unified WebSocket
- [ ] Create `/api/atomizer/ws` endpoint
- [ ] Implement streaming in `AtomizerClaudeAgent`
- [ ] Create `useAtomizerSocket` hook
- [ ] Wire canvas to receive spec updates
- [ ] Add canvas edit notifications
### Week 3-4: Tools & Interview
- [ ] Add `create_study` tool
- [ ] Add `introspect_model` tool
- [ ] Implement `InterviewEngine`
- [ ] Add interview tools
- [ ] Test guided creation flow
### Week 5: Polish
- [ ] Tool call indicators in chat
- [ ] Node appear/highlight animations
- [ ] Edge draw animations
- [ ] Error recovery & reconnection
- [ ] Performance optimization
---
## Success Metrics
| Metric | Target |
|--------|--------|
| Study creation time (experienced) | < 30 seconds |
| Study creation time (interview) | < 3 minutes |
| Canvas update latency | < 200ms |
| User edit → Claude context | < 100ms |
| Interview completion rate | > 90% |
---
## Key Files to Modify
### Backend
- `atomizer-dashboard/backend/api/routes/atomizer_ws.py` (new)
- `atomizer-dashboard/backend/api/services/claude_agent.py` (enhance)
- `atomizer-dashboard/backend/api/services/interview_engine.py` (new)
### Frontend
- `atomizer-dashboard/frontend/src/hooks/useAtomizerSocket.ts` (new)
- `atomizer-dashboard/frontend/src/pages/CanvasView.tsx` (update)
- `atomizer-dashboard/frontend/src/components/canvas/SpecRenderer.tsx` (update)
- `atomizer-dashboard/frontend/src/components/chat/ToolCallIndicator.tsx` (new)
---
*This architecture makes Atomizer uniquely powerful: natural language + visual feedback + full control, all in one seamless experience.*

View File

@@ -0,0 +1,132 @@
# NXOpen Documentation MCP Server - Setup TODO
**Created:** 2025-12-29
**Status:** PENDING - Waiting for manual configuration
---
## Current State
The NXOpen documentation MCP server exists on **dalidou** (192.168.86.50) but is not accessible from this Windows machine due to hostname resolution issues.
### What's Working
- ✅ Dalidou server is online and reachable at `192.168.86.50`
- ✅ Port 5000 (Documentation Proxy) is responding
- ✅ Port 3000 (Gitea) is responding
- ✅ MCP server code exists at `/srv/claude-assistant/` on dalidou
### What's NOT Working
-`dalidou.local` hostname doesn't resolve (mDNS not configured on this machine)
- ❌ MCP tools not integrated with Claude Code
---
## Steps to Complete
### Step 1: Fix Hostname Resolution (Manual - requires Admin)
**Option A: Run the script as Administrator**
```powershell
# Open PowerShell as Administrator, then:
C:\Users\antoi\Atomizer\add_dalidou_host.ps1
```
**Option B: Manually edit hosts file**
1. Open Notepad as Administrator
2. Open `C:\Windows\System32\drivers\etc\hosts`
3. Add this line at the end:
```
192.168.86.50 dalidou.local dalidou
```
4. Save the file
**Verify:**
```powershell
ping dalidou.local
```
### Step 2: Verify MCP Server is Running on Dalidou
SSH into dalidou and check:
```bash
ssh root@dalidou
# Check documentation proxy
systemctl status siemensdocumentationproxyserver
# Check MCP server (if it's a service)
# Or check what's running on port 5000
ss -tlnp | grep 5000
```
### Step 3: Configure Claude Code MCP Integration
The MCP server on dalidou uses **stdio-based MCP protocol**, not HTTP. To connect from Claude Code, you'll need one of:
**Option A: SSH-based MCP (if supported)**
Configure in `.claude/settings.json` or MCP config to connect via SSH tunnel.
**Option B: Local Proxy**
Run a local MCP proxy that connects to dalidou's MCP server.
**Option C: HTTP Wrapper**
The current port 5000 service may already expose HTTP endpoints - need to verify once hostname is fixed.
---
## Server Documentation Reference
Full documentation is in the SERVtomaste repo:
- **URL:** http://192.168.86.50:3000/Antoine/SERVtomaste
- **File:** `docs/SIEMENS-DOCS-SERVER.md`
### Key Server Paths (on dalidou)
```
/srv/siemens-docs/proxy/ # Documentation Proxy (port 5000)
/srv/claude-assistant/ # MCP Server
/srv/claude-assistant/mcp-server/ # MCP server code
/srv/claude-assistant/tools/ # Tool implementations
├── siemens-auth.js # Puppeteer authentication
├── siemens-docs.js # Documentation fetching
└── ...
/srv/claude-assistant/vault/ # Credentials (secured)
```
### Available MCP Tools (once connected)
| Tool | Description |
|------|-------------|
| `siemens_docs_search` | Search NX Open, Simcenter docs |
| `siemens_docs_fetch` | Fetch specific documentation page |
| `siemens_auth_status` | Check if auth session is active |
| `siemens_login` | Re-login if session expired |
| `siemens_docs_list` | List documentation categories |
---
## Files Created During Investigation
- `C:\Users\antoi\Atomizer\add_dalidou_host.ps1` - Script to add hosts entry (run as Admin)
- `C:\Users\antoi\Atomizer\test_mcp.py` - Test script for probing MCP server (can be deleted)
---
## Related Documentation
- `.claude/skills/modules/nx-docs-lookup.md` - How to use MCP tools once configured
- `docs/08_ARCHIVE/historical/NXOPEN_DOCUMENTATION_INTEGRATION_STRATEGY.md` - Full strategy doc
- `docs/05_API_REFERENCE/NXOPEN_RESOURCES.md` - Alternative NXOpen resources
---
## Workaround Until Fixed
Without the MCP server, you can still look up NXOpen documentation by:
1. **Using web search** - I can search for NXOpen API documentation online
2. **Using local stub files** - Python stubs at `C:\Program Files\Siemens\NX2412\UGOPEN\pythonStubs\`
3. **Using existing extractors** - Check `optimization_engine/extractors/` for patterns
4. **Recording NX journals** - Record operations in NX to learn the API calls
---
*To continue setup, run the hosts file fix and let me know when ready.*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,495 @@
# Unified Configuration Architecture - Execution Plan
**Project**: AtomizerSpec v2.0 Implementation
**Reference Document**: `docs/plans/UNIFIED_CONFIGURATION_ARCHITECTURE.md`
**Schema Definition**: `optimization_engine/schemas/atomizer_spec_v2.json`
**Status**: Ready for Implementation
---
## Project Overview
Transform Atomizer's fragmented configuration system into a unified architecture where:
- One JSON schema (AtomizerSpec v2.0) is the single source of truth
- Canvas, Backend, Claude, and Optimization Engine all use the same spec
- Real-time WebSocket sync keeps all clients updated
- Claude can dynamically modify specs and add custom functions
---
## Phase Structure
| Phase | Name | Duration | Focus |
|-------|------|----------|-------|
| 1 | Foundation | Week 1-3 | Backend SpecManager, REST API, Migration |
| 2 | Frontend | Week 4-6 | SpecRenderer, WebSocket Sync, Store |
| 3 | Claude Integration | Week 7-9 | MCP Tools, Custom Functions |
| 4 | Polish & Testing | Week 10-12 | Migration, Testing, Documentation |
---
## Implementation Order (Critical Path)
```
1. Schema & Types (P1.1-P1.3)
└── 2. SpecManager Service (P1.4-P1.7)
└── 3. REST Endpoints (P1.8-P1.12)
├── 4. Migration Script (P1.13-P1.16)
└── 5. Frontend Store (P2.1-P2.4)
└── 6. SpecRenderer (P2.5-P2.10)
└── 7. WebSocket Sync (P2.11-P2.15)
└── 8. MCP Tools (P3.1-P3.8)
└── 9. Custom Functions (P3.9-P3.14)
└── 10. Testing & Polish (P4.1-P4.12)
```
---
## PHASE 1: Foundation (Backend)
### P1.1 - Create TypeScript types from JSON Schema
- **File**: `atomizer-dashboard/frontend/src/types/atomizer-spec.ts`
- **Action**: Generate TypeScript interfaces matching `atomizer_spec_v2.json`
- **Reference**: Schema at `optimization_engine/schemas/atomizer_spec_v2.json`
- **Acceptance**: Types compile, cover all schema definitions
### P1.2 - Create Python Pydantic models from JSON Schema
- **File**: `optimization_engine/config/spec_models.py`
- **Action**: Create Pydantic models for AtomizerSpec validation
- **Reference**: Schema at `optimization_engine/schemas/atomizer_spec_v2.json`
- **Acceptance**: Models validate example specs correctly
### P1.3 - Create spec validation utility
- **File**: `optimization_engine/config/spec_validator.py`
- **Action**: JSON Schema validation + semantic validation (bounds, references)
- **Dependencies**: P1.2
- **Acceptance**: Validates good specs, rejects invalid with clear errors
### P1.4 - Create SpecManager core class
- **File**: `atomizer-dashboard/backend/api/services/spec_manager.py`
- **Action**: Implement `load()`, `save()`, `_validate()`, `_compute_hash()`
- **Reference**: Design in UNIFIED_CONFIGURATION_ARCHITECTURE.md Section 5.1
- **Dependencies**: P1.3
- **Acceptance**: Can load/save/validate atomizer_spec.json files
### P1.5 - Add SpecManager patch functionality
- **File**: `atomizer-dashboard/backend/api/services/spec_manager.py`
- **Action**: Implement `patch()` method for JSONPath-style updates
- **Dependencies**: P1.4
- **Acceptance**: Can update nested fields with conflict detection
### P1.6 - Add SpecManager node operations
- **File**: `atomizer-dashboard/backend/api/services/spec_manager.py`
- **Action**: Implement `add_node()`, `remove_node()`, `_generate_id()`, `_auto_position()`
- **Dependencies**: P1.5
- **Acceptance**: Can add/remove design vars, extractors, objectives, constraints
### P1.7 - Add SpecManager custom function support
- **File**: `atomizer-dashboard/backend/api/services/spec_manager.py`
- **Action**: Implement `add_custom_function()` with Python syntax validation
- **Dependencies**: P1.6
- **Acceptance**: Can add custom extractors with validated Python code
### P1.8 - Create spec REST router
- **File**: `atomizer-dashboard/backend/api/routes/spec.py`
- **Action**: Create FastAPI router for spec endpoints
- **Dependencies**: P1.7
- **Acceptance**: Router imports and mounts correctly
### P1.9 - Implement GET /studies/{study_id}/spec
- **File**: `atomizer-dashboard/backend/api/routes/spec.py`
- **Action**: Return full AtomizerSpec for a study
- **Dependencies**: P1.8
- **Acceptance**: Returns valid spec JSON, 404 for missing studies
### P1.10 - Implement PUT /studies/{study_id}/spec
- **File**: `atomizer-dashboard/backend/api/routes/spec.py`
- **Action**: Replace entire spec with validation
- **Dependencies**: P1.9
- **Acceptance**: Validates, saves, returns new hash
### P1.11 - Implement PATCH /studies/{study_id}/spec
- **File**: `atomizer-dashboard/backend/api/routes/spec.py`
- **Action**: Partial update with JSONPath
- **Dependencies**: P1.10
- **Acceptance**: Updates specific fields, broadcasts change
### P1.12 - Implement POST /studies/{study_id}/spec/validate
- **File**: `atomizer-dashboard/backend/api/routes/spec.py`
- **Action**: Validate spec and return detailed report
- **Dependencies**: P1.11
- **Acceptance**: Returns errors, warnings, summary
### P1.13 - Create config migration base
- **File**: `optimization_engine/config/migrator.py`
- **Action**: Create SpecMigrator class with field mapping
- **Reference**: Design in UNIFIED_CONFIGURATION_ARCHITECTURE.md Section 8
- **Acceptance**: Class structure ready for migration logic
### P1.14 - Implement design variable migration
- **File**: `optimization_engine/config/migrator.py`
- **Action**: Migrate `bounds[]``bounds.min/max`, `parameter``expression_name`
- **Dependencies**: P1.13
- **Acceptance**: All DV formats convert correctly
### P1.15 - Implement objective/constraint migration
- **File**: `optimization_engine/config/migrator.py`
- **Action**: Migrate `goal``direction`, extraction configs to new format
- **Dependencies**: P1.14
- **Acceptance**: Objectives and constraints convert correctly
### P1.16 - Implement full config migration
- **File**: `optimization_engine/config/migrator.py`
- **Action**: Complete migration including canvas positions, extractors inference
- **Dependencies**: P1.15
- **Acceptance**: Can migrate any existing optimization_config.json to AtomizerSpec
### P1.17 - Create migration CLI tool
- **File**: `tools/migrate_to_spec_v2.py`
- **Action**: CLI for batch migration with dry-run support
- **Dependencies**: P1.16
- **Acceptance**: `python tools/migrate_to_spec_v2.py --dry-run studies/*`
---
## PHASE 2: Frontend Integration
### P2.1 - Create useSpecStore hook
- **File**: `atomizer-dashboard/frontend/src/hooks/useSpecStore.ts`
- **Action**: Zustand store for AtomizerSpec state management
- **Dependencies**: P1.1
- **Acceptance**: Store holds spec, provides typed accessors
### P2.2 - Add spec loading to useSpecStore
- **File**: `atomizer-dashboard/frontend/src/hooks/useSpecStore.ts`
- **Action**: Implement `loadSpec(studyId)` fetching from API
- **Dependencies**: P2.1, P1.9
- **Acceptance**: Loads spec from backend, updates store
### P2.3 - Add spec modification to useSpecStore
- **File**: `atomizer-dashboard/frontend/src/hooks/useSpecStore.ts`
- **Action**: Implement `patchSpec()`, `addNode()`, `removeNode()` calling API
- **Dependencies**: P2.2, P1.11
- **Acceptance**: Modifications persist to backend
### P2.4 - Add optimistic updates to useSpecStore
- **File**: `atomizer-dashboard/frontend/src/hooks/useSpecStore.ts`
- **Action**: Update local state immediately, rollback on error
- **Dependencies**: P2.3
- **Acceptance**: UI feels instant, handles errors gracefully
### P2.5 - Create specToNodes converter
- **File**: `atomizer-dashboard/frontend/src/lib/spec/converter.ts`
- **Action**: Convert AtomizerSpec to ReactFlow nodes array
- **Reference**: Design in UNIFIED_CONFIGURATION_ARCHITECTURE.md Section 5.2
- **Dependencies**: P1.1
- **Acceptance**: All node types render correctly
### P2.6 - Create specToEdges converter
- **File**: `atomizer-dashboard/frontend/src/lib/spec/converter.ts`
- **Action**: Convert spec.canvas.edges to ReactFlow edges
- **Dependencies**: P2.5
- **Acceptance**: All connections render correctly
### P2.7 - Create SpecRenderer component
- **File**: `atomizer-dashboard/frontend/src/components/canvas/SpecRenderer.tsx`
- **Action**: ReactFlow component that renders from useSpecStore
- **Dependencies**: P2.5, P2.6, P2.4
- **Acceptance**: Canvas displays spec correctly
### P2.8 - Wire node editing to spec updates
- **File**: `atomizer-dashboard/frontend/src/components/canvas/SpecRenderer.tsx`
- **Action**: Node data changes call useSpecStore.patchSpec()
- **Dependencies**: P2.7
- **Acceptance**: Editing node properties persists to spec
### P2.9 - Wire node position to spec updates
- **File**: `atomizer-dashboard/frontend/src/components/canvas/SpecRenderer.tsx`
- **Action**: Drag-drop updates canvas_position in spec
- **Dependencies**: P2.8
- **Acceptance**: Layout persists across reloads
### P2.10 - Update node panels for full spec fields
- **Files**: `atomizer-dashboard/frontend/src/components/canvas/panels/*.tsx`
- **Action**: Update all node config panels to show/edit full spec fields
- **Dependencies**: P2.8
- **Acceptance**: All spec fields are editable in UI
### P2.11 - Create WebSocket connection hook
- **File**: `atomizer-dashboard/frontend/src/hooks/useSpecSync.ts`
- **Action**: WebSocket connection to `/api/studies/{id}/sync`
- **Dependencies**: P2.4
- **Acceptance**: Connects, handles reconnection
### P2.12 - Create WebSocket backend endpoint
- **File**: `atomizer-dashboard/backend/api/routes/spec.py`
- **Action**: WebSocket endpoint for spec sync
- **Dependencies**: P1.12
- **Acceptance**: Accepts connections, tracks subscribers
### P2.13 - Implement spec_updated broadcast
- **File**: `atomizer-dashboard/backend/api/services/spec_manager.py`
- **Action**: SpecManager broadcasts to all subscribers on save
- **Dependencies**: P2.12
- **Acceptance**: All connected clients receive updates
### P2.14 - Handle spec_updated in frontend
- **File**: `atomizer-dashboard/frontend/src/hooks/useSpecSync.ts`
- **Action**: On spec_updated message, refresh spec from store
- **Dependencies**: P2.11, P2.13
- **Acceptance**: Changes from other clients appear in real-time
### P2.15 - Add conflict detection
- **File**: `atomizer-dashboard/frontend/src/hooks/useSpecStore.ts`
- **Action**: Compare hashes, warn on conflict, offer merge/overwrite
- **Dependencies**: P2.14
- **Acceptance**: Concurrent edits don't silently overwrite
### P2.16 - Replace CanvasView with SpecRenderer
- **File**: `atomizer-dashboard/frontend/src/pages/CanvasView.tsx`
- **Action**: Switch from useCanvasStore to useSpecStore + SpecRenderer
- **Dependencies**: P2.10, P2.15
- **Acceptance**: Canvas page uses new spec-based system
---
## PHASE 3: Claude Integration
### P3.1 - Create spec_get MCP tool
- **File**: `mcp-server/atomizer-tools/src/tools/spec_tools.ts`
- **Action**: Tool to retrieve full AtomizerSpec
- **Reference**: Design in UNIFIED_CONFIGURATION_ARCHITECTURE.md Section 5.3
- **Dependencies**: P1.9
- **Acceptance**: Claude can read spec via MCP
### P3.2 - Create spec_modify MCP tool
- **File**: `mcp-server/atomizer-tools/src/tools/spec_tools.ts`
- **Action**: Tool to apply modifications (set, add, remove operations)
- **Dependencies**: P3.1, P1.11
- **Acceptance**: Claude can modify spec fields
### P3.3 - Create spec_add_node MCP tool
- **File**: `mcp-server/atomizer-tools/src/tools/spec_tools.ts`
- **Action**: Tool to add design vars, extractors, objectives, constraints
- **Dependencies**: P3.2
- **Acceptance**: Claude can add nodes to canvas
### P3.4 - Create spec_remove_node MCP tool
- **File**: `mcp-server/atomizer-tools/src/tools/spec_tools.ts`
- **Action**: Tool to remove nodes (with edge cleanup)
- **Dependencies**: P3.3
- **Acceptance**: Claude can remove nodes
### P3.5 - Create spec_validate MCP tool
- **File**: `mcp-server/atomizer-tools/src/tools/spec_tools.ts`
- **Action**: Tool to validate spec and return report
- **Dependencies**: P1.12
- **Acceptance**: Claude can check spec validity
### P3.6 - Create spec_add_custom_extractor MCP tool
- **File**: `mcp-server/atomizer-tools/src/tools/spec_tools.ts`
- **Action**: Tool to add custom Python functions as extractors
- **Dependencies**: P1.7, P3.3
- **Acceptance**: Claude can add custom extraction logic
### P3.7 - Register spec tools in MCP server
- **File**: `mcp-server/atomizer-tools/src/index.ts`
- **Action**: Import and register all spec_* tools
- **Dependencies**: P3.1-P3.6
- **Acceptance**: Tools appear in MCP tool list
### P3.8 - Update ContextBuilder for spec awareness
- **File**: `atomizer-dashboard/backend/api/services/context_builder.py`
- **Action**: Include spec summary in Claude context, mention spec tools
- **Dependencies**: P3.7
- **Acceptance**: Claude knows about spec tools in context
### P3.9 - Create custom extractor runtime loader
- **File**: `optimization_engine/extractors/custom_loader.py`
- **Action**: Dynamically load and execute custom functions from spec
- **Dependencies**: P1.7
- **Acceptance**: Custom functions execute during optimization
### P3.10 - Integrate custom extractors into optimization runner
- **File**: `optimization_engine/core/runner.py`
- **Action**: Check spec for custom extractors, load and use them
- **Dependencies**: P3.9
- **Acceptance**: Optimization uses custom extractors defined in spec
### P3.11 - Add custom extractor node type to canvas
- **File**: `atomizer-dashboard/frontend/src/components/canvas/nodes/CustomExtractorNode.tsx`
- **Action**: New node type showing custom function with code preview
- **Dependencies**: P2.10
- **Acceptance**: Custom extractors display distinctly in canvas
### P3.12 - Add code editor for custom extractors
- **File**: `atomizer-dashboard/frontend/src/components/canvas/panels/CustomExtractorPanel.tsx`
- **Action**: Monaco editor for viewing/editing custom Python code
- **Dependencies**: P3.11
- **Acceptance**: Users can view and edit custom function code
### P3.13 - Create spec_create_from_description MCP tool
- **File**: `mcp-server/atomizer-tools/src/tools/spec_tools.ts`
- **Action**: Tool to create new spec from natural language + model path
- **Dependencies**: P3.6, P1.16
- **Acceptance**: Claude can create studies from descriptions
### P3.14 - Update Claude prompts for spec workflow
- **File**: `atomizer-dashboard/backend/api/services/context_builder.py`
- **Action**: Update system prompts to guide Claude on using spec tools
- **Dependencies**: P3.13
- **Acceptance**: Claude naturally uses spec tools for modifications
---
## PHASE 4: Polish & Testing
### P4.1 - Migrate m1_mirror studies
- **Action**: Run migration on all m1_mirror_* studies
- **Dependencies**: P1.17
- **Acceptance**: All studies have valid atomizer_spec.json
### P4.2 - Migrate drone_gimbal study
- **Action**: Run migration on drone_gimbal study
- **Dependencies**: P1.17
- **Acceptance**: Study has valid atomizer_spec.json
### P4.3 - Migrate all remaining studies
- **Action**: Run migration on all studies in studies/
- **Dependencies**: P4.1, P4.2
- **Acceptance**: All studies migrated, validated
### P4.4 - Create spec unit tests
- **File**: `tests/test_spec_manager.py`
- **Action**: Unit tests for SpecManager operations
- **Dependencies**: P1.7
- **Acceptance**: All SpecManager methods tested
### P4.5 - Create spec API integration tests
- **File**: `tests/test_spec_api.py`
- **Action**: Integration tests for REST endpoints
- **Dependencies**: P1.12
- **Acceptance**: All endpoints tested
### P4.6 - Create migration tests
- **File**: `tests/test_migrator.py`
- **Action**: Test migration with various config formats
- **Dependencies**: P1.16
- **Acceptance**: All config variants migrate correctly
### P4.7 - Create frontend component tests
- **File**: `atomizer-dashboard/frontend/src/__tests__/SpecRenderer.test.tsx`
- **Action**: Test SpecRenderer with various specs
- **Dependencies**: P2.16
- **Acceptance**: Canvas renders correctly for all spec types
### P4.8 - Create WebSocket sync tests
- **File**: `tests/test_spec_sync.py`
- **Action**: Test real-time sync between multiple clients
- **Dependencies**: P2.15
- **Acceptance**: Changes propagate correctly
### P4.9 - Create MCP tools tests
- **File**: `mcp-server/atomizer-tools/src/__tests__/spec_tools.test.ts`
- **Action**: Test all spec_* MCP tools
- **Dependencies**: P3.7
- **Acceptance**: All tools work correctly
### P4.10 - End-to-end testing
- **Action**: Full workflow test: create study in canvas, modify via Claude, run optimization
- **Dependencies**: P4.1-P4.9
- **Acceptance**: Complete workflow works
### P4.11 - Update documentation
- **Files**: `docs/04_USER_GUIDES/CANVAS.md`, `docs/04_USER_GUIDES/DASHBOARD.md`
- **Action**: Document new spec-based workflow
- **Dependencies**: P4.10
- **Acceptance**: Documentation reflects new system
### P4.12 - Update CLAUDE.md
- **File**: `CLAUDE.md`
- **Action**: Add spec tools documentation, update context loading
- **Dependencies**: P4.11
- **Acceptance**: Claude Code sessions know about spec system
---
## File Summary
### New Files to Create
| File | Phase | Purpose |
|------|-------|---------|
| `atomizer-dashboard/frontend/src/types/atomizer-spec.ts` | P1 | TypeScript types |
| `optimization_engine/config/spec_models.py` | P1 | Pydantic models |
| `optimization_engine/config/spec_validator.py` | P1 | Validation logic |
| `atomizer-dashboard/backend/api/services/spec_manager.py` | P1 | Core spec service |
| `atomizer-dashboard/backend/api/routes/spec.py` | P1 | REST endpoints |
| `optimization_engine/config/migrator.py` | P1 | Config migration |
| `tools/migrate_to_spec_v2.py` | P1 | Migration CLI |
| `atomizer-dashboard/frontend/src/hooks/useSpecStore.ts` | P2 | Spec state store |
| `atomizer-dashboard/frontend/src/hooks/useSpecSync.ts` | P2 | WebSocket sync |
| `atomizer-dashboard/frontend/src/lib/spec/converter.ts` | P2 | Spec ↔ ReactFlow |
| `atomizer-dashboard/frontend/src/components/canvas/SpecRenderer.tsx` | P2 | New canvas component |
| `mcp-server/atomizer-tools/src/tools/spec_tools.ts` | P3 | MCP tools |
| `optimization_engine/extractors/custom_loader.py` | P3 | Custom function loader |
| `atomizer-dashboard/frontend/src/components/canvas/nodes/CustomExtractorNode.tsx` | P3 | Custom node type |
| `atomizer-dashboard/frontend/src/components/canvas/panels/CustomExtractorPanel.tsx` | P3 | Code editor panel |
### Files to Modify
| File | Phase | Changes |
|------|-------|---------|
| `atomizer-dashboard/backend/api/main.py` | P1 | Mount spec router |
| `mcp-server/atomizer-tools/src/index.ts` | P3 | Register spec tools |
| `atomizer-dashboard/backend/api/services/context_builder.py` | P3 | Update Claude context |
| `optimization_engine/core/runner.py` | P3 | Custom extractor support |
| `atomizer-dashboard/frontend/src/pages/CanvasView.tsx` | P2 | Use SpecRenderer |
| `CLAUDE.md` | P4 | Document spec system |
---
## Success Criteria
### Phase 1 Complete When:
- [ ] SpecManager can load/save/validate specs
- [ ] All REST endpoints return correct responses
- [ ] Migration tool converts existing configs
### Phase 2 Complete When:
- [ ] Canvas renders from AtomizerSpec
- [ ] Edits persist to spec file
- [ ] WebSocket sync works between clients
### Phase 3 Complete When:
- [ ] Claude can read and modify specs via MCP
- [ ] Custom extractors work in optimization
- [ ] Claude can create studies from descriptions
### Phase 4 Complete When:
- [ ] All existing studies migrated
- [ ] All tests pass
- [ ] Documentation updated
---
## Risk Mitigation
| Risk | Mitigation |
|------|------------|
| Existing studies break | Migration tool with dry-run, keep old configs as backup |
| WebSocket complexity | Start with polling, add WebSocket as enhancement |
| Custom code security | Sandbox execution, syntax validation, no imports |
| Performance with large specs | Lazy loading, incremental updates |
---
## Quick Reference
**Master Design**: `docs/plans/UNIFIED_CONFIGURATION_ARCHITECTURE.md`
**Schema**: `optimization_engine/schemas/atomizer_spec_v2.json`
**This Plan**: `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md`
---
*Execute tasks in order. Each task has clear acceptance criteria. Reference the master design document for detailed specifications.*

View File

@@ -0,0 +1,92 @@
# AtomizerSpec v2.0 - Quick Start Guide
## TL;DR
**Goal**: Replace 4+ config formats with one unified `atomizer_spec.json` that Canvas, Backend, Claude, and Optimization Engine all use.
**Effort**: ~50 tasks across 4 phases (~12 weeks)
**Key Files**:
- Design: `docs/plans/UNIFIED_CONFIGURATION_ARCHITECTURE.md`
- Tasks: `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md`
- Schema: `optimization_engine/schemas/atomizer_spec_v2.json`
- Prompts: `docs/plans/UNIFIED_CONFIG_RALPH_PROMPT.md`
---
## Start Autonomous Implementation
### 1. Open Claude CLI
```bash
claude --dangerously-skip-permissions
```
### 2. Paste Initial Prompt
```
You are implementing the AtomizerSpec v2.0 Unified Configuration Architecture for Atomizer.
Read these documents before starting:
1. `docs/plans/UNIFIED_CONFIGURATION_ARCHITECTURE.md` - Master design
2. `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md` - Task list (50+ tasks)
3. `optimization_engine/schemas/atomizer_spec_v2.json` - JSON Schema
Implement tasks in order (P1.1 → P4.12). Use TodoWrite to track progress. Commit after each logical group. Reference the design document for code examples.
Begin by reading the documents and loading tasks into TodoWrite. Start with P1.1.
```
### 3. Let It Run
Claude will work through the task list autonomously, committing as it goes.
---
## Phase Summary
| Phase | Tasks | Focus | Key Deliverables |
|-------|-------|-------|------------------|
| **1** | P1.1-P1.17 | Backend | SpecManager, REST API, Migration |
| **2** | P2.1-P2.16 | Frontend | SpecRenderer, WebSocket Sync |
| **3** | P3.1-P3.14 | Claude | MCP Tools, Custom Functions |
| **4** | P4.1-P4.12 | Polish | Tests, Migration, Docs |
---
## Key Concepts
**AtomizerSpec**: Single JSON file containing everything needed for optimization AND canvas display.
**SpecManager**: Backend service that validates, saves, and broadcasts spec changes.
**SpecRenderer**: Frontend component that renders canvas directly from spec.
**Real-time Sync**: WebSocket broadcasts changes to all connected clients.
**Custom Functions**: Python code stored in spec, loaded dynamically during optimization.
---
## Success Metrics
- [ ] Canvas loads/saves without data loss
- [ ] Claude can modify specs via MCP tools
- [ ] All existing studies migrated
- [ ] WebSocket sync works between clients
- [ ] Custom extractors execute in optimization
- [ ] All tests pass
---
## If Resuming
```
Continue implementing AtomizerSpec v2.0.
Reference:
- `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md`
Check current todo list, find last completed task, continue from there.
```
---
*Full details in the referenced documents.*

View File

@@ -0,0 +1,286 @@
# Ralph Loop Prompt: AtomizerSpec v2.0 Implementation
**Copy this prompt to start the autonomous implementation loop.**
---
## Claude CLI Command
```bash
claude --dangerously-skip-permissions
```
---
## Initial Prompt (Copy This)
```
You are implementing the AtomizerSpec v2.0 Unified Configuration Architecture for Atomizer.
## Project Context
Read these documents before starting:
1. `docs/plans/UNIFIED_CONFIGURATION_ARCHITECTURE.md` - Master design document with architecture, schemas, and component designs
2. `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md` - Detailed task list with 50+ tasks across 4 phases
3. `optimization_engine/schemas/atomizer_spec_v2.json` - The JSON Schema definition
## Your Mission
Implement the AtomizerSpec v2.0 system following the execution plan. Work through tasks in order (P1.1 → P1.2 → ... → P4.12).
## Rules
1. **Follow the plan**: Execute tasks in the order specified in UNIFIED_CONFIG_EXECUTION_PLAN.md
2. **Check dependencies**: Don't start a task until its dependencies are complete
3. **Use TodoWrite**: Track all tasks, mark in_progress when starting, completed when done
4. **Test as you go**: Verify each task meets its acceptance criteria before moving on
5. **Commit regularly**: Commit after completing each logical group of tasks (e.g., after P1.7, after P1.12)
6. **Reference the design**: The master document has code examples - use them
7. **Ask if blocked**: If you encounter a blocker, explain what's wrong and what you need
## Starting Point
1. Read the three reference documents listed above
2. Load the current task list into TodoWrite
3. Start with P1.1 (Create TypeScript types from JSON Schema)
4. Work through each task, marking completion as you go
## Commit Message Format
Use conventional commits:
- `feat(spec): Add SpecManager core class (P1.4)`
- `feat(spec-api): Implement GET /studies/{id}/spec (P1.9)`
- `feat(frontend): Create useSpecStore hook (P2.1)`
- `test(spec): Add SpecManager unit tests (P4.4)`
## Progress Tracking
After completing each phase, summarize:
- Tasks completed
- Files created/modified
- Any deviations from plan
- Blockers encountered
Begin by reading the reference documents and loading tasks into TodoWrite.
```
---
## Continuation Prompt (When Resuming)
```
Continue implementing the AtomizerSpec v2.0 system.
Reference documents:
- `docs/plans/UNIFIED_CONFIGURATION_ARCHITECTURE.md` - Master design
- `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md` - Task list
Check where we left off:
1. Read the current todo list
2. Find the last completed task
3. Continue with the next task in sequence
If you need context on what was done, check recent git commits.
Resume implementation.
```
---
## Phase-Specific Prompts
### Start Phase 1 (Foundation)
```
Begin Phase 1 of AtomizerSpec implementation - Foundation/Backend.
Reference: `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md` section "PHASE 1: Foundation"
Tasks P1.1 through P1.17 focus on:
- TypeScript and Python types from JSON Schema
- SpecManager service (load, save, validate, patch, nodes)
- REST API endpoints
- Migration tool
Start with P1.1: Create TypeScript types from the JSON Schema at `optimization_engine/schemas/atomizer_spec_v2.json`.
Work through each task in order, tracking with TodoWrite.
```
### Start Phase 2 (Frontend)
```
Begin Phase 2 of AtomizerSpec implementation - Frontend Integration.
Reference: `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md` section "PHASE 2: Frontend Integration"
Tasks P2.1 through P2.16 focus on:
- useSpecStore hook (Zustand state management)
- Spec ↔ ReactFlow conversion
- SpecRenderer component
- WebSocket real-time sync
- Replacing old canvas with spec-based canvas
Prerequisites: Phase 1 must be complete (P1.1-P1.17).
Start with P2.1: Create useSpecStore hook.
Work through each task in order, tracking with TodoWrite.
```
### Start Phase 3 (Claude Integration)
```
Begin Phase 3 of AtomizerSpec implementation - Claude Integration.
Reference: `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md` section "PHASE 3: Claude Integration"
Tasks P3.1 through P3.14 focus on:
- MCP tools for spec operations (spec_get, spec_modify, spec_add_node, etc.)
- Custom extractor support (add Python functions via Claude)
- Runtime loading of custom functions
- Natural language study creation
Prerequisites: Phases 1 and 2 must be complete.
Start with P3.1: Create spec_get MCP tool.
Work through each task in order, tracking with TodoWrite.
```
### Start Phase 4 (Polish & Testing)
```
Begin Phase 4 of AtomizerSpec implementation - Polish & Testing.
Reference: `docs/plans/UNIFIED_CONFIG_EXECUTION_PLAN.md` section "PHASE 4: Polish & Testing"
Tasks P4.1 through P4.12 focus on:
- Migrating all existing studies to AtomizerSpec v2
- Unit tests, integration tests, e2e tests
- Documentation updates
Prerequisites: Phases 1, 2, and 3 must be complete.
Start with P4.1: Migrate m1_mirror studies.
Work through each task in order, tracking with TodoWrite.
```
---
## Troubleshooting Prompts
### If Stuck on a Task
```
I'm blocked on task [TASK_ID].
The issue is: [describe the problem]
What I've tried: [list attempts]
Please help me resolve this so I can continue with the execution plan.
```
### If Tests Fail
```
Tests are failing for [component].
Error: [paste error]
This is blocking task [TASK_ID]. Help me fix the tests so I can mark the task complete.
```
### If Design Needs Clarification
```
Task [TASK_ID] requires [specific thing] but the design document doesn't specify [what's unclear].
Options I see:
1. [option A]
2. [option B]
Which approach should I take? Or should I update the design document?
```
---
## Full Task List for TodoWrite
Copy this to initialize the todo list:
```
P1.1 - Create TypeScript types from JSON Schema
P1.2 - Create Python Pydantic models from JSON Schema
P1.3 - Create spec validation utility
P1.4 - Create SpecManager core class
P1.5 - Add SpecManager patch functionality
P1.6 - Add SpecManager node operations
P1.7 - Add SpecManager custom function support
P1.8 - Create spec REST router
P1.9 - Implement GET /studies/{study_id}/spec
P1.10 - Implement PUT /studies/{study_id}/spec
P1.11 - Implement PATCH /studies/{study_id}/spec
P1.12 - Implement POST /studies/{study_id}/spec/validate
P1.13 - Create config migration base
P1.14 - Implement design variable migration
P1.15 - Implement objective/constraint migration
P1.16 - Implement full config migration
P1.17 - Create migration CLI tool
P2.1 - Create useSpecStore hook
P2.2 - Add spec loading to useSpecStore
P2.3 - Add spec modification to useSpecStore
P2.4 - Add optimistic updates to useSpecStore
P2.5 - Create specToNodes converter
P2.6 - Create specToEdges converter
P2.7 - Create SpecRenderer component
P2.8 - Wire node editing to spec updates
P2.9 - Wire node position to spec updates
P2.10 - Update node panels for full spec fields
P2.11 - Create WebSocket connection hook
P2.12 - Create WebSocket backend endpoint
P2.13 - Implement spec_updated broadcast
P2.14 - Handle spec_updated in frontend
P2.15 - Add conflict detection
P2.16 - Replace CanvasView with SpecRenderer
P3.1 - Create spec_get MCP tool
P3.2 - Create spec_modify MCP tool
P3.3 - Create spec_add_node MCP tool
P3.4 - Create spec_remove_node MCP tool
P3.5 - Create spec_validate MCP tool
P3.6 - Create spec_add_custom_extractor MCP tool
P3.7 - Register spec tools in MCP server
P3.8 - Update ContextBuilder for spec awareness
P3.9 - Create custom extractor runtime loader
P3.10 - Integrate custom extractors into optimization runner
P3.11 - Add custom extractor node type to canvas
P3.12 - Add code editor for custom extractors
P3.13 - Create spec_create_from_description MCP tool
P3.14 - Update Claude prompts for spec workflow
P4.1 - Migrate m1_mirror studies
P4.2 - Migrate drone_gimbal study
P4.3 - Migrate all remaining studies
P4.4 - Create spec unit tests
P4.5 - Create spec API integration tests
P4.6 - Create migration tests
P4.7 - Create frontend component tests
P4.8 - Create WebSocket sync tests
P4.9 - Create MCP tools tests
P4.10 - End-to-end testing
P4.11 - Update documentation
P4.12 - Update CLAUDE.md
```
---
## Expected Outcomes
After successful completion:
1. **Single Source of Truth**: `atomizer_spec.json` used everywhere
2. **Bidirectional Sync**: Canvas ↔ Spec with no data loss
3. **Real-time Updates**: WebSocket keeps all clients in sync
4. **Claude Integration**: Claude can read/modify/create specs via MCP
5. **Custom Functions**: Users can add Python extractors through UI/Claude
6. **All Studies Migrated**: Existing configs converted to v2 format
7. **Full Test Coverage**: Unit, integration, and e2e tests passing
8. **Updated Documentation**: User guides reflect new workflow
---
*This prompt file enables autonomous implementation of the AtomizerSpec v2.0 system using the Ralph Loop pattern.*