# Synapse-Conduit Integration Plan

## Overview
This document outlines the careful, phased approach to integrating Conduit into Synapse with comprehensive testing at each step.

## Integration Principles
1. **Test-Driven Development**: Write tests before implementation
2. **Incremental Changes**: Small, reviewable commits
3. **Backward Compatibility**: Ensure existing functionality remains intact
4. **Observability**: Sentry instrumentation at every critical point
5. **Configuration**: All features configurable via UI
6. **Data Safety**: Export/import utilities for rollback

## Phase 1: Foundation (Week 1)

### 1.1 Setup & Dependencies
```bash
# In Synapse directory
npm install --save @tehreet/conduit@file:../conduit
npm install --save-dev @testing-library/react vitest @vitest/ui
```

### 1.2 Create Core Service Structure
```
synapse/
├── src/
│   ├── main/
│   │   ├── services/
│   │   │   ├── conduit/
│   │   │   │   ├── conduit-service.ts
│   │   │   │   ├── conduit-service.test.ts
│   │   │   │   ├── metadata-handler.ts
│   │   │   │   ├── metadata-handler.test.ts
│   │   │   │   └── index.ts
│   │   │   └── ...
│   │   └── ...
│   ├── renderer/
│   │   ├── components/
│   │   │   ├── model-config/
│   │   │   │   ├── ModelConfiguration.tsx
│   │   │   │   ├── ModelConfiguration.test.tsx
│   │   │   │   ├── RoutingRulesEditor.tsx
│   │   │   │   ├── RoutingRulesEditor.test.tsx
│   │   │   │   └── index.ts
│   │   │   ├── usage-analytics/
│   │   │   │   ├── UsageAnalytics.tsx
│   │   │   │   ├── UsageAnalytics.test.tsx
│   │   │   │   ├── CostBreakdown.tsx
│   │   │   │   ├── ModelUsageChart.tsx
│   │   │   │   └── index.ts
│   │   │   └── ...
│   │   └── ...
│   └── shared/
│       ├── types/
│       │   ├── conduit.ts
│       │   └── ...
│       └── ...
└── supabase/
    └── migrations/
        ├── 20250115_conduit_integration_base.sql
        ├── 20250115_usage_tracking_tables.sql
        └── 20250115_routing_rules_tables.sql
```

### 1.3 Database Schema Design

#### Projects Table Extension
```sql
-- Model configuration with validation
ALTER TABLE projects
ADD COLUMN conduit_config JSONB DEFAULT '{
  "enabled": false,
  "defaultModel": "claude-3-5-sonnet-20241022",
  "routingRules": [],
  "costLimits": {
    "daily": null,
    "monthly": null
  },
  "features": {
    "autoRouting": true,
    "costTracking": true,
    "usageAnalytics": true
  }
}'::jsonb CHECK (
  conduit_config ? 'enabled' AND
  conduit_config ? 'defaultModel' AND
  conduit_config ? 'features'
);
```

#### Usage Tracking with Partitioning
```sql
-- Partitioned table for scalability
CREATE TABLE usage_tracking (
  id UUID DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  agent_id UUID REFERENCES agents(id) ON DELETE SET NULL,
  session_id UUID,
  model TEXT NOT NULL,
  input_tokens INTEGER NOT NULL CHECK (input_tokens >= 0),
  output_tokens INTEGER NOT NULL CHECK (output_tokens >= 0),
  total_tokens INTEGER GENERATED ALWAYS AS (input_tokens + output_tokens) STORED,
  cost DECIMAL(10, 6) NOT NULL CHECK (cost >= 0),
  cost_currency TEXT DEFAULT 'USD',
  routing_source TEXT NOT NULL,
  routing_reason TEXT,
  latency_ms INTEGER,
  metadata JSONB DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);

-- Create monthly partitions
CREATE TABLE usage_tracking_2025_01 PARTITION OF usage_tracking
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
```

#### Custom Routing Rules
```sql
CREATE TABLE routing_rules (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  name TEXT NOT NULL,
  description TEXT,
  enabled BOOLEAN DEFAULT true,
  priority INTEGER NOT NULL DEFAULT 0,
  conditions JSONB NOT NULL,
  target_model TEXT NOT NULL,
  created_by UUID REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  
  UNIQUE(project_id, name)
);

-- Example conditions structure:
-- {
--   "type": "AND",
--   "rules": [
--     {"field": "tokenCount", "operator": ">", "value": 50000},
--     {"field": "agentType", "operator": "equals", "value": "research"}
--   ]
-- }
```

## Phase 2: Core Implementation (Week 1-2)

### 2.1 ConduitService Implementation

```typescript
// src/main/services/conduit/conduit-service.ts
import { EventEmitter } from 'events';
import { ChildProcess, spawn } from 'child_process';
import * as Sentry from '@sentry/electron/main';
import { app } from 'electron';
import path from 'path';
import fs from 'fs';

export interface ConduitConfig {
  enabled: boolean;
  defaultModel: string;
  routingRules: RoutingRule[];
  costLimits: {
    daily: number | null;
    monthly: number | null;
  };
  features: {
    autoRouting: boolean;
    costTracking: boolean;
    usageAnalytics: boolean;
  };
}

export interface RoutingRule {
  id: string;
  name: string;
  enabled: boolean;
  priority: number;
  conditions: RuleCondition;
  targetModel: string;
}

export interface RuleCondition {
  type: 'AND' | 'OR';
  rules: Array<{
    field: string;
    operator: '>' | '<' | '>=' | '<=' | '=' | '!=' | 'contains' | 'matches';
    value: any;
  }>;
}

export class ConduitService extends EventEmitter {
  private static instance: ConduitService;
  private conduitBinary: string;
  private serverProcess: ChildProcess | null = null;
  private healthCheckTimer: NodeJS.Timer | null = null;
  
  private constructor(
    private sentryService: typeof Sentry,
    private supabaseService: any,
    private logger: any
  ) {
    super();
    this.setupConduit();
  }
  
  static getInstance(sentryService: any, supabaseService: any, logger: any): ConduitService {
    if (!ConduitService.instance) {
      ConduitService.instance = new ConduitService(sentryService, supabaseService, logger);
    }
    return ConduitService.instance;
  }
  
  private async setupConduit(): Promise<void> {
    const transaction = this.sentryService.startTransaction({
      op: 'conduit.setup',
      name: 'Conduit Service Setup'
    });
    
    try {
      // Find or install conduit binary
      this.conduitBinary = await this.ensureConduitInstalled();
      
      // Start health monitoring
      this.startHealthMonitoring();
      
      transaction.setStatus('ok');
    } catch (error) {
      transaction.setStatus('internal_error');
      this.sentryService.captureException(error);
      throw error;
    } finally {
      transaction.finish();
    }
  }
  
  private startHealthMonitoring(): void {
    this.healthCheckTimer = setInterval(async () => {
      try {
        const health = await this.checkHealth();
        if (!health.healthy) {
          this.emit('unhealthy', health);
          await this.restart();
        }
      } catch (error) {
        this.logger.error('Health check failed:', error);
      }
    }, 30000); // Every 30 seconds
  }
  
  async executeClaude(
    args: string[],
    projectId: string,
    agentConfig?: any,
    sessionId?: string
  ): Promise<ChildProcess> {
    const span = this.sentryService.getCurrentHub().getScope()?.getTransaction()?.startChild({
      op: 'conduit.execute',
      description: 'Execute Claude through Conduit'
    });
    
    try {
      // Get project configuration
      const projectConfig = await this.getProjectConfig(projectId);
      
      if (!projectConfig.enabled) {
        // Bypass Conduit if disabled
        return this.executeClaudeDirect(args);
      }
      
      // Check cost limits
      await this.checkCostLimits(projectId, projectConfig.costLimits);
      
      // Prepare environment
      const env = this.prepareEnvironment(projectId, projectConfig, agentConfig);
      
      // Apply custom routing rules
      const model = await this.applyRoutingRules(
        args,
        projectConfig.routingRules,
        { projectId, agentConfig, sessionId }
      );
      
      if (model) {
        // Override model in args if routing rule matched
        this.overrideModelInArgs(args, model);
      }
      
      // Execute through Conduit
      const process = spawn(this.conduitBinary, args, { env });
      
      // Setup monitoring
      this.monitorProcess(process, projectId, agentConfig?.id, sessionId);
      
      span?.setStatus('ok');
      return process;
      
    } catch (error) {
      span?.setStatus('internal_error');
      this.sentryService.captureException(error);
      throw error;
    } finally {
      span?.finish();
    }
  }
  
  private async applyRoutingRules(
    args: string[],
    rules: RoutingRule[],
    context: any
  ): Promise<string | null> {
    // Sort by priority (higher first)
    const sortedRules = [...rules]
      .filter(r => r.enabled)
      .sort((a, b) => b.priority - a.priority);
    
    for (const rule of sortedRules) {
      if (await this.evaluateRule(rule, args, context)) {
        this.logger.info(`Routing rule '${rule.name}' matched, using model: ${rule.targetModel}`);
        return rule.targetModel;
      }
    }
    
    return null;
  }
  
  private async evaluateRule(
    rule: RoutingRule,
    args: string[],
    context: any
  ): Promise<boolean> {
    const evaluate = async (condition: RuleCondition): Promise<boolean> => {
      const results = await Promise.all(
        condition.rules.map(r => this.evaluateCondition(r, args, context))
      );
      
      return condition.type === 'AND' 
        ? results.every(r => r)
        : results.some(r => r);
    };
    
    return evaluate(rule.conditions);
  }
  
  // ... more implementation
}
```

### 2.2 Test Implementation

```typescript
// src/main/services/conduit/conduit-service.test.ts
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ConduitService } from './conduit-service';
import { EventEmitter } from 'events';
import * as child_process from 'child_process';

vi.mock('child_process');

describe('ConduitService', () => {
  let service: ConduitService;
  let mockSentry: any;
  let mockSupabase: any;
  let mockLogger: any;
  
  beforeEach(() => {
    mockSentry = {
      startTransaction: vi.fn(() => ({
        setStatus: vi.fn(),
        finish: vi.fn(),
        startChild: vi.fn(() => ({
          setStatus: vi.fn(),
          finish: vi.fn()
        }))
      })),
      captureException: vi.fn(),
      getCurrentHub: vi.fn(() => ({
        getScope: vi.fn(() => ({
          getTransaction: vi.fn(() => ({
            startChild: vi.fn()
          }))
        }))
      }))
    };
    
    mockSupabase = {
      from: vi.fn(() => ({
        select: vi.fn(() => ({
          eq: vi.fn(() => ({
            single: vi.fn(() => Promise.resolve({
              data: {
                conduit_config: {
                  enabled: true,
                  defaultModel: 'claude-3-5-sonnet-20241022',
                  routingRules: [],
                  costLimits: { daily: null, monthly: null },
                  features: {
                    autoRouting: true,
                    costTracking: true,
                    usageAnalytics: true
                  }
                }
              }
            }))
          }))
        }))
      }))
    };
    
    mockLogger = {
      info: vi.fn(),
      error: vi.fn(),
      debug: vi.fn()
    };
    
    service = ConduitService.getInstance(mockSentry, mockSupabase, mockLogger);
  });
  
  afterEach(() => {
    vi.clearAllMocks();
  });
  
  describe('executeClaude', () => {
    it('should execute Claude through Conduit when enabled', async () => {
      const mockProcess = new EventEmitter() as any;
      mockProcess.stdout = new EventEmitter();
      mockProcess.stderr = new EventEmitter();
      mockProcess.pid = 12345;
      
      vi.spyOn(child_process, 'spawn').mockReturnValue(mockProcess);
      
      const args = ['--message', 'Test message'];
      const projectId = 'test-project';
      
      const result = await service.executeClaude(args, projectId);
      
      expect(result).toBe(mockProcess);
      expect(child_process.spawn).toHaveBeenCalledWith(
        expect.any(String),
        args,
        expect.objectContaining({
          env: expect.objectContaining({
            SYNAPSE_PROJECT_ID: projectId
          })
        })
      );
    });
    
    it('should bypass Conduit when disabled', async () => {
      mockSupabase.from = vi.fn(() => ({
        select: vi.fn(() => ({
          eq: vi.fn(() => ({
            single: vi.fn(() => Promise.resolve({
              data: {
                conduit_config: {
                  enabled: false,
                  defaultModel: 'claude-3-5-sonnet-20241022'
                }
              }
            }))
          }))
        }))
      }));
      
      const mockProcess = new EventEmitter() as any;
      vi.spyOn(service as any, 'executeClaudeDirect').mockResolvedValue(mockProcess);
      
      const result = await service.executeClaude(['--message', 'Test'], 'project-id');
      
      expect(result).toBe(mockProcess);
      expect((service as any).executeClaudeDirect).toHaveBeenCalled();
    });
    
    it('should apply routing rules correctly', async () => {
      const rules = [{
        id: 'rule-1',
        name: 'Large Context Rule',
        enabled: true,
        priority: 10,
        conditions: {
          type: 'AND' as const,
          rules: [{
            field: 'tokenCount',
            operator: '>' as const,
            value: 50000
          }]
        },
        targetModel: 'claude-3-5-sonnet-20241022'
      }];
      
      mockSupabase.from = vi.fn(() => ({
        select: vi.fn(() => ({
          eq: vi.fn(() => ({
            single: vi.fn(() => Promise.resolve({
              data: {
                conduit_config: {
                  enabled: true,
                  defaultModel: 'claude-3-5-haiku-20241022',
                  routingRules: rules
                }
              }
            }))
          }))
        }))
      }));
      
      // Mock token count evaluation
      vi.spyOn(service as any, 'getTokenCount').mockResolvedValue(60000);
      
      const mockProcess = new EventEmitter() as any;
      vi.spyOn(child_process, 'spawn').mockReturnValue(mockProcess);
      
      await service.executeClaude(['--message', 'Long message'], 'project-id');
      
      // Verify model was overridden
      expect(child_process.spawn).toHaveBeenCalledWith(
        expect.any(String),
        expect.arrayContaining(['--model', 'claude-3-5-sonnet-20241022']),
        expect.any(Object)
      );
    });
    
    it('should track usage when cost tracking is enabled', async () => {
      const mockProcess = new EventEmitter() as any;
      mockProcess.stdout = new EventEmitter();
      mockProcess.stderr = new EventEmitter();
      
      vi.spyOn(child_process, 'spawn').mockReturnValue(mockProcess);
      vi.spyOn(service, 'emit');
      
      await service.executeClaude(['--message', 'Test'], 'project-id');
      
      // Simulate Conduit metadata
      const metadata = {
        type: 'conduit_metadata',
        data: {
          routingDecision: {
            model: 'claude-3-5-sonnet-20241022',
            source: 'default',
            tokenCount: 150
          },
          usage: {
            inputTokens: 100,
            outputTokens: 50,
            cost: 0.0015
          }
        }
      };
      
      mockProcess.stdout.emit('data', JSON.stringify(metadata) + '\n');
      
      expect(service.emit).toHaveBeenCalledWith('usage-tracked', expect.objectContaining({
        projectId: 'project-id',
        model: 'claude-3-5-sonnet-20241022',
        inputTokens: 100,
        outputTokens: 50,
        cost: 0.0015
      }));
    });
    
    it('should enforce cost limits', async () => {
      mockSupabase.from = vi.fn(() => ({
        select: vi.fn(() => ({
          eq: vi.fn(() => ({
            single: vi.fn(() => Promise.resolve({
              data: {
                conduit_config: {
                  enabled: true,
                  costLimits: { daily: 10, monthly: 100 }
                }
              }
            }))
          }))
        }))
      }));
      
      // Mock current usage exceeding limit
      vi.spyOn(service as any, 'getCurrentUsage').mockResolvedValue({
        daily: 11,
        monthly: 50
      });
      
      await expect(
        service.executeClaude(['--message', 'Test'], 'project-id')
      ).rejects.toThrow('Daily cost limit exceeded');
    });
  });
  
  describe('routing rules evaluation', () => {
    it('should evaluate complex AND conditions', async () => {
      const rule = {
        id: 'complex-rule',
        name: 'Complex Rule',
        enabled: true,
        priority: 1,
        conditions: {
          type: 'AND' as const,
          rules: [
            { field: 'tokenCount', operator: '>' as const, value: 1000 },
            { field: 'agentType', operator: '=' as const, value: 'research' },
            { field: 'message', operator: 'contains' as const, value: 'analyze' }
          ]
        },
        targetModel: 'claude-3-opus-20240229'
      };
      
      vi.spyOn(service as any, 'getTokenCount').mockResolvedValue(1500);
      
      const context = {
        agentConfig: { persona_type: 'research' }
      };
      
      const result = await (service as any).evaluateRule(
        rule,
        ['--message', 'Please analyze this data'],
        context
      );
      
      expect(result).toBe(true);
    });
    
    it('should evaluate OR conditions correctly', async () => {
      const rule = {
        conditions: {
          type: 'OR' as const,
          rules: [
            { field: 'tokenCount', operator: '>' as const, value: 100000 },
            { field: 'flags', operator: 'contains' as const, value: '--thinking' }
          ]
        }
      };
      
      vi.spyOn(service as any, 'getTokenCount').mockResolvedValue(500);
      
      const result = await (service as any).evaluateRule(
        rule,
        ['--message', 'Test', '--thinking'],
        {}
      );
      
      expect(result).toBe(true);
    });
  });
});
```

## Phase 3: UI Components (Week 2)

### 3.1 Model Configuration Component

```typescript
// src/renderer/components/model-config/ModelConfiguration.tsx
import React, { useState, useEffect, useCallback } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Input } from '@/components/ui/input';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge';
import { 
  Settings, 
  DollarSign, 
  Zap, 
  ChartBar, 
  Plus,
  Trash2,
  AlertCircle,
  CheckCircle
} from 'lucide-react';
import { RoutingRulesEditor } from './RoutingRulesEditor';
import { useDebounce } from '@/hooks/useDebounce';

interface ModelConfigurationProps {
  projectId: string;
  onSave?: (config: ConduitConfig) => void;
  onCancel?: () => void;
}

export const ModelConfiguration: React.FC<ModelConfigurationProps> = ({
  projectId,
  onSave,
  onCancel
}) => {
  const [config, setConfig] = useState<ConduitConfig>({
    enabled: false,
    defaultModel: 'claude-3-5-sonnet-20241022',
    routingRules: [],
    costLimits: {
      daily: null,
      monthly: null
    },
    features: {
      autoRouting: true,
      costTracking: true,
      usageAnalytics: true
    }
  });
  
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [isDirty, setIsDirty] = useState(false);
  const [testResult, setTestResult] = useState<any>(null);
  
  const debouncedConfig = useDebounce(config, 500);
  
  useEffect(() => {
    loadConfiguration();
  }, [projectId]);
  
  useEffect(() => {
    if (!loading) {
      setIsDirty(true);
    }
  }, [debouncedConfig]);
  
  const loadConfiguration = async () => {
    try {
      setLoading(true);
      const response = await window.api.getConduitConfig(projectId);
      if (response.success) {
        setConfig(response.config);
        setIsDirty(false);
      } else {
        throw new Error(response.error);
      }
    } catch (err) {
      setError(err.message);
      console.error('Failed to load configuration:', err);
    } finally {
      setLoading(false);
    }
  };
  
  const handleSave = async () => {
    try {
      setSaving(true);
      setError(null);
      
      // Validate configuration
      const validation = validateConfig(config);
      if (!validation.valid) {
        setError(validation.error);
        return;
      }
      
      const response = await window.api.saveConduitConfig(projectId, config);
      if (response.success) {
        setIsDirty(false);
        onSave?.(config);
      } else {
        throw new Error(response.error);
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setSaving(false);
    }
  };
  
  const testConfiguration = async () => {
    try {
      setTestResult(null);
      const response = await window.api.testConduitConfig(projectId, config);
      setTestResult(response);
    } catch (err) {
      setTestResult({ success: false, error: err.message });
    }
  };
  
  if (loading) {
    return <div className="flex items-center justify-center p-8">Loading configuration...</div>;
  }
  
  return (
    <div className="space-y-6">
      <Card>
        <CardHeader>
          <CardTitle className="flex items-center justify-between">
            <span className="flex items-center gap-2">
              <Settings className="h-5 w-5" />
              Conduit Configuration
            </span>
            <Badge variant={config.enabled ? 'default' : 'secondary'}>
              {config.enabled ? 'Enabled' : 'Disabled'}
            </Badge>
          </CardTitle>
        </CardHeader>
        <CardContent>
          <Tabs defaultValue="general" className="space-y-4">
            <TabsList className="grid w-full grid-cols-4">
              <TabsTrigger value="general">General</TabsTrigger>
              <TabsTrigger value="routing">Routing Rules</TabsTrigger>
              <TabsTrigger value="costs">Cost Limits</TabsTrigger>
              <TabsTrigger value="features">Features</TabsTrigger>
            </TabsList>
            
            <TabsContent value="general" className="space-y-4">
              <div className="flex items-center justify-between">
                <div className="space-y-0.5">
                  <Label htmlFor="enabled">Enable Conduit Integration</Label>
                  <p className="text-sm text-muted-foreground">
                    Use Conduit for intelligent model routing and usage tracking
                  </p>
                </div>
                <Switch
                  id="enabled"
                  checked={config.enabled}
                  onCheckedChange={(enabled) => 
                    setConfig(prev => ({ ...prev, enabled }))
                  }
                />
              </div>
              
              <div className="space-y-2">
                <Label htmlFor="defaultModel">Default Model</Label>
                <Select
                  value={config.defaultModel}
                  onValueChange={(model) => 
                    setConfig(prev => ({ ...prev, defaultModel: model }))
                  }
                  disabled={!config.enabled}
                >
                  <SelectTrigger id="defaultModel">
                    <SelectValue placeholder="Select default model" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="claude-3-5-sonnet-20241022">
                      Claude 3.5 Sonnet (Balanced)
                    </SelectItem>
                    <SelectItem value="claude-3-5-haiku-20241022">
                      Claude 3.5 Haiku (Fast & Efficient)
                    </SelectItem>
                    <SelectItem value="claude-3-opus-20240229">
                      Claude 3 Opus (Most Capable)
                    </SelectItem>
                  </SelectContent>
                </Select>
                <p className="text-xs text-muted-foreground">
                  Model used when no routing rules match
                </p>
              </div>
              
              {config.enabled && (
                <Alert>
                  <AlertCircle className="h-4 w-4" />
                  <AlertDescription>
                    When enabled, all Claude requests will be routed through Conduit 
                    for optimal model selection and usage tracking.
                  </AlertDescription>
                </Alert>
              )}
            </TabsContent>
            
            <TabsContent value="routing" className="space-y-4">
              <RoutingRulesEditor
                rules={config.routingRules}
                onChange={(rules) => 
                  setConfig(prev => ({ ...prev, routingRules: rules }))
                }
                disabled={!config.enabled || !config.features.autoRouting}
              />
            </TabsContent>
            
            <TabsContent value="costs" className="space-y-4">
              <div className="space-y-4">
                <div>
                  <Label htmlFor="dailyLimit" className="flex items-center gap-2">
                    <DollarSign className="h-4 w-4" />
                    Daily Cost Limit
                  </Label>
                  <div className="flex items-center gap-2 mt-2">
                    <span>$</span>
                    <Input
                      id="dailyLimit"
                      type="number"
                      placeholder="No limit"
                      value={config.costLimits.daily || ''}
                      onChange={(e) => 
                        setConfig(prev => ({
                          ...prev,
                          costLimits: {
                            ...prev.costLimits,
                            daily: e.target.value ? parseFloat(e.target.value) : null
                          }
                        }))
                      }
                      disabled={!config.enabled}
                      min="0"
                      step="0.01"
                    />
                  </div>
                </div>
                
                <div>
                  <Label htmlFor="monthlyLimit" className="flex items-center gap-2">
                    <DollarSign className="h-4 w-4" />
                    Monthly Cost Limit
                  </Label>
                  <div className="flex items-center gap-2 mt-2">
                    <span>$</span>
                    <Input
                      id="monthlyLimit"
                      type="number"
                      placeholder="No limit"
                      value={config.costLimits.monthly || ''}
                      onChange={(e) => 
                        setConfig(prev => ({
                          ...prev,
                          costLimits: {
                            ...prev.costLimits,
                            monthly: e.target.value ? parseFloat(e.target.value) : null
                          }
                        }))
                      }
                      disabled={!config.enabled}
                      min="0"
                      step="0.01"
                    />
                  </div>
                </div>
                
                <Alert>
                  <AlertCircle className="h-4 w-4" />
                  <AlertDescription>
                    When a cost limit is reached, requests will be blocked until the 
                    limit period resets. Set to empty for no limit.
                  </AlertDescription>
                </Alert>
              </div>
            </TabsContent>
            
            <TabsContent value="features" className="space-y-4">
              <div className="space-y-4">
                <div className="flex items-center justify-between">
                  <div className="space-y-0.5">
                    <Label htmlFor="autoRouting" className="flex items-center gap-2">
                      <Zap className="h-4 w-4" />
                      Automatic Routing
                    </Label>
                    <p className="text-sm text-muted-foreground">
                      Automatically select the best model based on context
                    </p>
                  </div>
                  <Switch
                    id="autoRouting"
                    checked={config.features.autoRouting}
                    onCheckedChange={(autoRouting) => 
                      setConfig(prev => ({
                        ...prev,
                        features: { ...prev.features, autoRouting }
                      }))
                    }
                    disabled={!config.enabled}
                  />
                </div>
                
                <div className="flex items-center justify-between">
                  <div className="space-y-0.5">
                    <Label htmlFor="costTracking" className="flex items-center gap-2">
                      <DollarSign className="h-4 w-4" />
                      Cost Tracking
                    </Label>
                    <p className="text-sm text-muted-foreground">
                      Track costs for each request and model
                    </p>
                  </div>
                  <Switch
                    id="costTracking"
                    checked={config.features.costTracking}
                    onCheckedChange={(costTracking) => 
                      setConfig(prev => ({
                        ...prev,
                        features: { ...prev.features, costTracking }
                      }))
                    }
                    disabled={!config.enabled}
                  />
                </div>
                
                <div className="flex items-center justify-between">
                  <div className="space-y-0.5">
                    <Label htmlFor="usageAnalytics" className="flex items-center gap-2">
                      <ChartBar className="h-4 w-4" />
                      Usage Analytics
                    </Label>
                    <p className="text-sm text-muted-foreground">
                      Collect detailed usage statistics and insights
                    </p>
                  </div>
                  <Switch
                    id="usageAnalytics"
                    checked={config.features.usageAnalytics}
                    onCheckedChange={(usageAnalytics) => 
                      setConfig(prev => ({
                        ...prev,
                        features: { ...prev.features, usageAnalytics }
                      }))
                    }
                    disabled={!config.enabled}
                  />
                </div>
              </div>
            </TabsContent>
          </Tabs>
          
          {error && (
            <Alert variant="destructive" className="mt-4">
              <AlertCircle className="h-4 w-4" />
              <AlertDescription>{error}</AlertDescription>
            </Alert>
          )}
          
          {testResult && (
            <Alert variant={testResult.success ? 'default' : 'destructive'} className="mt-4">
              {testResult.success ? (
                <CheckCircle className="h-4 w-4" />
              ) : (
                <AlertCircle className="h-4 w-4" />
              )}
              <AlertDescription>
                {testResult.success 
                  ? 'Configuration test passed! Conduit is working correctly.'
                  : `Test failed: ${testResult.error}`
                }
              </AlertDescription>
            </Alert>
          )}
          
          <div className="flex justify-between items-center mt-6">
            <Button
              variant="outline"
              onClick={testConfiguration}
              disabled={!config.enabled || saving}
            >
              Test Configuration
            </Button>
            
            <div className="flex gap-2">
              {onCancel && (
                <Button
                  variant="outline"
                  onClick={onCancel}
                  disabled={saving}
                >
                  Cancel
                </Button>
              )}
              <Button
                onClick={handleSave}
                disabled={!isDirty || saving}
              >
                {saving ? 'Saving...' : 'Save Configuration'}
              </Button>
            </div>
          </div>
        </CardContent>
      </Card>
    </div>
  );
};

// Validation function
function validateConfig(config: ConduitConfig): { valid: boolean; error?: string } {
  if (config.enabled && !config.defaultModel) {
    return { valid: false, error: 'Default model is required when Conduit is enabled' };
  }
  
  if (config.costLimits.daily && config.costLimits.daily < 0) {
    return { valid: false, error: 'Daily cost limit must be positive' };
  }
  
  if (config.costLimits.monthly && config.costLimits.monthly < 0) {
    return { valid: false, error: 'Monthly cost limit must be positive' };
  }
  
  if (config.costLimits.daily && config.costLimits.monthly && 
      config.costLimits.daily > config.costLimits.monthly) {
    return { valid: false, error: 'Daily limit cannot exceed monthly limit' };
  }
  
  // Validate routing rules
  for (const rule of config.routingRules) {
    if (!rule.name || !rule.targetModel) {
      return { valid: false, error: 'All routing rules must have a name and target model' };
    }
    
    if (!rule.conditions || rule.conditions.rules.length === 0) {
      return { valid: false, error: `Routing rule "${rule.name}" must have at least one condition` };
    }
  }
  
  return { valid: true };
}
```

### 3.2 Component Tests

```typescript
// src/renderer/components/model-config/ModelConfiguration.test.tsx
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ModelConfiguration } from './ModelConfiguration';

// Mock window.api
const mockApi = {
  getConduitConfig: vi.fn(),
  saveConduitConfig: vi.fn(),
  testConduitConfig: vi.fn()
};

(global as any).window = {
  api: mockApi
};

describe('ModelConfiguration', () => {
  const defaultProps = {
    projectId: 'test-project',
    onSave: vi.fn(),
    onCancel: vi.fn()
  };
  
  beforeEach(() => {
    vi.clearAllMocks();
    mockApi.getConduitConfig.mockResolvedValue({
      success: true,
      config: {
        enabled: false,
        defaultModel: 'claude-3-5-sonnet-20241022',
        routingRules: [],
        costLimits: { daily: null, monthly: null },
        features: {
          autoRouting: true,
          costTracking: true,
          usageAnalytics: true
        }
      }
    });
  });
  
  it('should load configuration on mount', async () => {
    render(<ModelConfiguration {...defaultProps} />);
    
    await waitFor(() => {
      expect(mockApi.getConduitConfig).toHaveBeenCalledWith('test-project');
    });
    
    expect(screen.getByText('Disabled')).toBeInTheDocument();
  });
  
  it('should enable/disable Conduit integration', async () => {
    const user = userEvent.setup();
    render(<ModelConfiguration {...defaultProps} />);
    
    await waitFor(() => {
      expect(screen.getByLabelText('Enable Conduit Integration')).toBeInTheDocument();
    });
    
    const toggle = screen.getByLabelText('Enable Conduit Integration');
    expect(toggle).not.toBeChecked();
    
    await user.click(toggle);
    expect(toggle).toBeChecked();
    
    await waitFor(() => {
      expect(screen.getByText('Enabled')).toBeInTheDocument();
    });
  });
  
  it('should validate cost limits', async () => {
    const user = userEvent.setup();
    render(<ModelConfiguration {...defaultProps} />);
    
    await waitFor(() => {
      expect(screen.getByText('Cost Limits')).toBeInTheDocument();
    });
    
    await user.click(screen.getByText('Cost Limits'));
    
    const dailyInput = screen.getByLabelText('Daily Cost Limit');
    const monthlyInput = screen.getByLabelText('Monthly Cost Limit');
    
    // Set invalid limits (daily > monthly)
    await user.type(dailyInput, '100');
    await user.type(monthlyInput, '50');
    
    mockApi.saveConduitConfig.mockResolvedValue({ success: false });
    
    await user.click(screen.getByText('Save Configuration'));
    
    await waitFor(() => {
      expect(screen.getByText('Daily limit cannot exceed monthly limit')).toBeInTheDocument();
    });
  });
  
  it('should test configuration', async () => {
    const user = userEvent.setup();
    render(<ModelConfiguration {...defaultProps} />);
    
    await waitFor(() => {
      expect(screen.getByText('General')).toBeInTheDocument();
    });
    
    // Enable Conduit first
    await user.click(screen.getByLabelText('Enable Conduit Integration'));
    
    mockApi.testConduitConfig.mockResolvedValue({
      success: true,
      message: 'Configuration valid'
    });
    
    await user.click(screen.getByText('Test Configuration'));
    
    await waitFor(() => {
      expect(mockApi.testConduitConfig).toHaveBeenCalledWith(
        'test-project',
        expect.objectContaining({ enabled: true })
      );
      expect(screen.getByText(/Configuration test passed/)).toBeInTheDocument();
    });
  });
  
  it('should handle save errors gracefully', async () => {
    const user = userEvent.setup();
    render(<ModelConfiguration {...defaultProps} />);
    
    await waitFor(() => {
      expect(screen.getByText('General')).toBeInTheDocument();
    });
    
    mockApi.saveConduitConfig.mockRejectedValue(new Error('Network error'));
    
    await user.click(screen.getByLabelText('Enable Conduit Integration'));
    await user.click(screen.getByText('Save Configuration'));
    
    await waitFor(() => {
      expect(screen.getByText('Network error')).toBeInTheDocument();
    });
  });
  
  it('should show dirty state when configuration changes', async () => {
    const user = userEvent.setup();
    render(<ModelConfiguration {...defaultProps} />);
    
    await waitFor(() => {
      expect(screen.getByText('Save Configuration')).toBeDisabled();
    });
    
    await user.click(screen.getByLabelText('Enable Conduit Integration'));
    
    await waitFor(() => {
      expect(screen.getByText('Save Configuration')).toBeEnabled();
    });
  });
});
```

## Phase 4: Migration & Rollout (Week 3)

### 4.1 Data Export/Import Utilities

```typescript
// src/main/services/migration/export-import.ts
import fs from 'fs/promises';
import path from 'path';
import { app } from 'electron';
import * as Sentry from '@sentry/electron/main';

export class MigrationService {
  private backupDir: string;
  
  constructor(
    private supabaseService: any,
    private localDb: any
  ) {
    this.backupDir = path.join(app.getPath('userData'), 'backups');
  }
  
  async exportData(projectId?: string): Promise<string> {
    const transaction = Sentry.startTransaction({
      op: 'migration.export',
      name: 'Export Data'
    });
    
    try {
      await fs.mkdir(this.backupDir, { recursive: true });
      
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const filename = `backup-${timestamp}.json`;
      const filepath = path.join(this.backupDir, filename);
      
      const data = {
        version: '1.0',
        timestamp,
        projects: [],
        agents: [],
        sessions: [],
        usage: []
      };
      
      // Export from Supabase
      if (projectId) {
        const { data: project } = await this.supabaseService.client
          .from('projects')
          .select('*')
          .eq('id', projectId)
          .single();
        
        data.projects = [project];
        
        const { data: agents } = await this.supabaseService.client
          .from('agents')
          .select('*')
          .eq('project_id', projectId);
        
        data.agents = agents || [];
        
        // Export usage data
        const { data: usage } = await this.supabaseService.client
          .from('usage_tracking')
          .select('*')
          .eq('project_id', projectId)
          .order('created_at', { ascending: false })
          .limit(1000);
        
        data.usage = usage || [];
      }
      
      // Export from local SQLite
      const sessions = await this.localDb.all(`
        SELECT * FROM sessions 
        WHERE project_id = ? OR ? IS NULL
        ORDER BY updated_at DESC
        LIMIT 100
      `, [projectId, projectId]);
      
      data.sessions = sessions;
      
      await fs.writeFile(filepath, JSON.stringify(data, null, 2));
      
      transaction.setStatus('ok');
      return filepath;
      
    } catch (error) {
      transaction.setStatus('internal_error');
      Sentry.captureException(error);
      throw error;
    } finally {
      transaction.finish();
    }
  }
  
  async importData(filepath: string): Promise<void> {
    const transaction = Sentry.startTransaction({
      op: 'migration.import',
      name: 'Import Data'
    });
    
    try {
      const content = await fs.readFile(filepath, 'utf-8');
      const data = JSON.parse(content);
      
      // Validate version
      if (data.version !== '1.0') {
        throw new Error(`Unsupported backup version: ${data.version}`);
      }
      
      // Import projects
      for (const project of data.projects) {
        await this.supabaseService.client
          .from('projects')
          .upsert(project, { onConflict: 'id' });
      }
      
      // Import agents
      for (const agent of data.agents) {
        await this.supabaseService.client
          .from('agents')
          .upsert(agent, { onConflict: 'id' });
      }
      
      // Import sessions to local DB
      for (const session of data.sessions) {
        await this.localDb.run(`
          INSERT OR REPLACE INTO sessions 
          (id, project_id, agent_id, title, created_at, updated_at)
          VALUES (?, ?, ?, ?, ?, ?)
        `, [
          session.id,
          session.project_id,
          session.agent_id,
          session.title,
          session.created_at,
          session.updated_at
        ]);
      }
      
      transaction.setStatus('ok');
      
    } catch (error) {
      transaction.setStatus('internal_error');
      Sentry.captureException(error);
      throw error;
    } finally {
      transaction.finish();
    }
  }
  
  async listBackups(): Promise<Array<{ filename: string; timestamp: string; size: number }>> {
    try {
      await fs.mkdir(this.backupDir, { recursive: true });
      const files = await fs.readdir(this.backupDir);
      
      const backups = await Promise.all(
        files
          .filter(f => f.endsWith('.json'))
          .map(async (filename) => {
            const filepath = path.join(this.backupDir, filename);
            const stats = await fs.stat(filepath);
            const match = filename.match(/backup-(.+)\.json/);
            
            return {
              filename,
              timestamp: match ? match[1].replace(/-/g, ':') : '',
              size: stats.size
            };
          })
      );
      
      return backups.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
      
    } catch (error) {
      console.error('Failed to list backups:', error);
      return [];
    }
  }
}
```

## Deployment Strategy

### Phased Rollout Plan

#### Phase A: Internal Testing (Week 3)
1. Deploy to test environment
2. Run integration test suite
3. Performance benchmarking
4. Security audit

#### Phase B: Beta Release (Week 4)
1. Feature flag controlled rollout
2. Enable for 10% of users initially
3. Monitor metrics:
   - Error rates
   - Performance impact
   - Cost tracking accuracy
   - User feedback

#### Phase C: Full Release (Week 5)
1. Gradual rollout to 100%
2. Documentation updates
3. Support team training
4. Marketing announcement

### Monitoring & Alerts

```typescript
// src/main/services/monitoring/conduit-monitoring.ts
export class ConduitMonitoring {
  constructor(private sentry: typeof Sentry) {}
  
  setupMonitoring() {
    // Performance monitoring
    this.sentry.addGlobalEventProcessor((event) => {
      if (event.contexts?.trace?.op?.startsWith('conduit.')) {
        event.tags = {
          ...event.tags,
          'conduit.enabled': 'true',
          'conduit.version': process.env.CONDUIT_VERSION
        };
      }
      return event;
    });
    
    // Custom metrics
    this.trackMetric('conduit.requests.total', 'counter');
    this.trackMetric('conduit.requests.failed', 'counter');
    this.trackMetric('conduit.routing.duration', 'histogram');
    this.trackMetric('conduit.cost.daily', 'gauge');
  }
  
  private trackMetric(name: string, type: string) {
    // Implementation depends on metrics backend
  }
}
```

### Rollback Plan

1. **Feature Flag Disable**: Immediate disable via feature flag
2. **Configuration Override**: Force `enabled: false` for all projects
3. **Binary Fallback**: Direct Claude CLI execution path
4. **Data Preservation**: All tracking data retained for analysis

## Success Metrics

1. **Performance**
   - Routing decision latency < 50ms
   - No increase in overall request latency
   - 99.9% uptime for Conduit service

2. **Accuracy**
   - Cost tracking accuracy > 99%
   - Routing rule match rate > 90%
   - Model selection improvement > 20%

3. **User Satisfaction**
   - Feature adoption > 80% within 30 days
   - Support tickets < 1% of active users
   - Positive feedback > 90%

4. **Business Impact**
   - Cost reduction > 15% average
   - Model diversity increase > 30%
   - Analytics dashboard usage > 70%