Lessons Learned - Sasha Studio Development
Overview
This document captures key insights and lessons learned during the transformation of Claude Code UI into Sasha Studio, a business-focused AI knowledge management platform.
1. Service Worker Caching Issues in Development
Problem
Service Workers aggressively cache resources, which can cause major issues during development when servers restart or ports change.
What Happened
- Service Worker was caching all fetch requests
- When Vite dev server wasn't running, cached resources became stale
- Fetch failures resulted in "Failed to fetch" errors, preventing app from loading
- Console showed:
The FetchEvent for "<URL>" resulted in a network error response
Solution
Immediate Fix:
// Clear Service Worker and caches in browser console
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for(let registration of registrations) {
registration.unregister();
}
});
caches.keys().then(names => names.forEach(name => caches.delete(name)));
Long-term Fix:
- Consider disabling Service Worker in development mode
- Only enable for production PWA functionality
- Add development check:
if (process.env.NODE_ENV === 'production')
Lesson
Service Workers are powerful but can complicate development. Always provide a way to bypass or disable them during development.
2. CLI Command Integration Testing
Problem
Integrated CLI commands without testing actual command syntax, leading to 500 errors in production.
What Happened
- MCP routes used
claude mcp list -s usersyntax - Claude CLI didn't recognize
-s userflag - All MCP API calls failed with 500 errors
- Error was silent except in server logs
Solution
- Test CLI commands directly before integration
- Remove invalid flags:
claude mcp list(without-s user) - Add proper error handling and logging
Lesson
Always test external CLI commands in isolation before integrating them into your application. Document the exact syntax that works.
3. GitHub Repository Migration
Problem
Needed to migrate from original repository to new branded repository while maintaining functionality.
What Happened
- Version checking was hardcoded to original repo (siteboon/claudecodeui)
- GitHub API returned 404 errors for non-existent releases
- Had to update multiple references throughout codebase
Solution
# Update git remote
git remote remove origin
git remote add origin https://github.com/wapdat/sasha-studio.git
# Update code references
# Change: useVersionCheck('siteboon', 'claudecodeui')
# To: useVersionCheck('wapdat', 'sasha-studio')
Lesson
Make repository references configurable through environment variables or config files to ease migration and white-labeling.
4. Port Management with Multiple Services
Problem
Running both backend (Express) and frontend (Vite) servers requires careful port management.
What Happened
- Backend runs on port 3005
- Frontend (Vite) runs on port 5175
- Port conflicts when trying to restart services
- "EADDRINUSE" errors when ports already in use
Solution
- Use separate commands:
npm run serverandnpm run client - Kill existing processes before restarting:
pkill -f "node server/index.js" - Configure ports in .env file for easy management
Lesson
Document which services run on which ports. Provide scripts to cleanly stop and restart services.
5. Version Checking Without Releases
Problem
Version checking system expects GitHub releases to exist but none were created initially.
What Happened
- API calls to
/repos/owner/repo/releases/latestreturned 404 - Version check gracefully handled the error but logged console warnings
- Version tab showed "Unknown" for current version
Solution
- System handles missing releases gracefully
- Create GitHub releases to enable version checking
- Use semantic versioning (e.g., v1.0.0)
Lesson
Version checking systems should gracefully handle the absence of releases, especially for new repositories.
6. UI Component Removal Strategy
Problem
Removing developer-focused features while maintaining system stability.
What Happened
- Needed to remove Shell, Git tabs, and technical labels
- Had to preserve underlying functionality for potential future use
- Some components had deep integrations
Solution
- Remove UI elements but keep backend code
- Document removed features for potential restoration
- Use feature flags for easy toggling
Lesson
When transforming products, remove UI elements first while keeping backend functionality intact. This allows for easy restoration if needed.
7. Configuration Management
Problem
Hardcoded labels and branding throughout the codebase made customization difficult.
What Happened
- "Claude Code UI" references scattered across multiple files
- Each label change required finding and updating multiple locations
- Risk of missing references during rebranding
Solution
Created centralized configuration:
// src/config/appConfig.js
const appConfig = {
appName: process.env.VITE_APP_NAME || 'Sasha Studio',
aiName: process.env.VITE_AI_NAME || 'Sasha',
// ... all other configurable strings
};
Lesson
Always centralize configuration and branding elements from the start. This makes white-labeling and customization much easier.
8. Development vs Production Considerations
Problem
Features that work well in production can hinder development.
Examples Encountered
- Service Workers: Great for offline functionality, problematic during development
- Caching: Improves performance but complicates testing changes
- Authentication: Necessary for production but slows development
Solution
- Use environment checks:
if (process.env.NODE_ENV === 'development') - Provide bypass mechanisms for development
- Clear documentation on how to disable production features
Lesson
Design systems with both development and production modes in mind from the beginning.
9. React Component Structure and Conditional Rendering
Problem
Tab content not displaying despite being present in the code.
What Happened
- Version tab in Settings modal was completely blank
- The tab button appeared but clicking it showed nothing
- Component had all the correct props and logic
- Console showed no errors
Root Cause
The Version tab content was placed OUTSIDE the main scrollable content container:
// WRONG - Outside the content container
<div className="p-4 md:p-6">
{activeTab === 'tools' && (...)}
{activeTab === 'appearance' && (...)}
</div> <!-- Container closes here -->
{/* Version tab wrongly placed outside */}
{activeTab === 'version' && (...)}
Solution
Moved the Version tab content INSIDE the main content container:
// CORRECT - Inside the content container
<div className="p-4 md:p-6">
{activeTab === 'tools' && (...)}
{activeTab === 'appearance' && (...)}
{activeTab === 'version' && (...)} <!-- Now inside -->
</div>
Lesson
When components don't render despite correct logic:
- Check the component hierarchy and nesting
- Verify content is within the correct parent containers
- Look for misplaced closing tags
- Use React DevTools to inspect component tree
- Be careful with conditional rendering placement
This is especially important in modal and tab components where content areas are strictly defined.
Key Takeaways
- Test External Dependencies: Always test CLI commands and external APIs in isolation first
- Centralize Configuration: Keep all configurable elements in one place
- Plan for Migration: Make repository and service references configurable
- Document Everything: Especially removal of features and workarounds
- Development Experience Matters: Provide tools and scripts to make development easier
- Graceful Degradation: Systems should handle missing dependencies gracefully
- Version Control: Commit after every significant change for easy rollback
- Clear Error Messages: Log detailed errors server-side for debugging
Future Improvements
- Add feature flags system for easier feature toggling
- Create development mode that bypasses Service Workers
- Add health check endpoints for all services
- Implement proper logging system with log levels
- Create automated tests for CLI integrations
- Add pre-commit hooks to check for hardcoded values
- Build configuration validation on startup
- Create migration scripts for repository changes
4. Dynamic Content Loading - localStorage Token Mismatch
Problem
API endpoints returning 403 Forbidden errors despite user being logged in, causing personas and guides panels to crash.
What Happened
- Login process saved token as
localStorage.setItem('auth-token', token) - New components tried to read token as
localStorage.getItem('token') - Missing token resulted in 403 errors from protected API endpoints
- Frontend crashed with "personas.map is not a function" when API returned error object instead of array
Solution
Immediate Fix:
- Added proper error handling to check response status and validate data types
- Fixed token key mismatch by using consistent
'auth-token'key - Added fallback data for when API fails
Code Fix:
// Wrong - inconsistent key
localStorage.getItem('token')
// Correct - matches login process
localStorage.getItem('auth-token')
Lesson
- Always verify localStorage key names match between write and read operations
- Add robust error handling for API responses - validate both HTTP status and data structure
- Include debug logging during development to quickly identify authentication issues
- Use TypeScript or constants for localStorage keys to prevent typos
Best Practice for Future
// Define constants for localStorage keys
const STORAGE_KEYS = {
AUTH_TOKEN: 'auth-token',
USER_DATA: 'user-data'
};
// Use consistently
localStorage.setItem(STORAGE_KEYS.AUTH_TOKEN, token);
localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
10. Context Injection System Design
Challenge
Designing a system to inject guide and persona markdown content into chat sessions without disrupting user experience.
Design Considerations
- Personas: Should persist across messages as they define the AI's behavior/perspective
- Guides: Should apply once as they provide specific instructions or frameworks
- Visibility: Users need to know what context is active
- Flexibility: Should support using both, either, or neither
Solution Architecture
Dual Injection Methods:
- Personas: Persistent context included with every message
- Guides: One-time application for current conversation
State Management:
- App-level state for activePersona and appliedGuides
- Pass context through props to ChatInterface
- Include context in WebSocket message payload
Visual Feedback:
- Badge in chat header showing active persona
- Toast notifications when guides are applied
- Clear indicators of what context is active
Lesson
When designing context injection systems:
- Consider persistence needs (session vs message)
- Provide clear visual feedback
- Make it easy to clear/change context
- Don't clutter the chat interface
- Keep the implementation flexible for future enhancements
Last Updated: 2025-08-09
Maintained by: Knowcode Team