Last updated: Sep 1, 2025, 01:10 PM UTC

Target Architecture Documentation

Version: 1.0.0
Status: PROPOSED
Date: 2025-01-29
Purpose: Defines the target architecture to solve current problems

Target System Overview

Sasha Studio will implement targeted, granular updates where file changes trigger minimal UI updates based on change type and scope.

Target Architecture Diagram

User Input β†’ UI β†’ WebSocket β†’ Server β†’ Claude CLI β†’ File System
                                             ↓
                                     JSONL Session Files
                                             ↑
                                     Smart File Watcher
                                             ↓
                              Analyze Change Type & Scope
                                             ↓
                         Route to Appropriate Handler
                              /              |              \
                   Documentation      Message Stream    File Change
                         ↓                   ↓               ↓
                   HTML Generation    Stream Parser     Tree Update
                         ↓                   ↓               ↓
                  Toast Message      Single Message    Minimal Update
                         ↓                   ↓               ↓
                    No Re-render      Append Only      Tree Only

Key Architectural Changes

1. Targeted WebSocket Messages

Instead of: Sending entire projects array
Send: Specific update messages

// Documentation Update
{
  type: 'documentation_updated',
  projectId: 'project-123',
  file: 'docs/guide.md',
  htmlPath: '/html-static/content/guide.html',
  operation: 'created|modified|deleted'
}

// Message Stream Update (RECOMMENDED)
{
  type: 'message_streamed',
  projectId: 'project-123',
  sessionId: 'session-456',
  message: {
    id: 'msg-123',
    type: 'assistant',
    content: '...',
    timestamp: '...'
  },
  operation: 'append|replace|delete'
}

// Alternative: Message Delta Update
{
  type: 'messages_appended',
  projectId: 'project-123',
  sessionId: 'session-456',
  newMessages: [...],  // Only new messages
  startIndex: 47      // Where to insert
}

// File Change
{
  type: 'project_files_changed',
  projectId: 'project-123',
  changes: [
    { path: 'src/app.js', operation: 'add|modify|delete' }
  ]
}

2. Smart File Watcher with Message Streaming

Location: server/services/smart-watcher.js (new)

class SmartWatcher {
  constructor() {
    this.watchers = new Map();
    this.lastMessagePosition = new Map(); // Track file positions for streaming
    this.debounces = {
      documentation: 1000,  // 1s for docs
      session: 50,          // 50ms for streaming messages
      files: 500           // 500ms for code
    };
  }

  async routeFileChange(path, changeType) {
    if (path.includes('/docs/') && path.endsWith('.md')) {
      return { handler: 'documentation', debounce: 1000 };
    }
    if (path.includes('/sessions/') && path.endsWith('.jsonl')) {
      // For session files, read only new lines (streaming)
      const lastPos = this.lastMessagePosition.get(path) || 0;
      const newMessages = await this.readNewLines(path, lastPos);
      return { 
        handler: 'message_stream', 
        debounce: 50,  // Very short for real-time feel
        messages: newMessages 
      };
    }
    return { handler: 'files', debounce: 500 };
  }
  
  async readNewLines(filePath, fromPosition) {
    // Read only new lines added to JSONL file
    const stream = fs.createReadStream(filePath, { start: fromPosition });
    const newMessages = [];
    // Parse and return only new messages
    return newMessages;
  }
}

3. State Machine for Operations

Replace: Multiple boolean flags
With: Single state machine

// New state structure
{
  operationState: {
    current: 'idle|selecting_project|creating_session|loading_files',
    context: {
      projectId: 'project-123',
      sessionId: 'session-456',
      startTime: 1706527200000
    }
  },
  projects: [],
  selectedIds: {
    project: 'project-123',
    session: 'session-456'
  },
  messageStreams: {
    'session-456': {
      messages: [],  // Append-only array
      lastMessageId: 'msg-123',
      isStreaming: false
    }
  },
  cache: {
    files: { 'project-123': [...] },
    lastUpdate: { 'project-123': 1706527200000 }
  }
}

4. URL Routing Structure

New Route Pattern:

/project/{projectName}/chat/{sessionId}
/project/{projectName}/files/{path}
/project/{projectName}/docs/{docPath}
/project/{projectName}/settings

Implementation:

<Routes>
  <Route path="/" element={<Welcome />} />
  <Route path="/project/:projectName" element={<ProjectLayout />}>
    <Route index element={<Navigate to="chat" />} />
    <Route path="chat" element={<ChatView />} />
    <Route path="chat/:sessionId" element={<ChatView />} />
    <Route path="files/*" element={<FilesView />} />
    <Route path="docs/*" element={<DocsView />} />
    <Route path="settings" element={<Settings />} />
  </Route>
</Routes>

5. Optimized Reducer Actions

New Action Types:

const ActionTypes = {
  // State machine transitions
  SET_OPERATION_STATE: 'SET_OPERATION_STATE',
  
  // Message streaming (RECOMMENDED)
  APPEND_MESSAGE: 'APPEND_MESSAGE',
  UPDATE_MESSAGE: 'UPDATE_MESSAGE',
  
  // Targeted updates
  UPDATE_PROJECT_FILES: 'UPDATE_PROJECT_FILES',
  SHOW_DOCUMENTATION_TOAST: 'SHOW_DOCUMENTATION_TOAST',
  
  // Atomic operations
  SELECT_PROJECT_AND_SESSION: 'SELECT_PROJECT_AND_SESSION',
  CREATE_AND_SELECT_SESSION: 'CREATE_AND_SELECT_SESSION'
};

Message Handling Pattern:

case 'APPEND_MESSAGE':
  // Stream single message - no array replacement
  return {
    ...state,
    messageStreams: {
      ...state.messageStreams,
      [action.sessionId]: {
        ...state.messageStreams[action.sessionId],
        messages: [
          ...(state.messageStreams[action.sessionId]?.messages || []),
          action.message
        ],
        lastMessageId: action.message.id,
        isStreaming: action.message.isStreaming || false,
        lastUpdate: Date.now()
      }
    }
  };

case 'UPDATE_MESSAGE':
  // Update streaming message content
  const stream = state.messageStreams[action.sessionId];
  const messageIndex = stream.messages.findIndex(m => m.id === action.messageId);
  if (messageIndex === -1) return state;
  
  const updatedMessages = [...stream.messages];
  updatedMessages[messageIndex] = {
    ...updatedMessages[messageIndex],
    ...action.updates,
    isStreaming: action.isStreaming
  };
  
  return {
    ...state,
    messageStreams: {
      ...state.messageStreams,
      [action.sessionId]: {
        ...stream,
        messages: updatedMessages,
        isStreaming: action.isStreaming
      }
    }
  };

6. Component Optimization

App.jsx Refactor:

// Split mega useEffect into focused effects
useEffect(() => {
  // Only handle URL synchronization
}, [selectedIds.project, selectedIds.session]);

useEffect(() => {
  // Only handle initial data loading
}, [selectedIds.project]);

ChatInterface.jsx Refactor:

// Replace ref dance with state machine
const [onboardingState, dispatch] = useReducer(onboardingReducer, {
  phase: 'idle', // idle β†’ detected β†’ submitting β†’ complete
  prompt: null,
  error: null
});

7. Storage Strategy

Clear Separation:

Data Type Storage Location Lifecycle
User preferences localStorage Persistent
Auth token localStorage Until logout
Onboarding prompt sessionStorage One-time use
Project state Reducer only Runtime
Session messages JSONL files only Persistent
File cache Reducer cache Runtime

Remove From Storage:

  • All sasha_chat_messages_* from localStorage
  • pendingSessionId, activeSessionId from sessionStorage
  • Any duplicated state

8. Performance Targets

Metric Current Target How
Re-renders per message 10-20 1-2 Message streaming
WebSocket message size ~100KB <500 bytes Single message only
Message display latency 800ms <100ms Direct streaming
Project switches 3-5 1 Atomic operations
File fetches per project 10-40 1 Proper caching
Debounce delay 300ms 50ms for messages Type-specific

9. Stable Dependencies

useEffect Rules:

// βœ… GOOD - Stable primitives
useEffect(() => {
  loadProject(projectId);
}, [projectId]); // String is stable

// ❌ BAD - Unstable objects
useEffect(() => {
  processProjects(projects);
}, [projects]); // Array recreated every render

10. Event Flow Optimization

1. File Change Detection
   └─ Identify type (doc/session/file)
   
2. Type-Specific Processing
   β”œβ”€ Documentation β†’ Generate HTML β†’ Toast only
   β”œβ”€ Session β†’ Parse new message β†’ Stream single message
   └─ Files β†’ Update tree β†’ Refresh file list only
   
3. Targeted WebSocket Message
   └─ Send only relevant data (single message, not array)
   
4. Minimal Client Update
   └─ Append message to existing array (no replacement)
   
5. Surgical State Change
   └─ Preserve unchanged references

Implementation Priority

Phase 1: Foundation (Week 1)

  • Implement state machine for operations
  • Clean up storage (remove duplicates)
  • Fix useEffect dependencies
  • Add message streaming structure to state

Phase 2: Message Streaming (Week 2) - CRITICAL

  • Implement JSONL incremental reader
  • Create message streaming WebSocket handler
  • Update reducer for single message append
  • Test with real-time Claude responses

Phase 3: Smart Watcher & Routing (Week 3)

  • Create smart file watcher with stream detection
  • Implement new URL route structure
  • Add deep linking support
  • Fix browser navigation

Phase 4: Optimization (Week 4)

  • Add React.memo to message components
  • Implement virtual scrolling for long chats
  • Performance testing with 1000+ messages

Success Metrics

User Experience

  • Zero UI flashing during updates
  • Instant response to user actions
  • Working browser navigation
  • Shareable URLs

Technical Performance

  • 3x reduction in re-renders
  • 20x reduction in WebSocket payload
  • 10x reduction in unnecessary file fetches
  • Sub-100ms UI updates

Code Quality

  • Single source of truth for state
  • Clear separation of concerns
  • Testable architecture
  • Maintainable code structure

Risk Mitigation

Risk 1: Breaking Changes

Mitigation: Implement changes behind feature flags

Risk 2: WebSocket Compatibility

Mitigation: Support both old and new message formats during transition

Risk 3: Performance Regression

Mitigation: Add performance monitoring before changes

Validation Approach

  1. Unit Tests: Test each handler independently
  2. Integration Tests: Test full update flow
  3. Performance Tests: Measure re-render counts
  4. User Testing: Validate UX improvements

This architecture will transform Sasha Studio from a system that re-renders everything on every change to one that surgically updates only what's necessary.