feat(types): Add PhaseModelConfig for per-phase AI model selection

- Add PhaseModelConfig interface with 8 configurable phases:
  - Quick tasks: enhancement, fileDescription, imageDescription
  - Validation: validationModel
  - Generation: specGeneration, featureGeneration, backlogPlanning, projectAnalysis
- Add PhaseModelKey type for type-safe access
- Add DEFAULT_PHASE_MODELS with sensible defaults
- Add phaseModels field to GlobalSettings
- Mark legacy enhancementModel/validationModel as deprecated
- Export new types from @automaker/types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-30 01:55:21 +01:00
parent c1c2e706f0
commit a415ae6207
3 changed files with 822 additions and 3 deletions

View File

@@ -52,6 +52,8 @@ export type {
PlanningMode,
ThinkingLevel,
ModelProvider,
PhaseModelConfig,
PhaseModelKey,
KeyboardShortcuts,
AIProfile,
ProjectRef,
@@ -65,6 +67,7 @@ export type {
} from './settings.js';
export {
DEFAULT_KEYBOARD_SHORTCUTS,
DEFAULT_PHASE_MODELS,
DEFAULT_GLOBAL_SETTINGS,
DEFAULT_CREDENTIALS,
DEFAULT_PROJECT_SETTINGS,

View File

@@ -72,6 +72,40 @@ export type ThinkingLevel = 'none' | 'low' | 'medium' | 'high' | 'ultrathink';
/** ModelProvider - AI model provider for credentials and API key management */
export type ModelProvider = 'claude' | 'cursor';
/**
* PhaseModelConfig - Configuration for AI models used in different application phases
*
* Allows users to choose which model (Claude or Cursor) to use for each distinct
* operation in the application. This provides fine-grained control over cost,
* speed, and quality tradeoffs.
*/
export interface PhaseModelConfig {
// Quick tasks - recommend fast/cheap models (Haiku, Cursor auto)
/** Model for enhancing feature names and descriptions */
enhancementModel: AgentModel | CursorModelId;
/** Model for generating file context descriptions */
fileDescriptionModel: AgentModel | CursorModelId;
/** Model for analyzing and describing context images */
imageDescriptionModel: AgentModel | CursorModelId;
// Validation tasks - recommend smart models (Sonnet, Opus)
/** Model for validating and improving GitHub issues */
validationModel: AgentModel | CursorModelId;
// Generation tasks - recommend powerful models (Opus, Sonnet)
/** Model for generating full application specifications */
specGenerationModel: AgentModel | CursorModelId;
/** Model for creating features from specifications */
featureGenerationModel: AgentModel | CursorModelId;
/** Model for reorganizing and prioritizing backlog */
backlogPlanningModel: AgentModel | CursorModelId;
/** Model for analyzing project structure */
projectAnalysisModel: AgentModel | CursorModelId;
}
/** Keys of PhaseModelConfig for type-safe access */
export type PhaseModelKey = keyof PhaseModelConfig;
/**
* WindowBounds - Electron window position and size for persistence
*
@@ -298,10 +332,14 @@ export interface GlobalSettings {
/** Mute completion notification sound */
muteDoneSound: boolean;
// AI Model Selection
/** Which model to use for feature name/description enhancement */
// AI Model Selection (per-phase configuration)
/** Phase-specific AI model configuration */
phaseModels: PhaseModelConfig;
// Legacy AI Model Selection (deprecated - use phaseModels instead)
/** @deprecated Use phaseModels.enhancementModel instead */
enhancementModel: AgentModel;
/** Which model to use for GitHub issue validation */
/** @deprecated Use phaseModels.validationModel instead */
validationModel: AgentModel;
// Cursor CLI Settings (global)
@@ -452,6 +490,23 @@ export interface ProjectSettings {
* Default values and constants
*/
/** Default phase model configuration - sensible defaults for each task type */
export const DEFAULT_PHASE_MODELS: PhaseModelConfig = {
// Quick tasks - use fast models for speed and cost
enhancementModel: 'sonnet',
fileDescriptionModel: 'haiku',
imageDescriptionModel: 'haiku',
// Validation - use smart models for accuracy
validationModel: 'sonnet',
// Generation - use powerful models for quality
specGenerationModel: 'opus',
featureGenerationModel: 'sonnet',
backlogPlanningModel: 'sonnet',
projectAnalysisModel: 'sonnet',
};
/** Default keyboard shortcut bindings */
export const DEFAULT_KEYBOARD_SHORTCUTS: KeyboardShortcuts = {
board: 'K',
@@ -492,6 +547,7 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
defaultRequirePlanApproval: false,
defaultAIProfileId: null,
muteDoneSound: false,
phaseModels: DEFAULT_PHASE_MODELS,
enhancementModel: 'sonnet',
validationModel: 'opus',
enabledCursorModels: getAllCursorModelIds(),

View File

@@ -0,0 +1,760 @@
# Per-Phase AI Provider Configuration - Implementation Plan
> **Created**: 2024-12-30
> **Approach**: UI-First with incremental wiring
> **Estimated Total Effort**: 20-25 hours
## Overview
Allow users to configure which AI provider/model to use for each distinct phase of the application. This gives users fine-grained control over cost, speed, and quality tradeoffs.
---
## Current State Analysis
### Existing Settings Fields (UNUSED)
These fields already exist in `GlobalSettings` but are not wired up:
```typescript
// libs/types/src/settings.ts - Line ~50
enhancementModel: AgentModel; // Currently ignored, hardcoded to 'sonnet'
validationModel: AgentModel; // Currently ignored, hardcoded to 'opus'
```
### All AI Usage Phases
| Phase | Location | Current Model | Priority |
| ------------------- | -------------------------------- | ------------------ | -------- |
| Feature Execution | `auto-mode-service.ts` | Per-feature | ✅ Done |
| Enhancement | `enhance.ts` | Hardcoded `sonnet` | P1 |
| GitHub Validation | `validate-issue.ts` | Hardcoded `opus` | P1 |
| File Description | `describe-file.ts` | Hardcoded `haiku` | P2 |
| Image Description | `describe-image.ts` | Hardcoded `haiku` | P2 |
| App Spec Generation | `generate-spec.ts` | SDK default | P2 |
| Feature from Spec | `generate-features-from-spec.ts` | SDK default | P3 |
| Backlog Planning | `generate-plan.ts` | SDK default | P3 |
| Project Analysis | `analyze-project.ts` | Hardcoded default | P3 |
---
## Phase 1: Type Definitions & Settings Structure
**Effort**: 2-3 hours
**Files**: `libs/types/src/settings.ts`
### 1.1 Add PhaseModelConfig Type
```typescript
/**
* Configuration for AI models used in different application phases
*/
export interface PhaseModelConfig {
// Quick tasks - recommend fast/cheap models (Haiku, Cursor auto)
enhancementModel: AgentModel | CursorModelId;
fileDescriptionModel: AgentModel | CursorModelId;
imageDescriptionModel: AgentModel | CursorModelId;
// Validation tasks - recommend smart models (Sonnet, Opus)
validationModel: AgentModel | CursorModelId;
// Generation tasks - recommend powerful models (Opus, Sonnet)
specGenerationModel: AgentModel | CursorModelId;
featureGenerationModel: AgentModel | CursorModelId;
backlogPlanningModel: AgentModel | CursorModelId;
projectAnalysisModel: AgentModel | CursorModelId;
}
```
### 1.2 Update GlobalSettings
```typescript
export interface GlobalSettings {
// ... existing fields ...
// Phase-specific model configuration
phaseModels: PhaseModelConfig;
// Legacy fields (keep for backwards compatibility)
enhancementModel?: AgentModel; // Deprecated, use phaseModels
validationModel?: AgentModel; // Deprecated, use phaseModels
}
```
### 1.3 Default Values
```typescript
export const DEFAULT_PHASE_MODELS: PhaseModelConfig = {
// Quick tasks - use fast models
enhancementModel: 'sonnet',
fileDescriptionModel: 'haiku',
imageDescriptionModel: 'haiku',
// Validation - use smart models
validationModel: 'sonnet',
// Generation - use powerful models
specGenerationModel: 'opus',
featureGenerationModel: 'sonnet',
backlogPlanningModel: 'sonnet',
projectAnalysisModel: 'sonnet',
};
```
### 1.4 Migration Helper
```typescript
// In settings-service.ts
function migrateSettings(settings: GlobalSettings): GlobalSettings {
// Migrate legacy fields to new structure
if (!settings.phaseModels) {
settings.phaseModels = {
...DEFAULT_PHASE_MODELS,
enhancementModel: settings.enhancementModel || 'sonnet',
validationModel: settings.validationModel || 'opus',
};
}
return settings;
}
```
---
## Phase 2: Settings UI
**Effort**: 6-8 hours
**Files**:
- `apps/ui/src/components/views/settings-view.tsx`
- `apps/ui/src/components/views/settings-view/phase-models-tab.tsx` (new)
### 2.1 Create PhaseModelsTab Component
```
┌─────────────────────────────────────────────────────────────┐
│ AI Phase Configuration │
├─────────────────────────────────────────────────────────────┤
│ │
│ Configure which AI model to use for each application task. │
│ Cursor models require cursor-agent CLI installed. │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ QUICK TASKS │ │
│ │ Fast models recommended for speed and cost savings │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Feature Enhancement │ │
│ │ Improves feature names and descriptions │ │
│ │ [Claude Sonnet ▼] [Haiku] [Cursor Auto] │ │
│ │ │ │
│ │ File Descriptions │ │
│ │ Generates descriptions for context files │ │
│ │ [Claude Haiku ▼] [Sonnet] [Cursor Auto] │ │
│ │ │ │
│ │ Image Descriptions │ │
│ │ Analyzes and describes context images │ │
│ │ [Claude Haiku ▼] [Sonnet] [Cursor Auto] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ VALIDATION TASKS │ │
│ │ Smart models recommended for accuracy │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ GitHub Issue Validation │ │
│ │ Validates and improves GitHub issues │ │
│ │ [Claude Sonnet ▼] [Opus] [Cursor Sonnet] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GENERATION TASKS │ │
│ │ Powerful models recommended for quality │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ App Specification │ │
│ │ Generates full application specifications │ │
│ │ [Claude Opus ▼] [Sonnet] [Cursor Opus] │ │
│ │ │ │
│ │ Feature Generation │ │
│ │ Creates features from specifications │ │
│ │ [Claude Sonnet ▼] [Opus] [Cursor Auto] │ │
│ │ │ │
│ │ Backlog Planning │ │
│ │ Reorganizes and prioritizes backlog │ │
│ │ [Claude Sonnet ▼] [Opus] [Cursor Auto] │ │
│ │ │ │
│ │ Project Analysis │ │
│ │ Analyzes project structure for suggestions │ │
│ │ [Claude Sonnet ▼] [Opus] [Cursor Auto] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ [Reset to Defaults] [Save Changes] │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 PhaseModelSelector Component
Reusable component for selecting model per phase:
```typescript
interface PhaseModelSelectorProps {
phase: keyof PhaseModelConfig;
label: string;
description: string;
value: AgentModel | CursorModelId;
onChange: (value: AgentModel | CursorModelId) => void;
recommendedModels?: string[];
}
```
Features:
- Shows both Claude and Cursor models
- Indicates which provider each model uses
- Shows "Recommended" badge on suggested models
- Disables Cursor models if CLI not installed
- Shows thinking level indicator for supported models
### 2.3 Add Tab to Settings View
```typescript
// In settings-view.tsx, add new tab
const SETTINGS_TABS = [
{ id: 'general', label: 'General', icon: Settings },
{ id: 'ai-profiles', label: 'AI Profiles', icon: Bot },
{ id: 'phase-models', label: 'Phase Models', icon: Workflow }, // NEW
{ id: 'providers', label: 'Providers', icon: Key },
// ...
];
```
---
## Phase 3: Wire Enhancement Route (P1)
**Effort**: 2-3 hours
**Files**: `apps/server/src/routes/enhance-prompt/routes/enhance.ts`
### 3.1 Current Code
```typescript
// BEFORE - Hardcoded
const model = CLAUDE_MODEL_MAP.sonnet;
```
### 3.2 Updated Code
```typescript
// AFTER - Uses settings
import { SettingsService } from '@/services/settings-service.js';
import { ProviderFactory } from '@/providers/provider-factory.js';
const settingsService = new SettingsService(dataDir);
const settings = await settingsService.getSettings();
const modelId = settings.phaseModels?.enhancementModel || 'sonnet';
// Resolve to full model string
const provider = ProviderFactory.getProviderForModel(modelId);
const model = resolveModelString(modelId);
```
### 3.3 Test Cases
- [ ] Default behavior (uses sonnet) still works
- [ ] Changing to haiku in settings uses haiku
- [ ] Changing to cursor-auto routes to Cursor provider
- [ ] Invalid model falls back to default
---
## Phase 4: Wire Validation Route (P1)
**Effort**: 2-3 hours
**Files**: `apps/server/src/routes/github/routes/validate-issue.ts`
### 4.1 Current Code
```typescript
// BEFORE - Has model param but defaults to hardcoded
const model = request.body.model || 'opus';
```
### 4.2 Updated Code
```typescript
// AFTER - Uses settings as default
const settings = await settingsService.getSettings();
const defaultModel = settings.phaseModels?.validationModel || 'opus';
const model = request.body.model || defaultModel;
```
### 4.3 Test Cases
- [ ] Default uses configured model from settings
- [ ] Explicit model in request overrides settings
- [ ] Cursor models work for validation
---
## Phase 5: Wire Context Description Routes (P2)
**Effort**: 3-4 hours
**Files**:
- `apps/server/src/routes/context/routes/describe-file.ts`
- `apps/server/src/routes/context/routes/describe-image.ts`
### 5.1 Pattern
Same pattern as enhancement - replace hardcoded `haiku` with settings lookup.
### 5.2 Test Cases
- [ ] File description uses configured model
- [ ] Image description uses configured model (with vision support check)
- [ ] Fallback to haiku if model doesn't support vision
---
## Phase 6: Wire Generation Routes (P2)
**Effort**: 4-5 hours
**Files**:
- `apps/server/src/routes/app-spec/generate-spec.ts`
- `apps/server/src/routes/app-spec/generate-features-from-spec.ts`
### 6.1 Pattern
These routes use the Claude SDK directly. Need to:
1. Load settings
2. Resolve model string
3. Pass to SDK configuration
### 6.2 Test Cases
- [ ] App spec generation uses configured model
- [ ] Feature generation uses configured model
- [ ] Works with both Claude and Cursor providers
---
## Phase 7: Wire Remaining Routes (P3)
**Effort**: 4-5 hours
**Files**:
- `apps/server/src/routes/backlog-plan/generate-plan.ts`
- `apps/server/src/routes/auto-mode/routes/analyze-project.ts`
### 7.1 Pattern
Same settings injection pattern.
### 7.2 Test Cases
- [ ] Backlog planning uses configured model
- [ ] Project analysis uses configured model
---
## Implementation Order
```
Phase 1: Types & Settings Structure
Phase 2: Settings UI (Phase Models tab)
Phase 8: Quick Model Override Component <- Right after UI for easier testing
Phase 9: Integration Points for Override <- Wire override to each feature
Then wire routes (testing both global + override together):
Phase 3: Enhancement Route
Phase 4: Validation Route
Phase 5: Context Routes (file/image description)
Phase 6: Generation Routes (spec, features)
Phase 7: Remaining Routes (backlog, analysis)
```
---
## File Changes Summary
### New Files
- `apps/ui/src/components/views/settings-view/phase-models-tab.tsx`
- `apps/ui/src/components/views/settings-view/phase-model-selector.tsx`
### Modified Files
| File | Changes |
| ---------------------------------------------------------------- | ------------------------- |
| `libs/types/src/settings.ts` | Add PhaseModelConfig type |
| `apps/server/src/services/settings-service.ts` | Add migration logic |
| `apps/ui/src/components/views/settings-view.tsx` | Add Phase Models tab |
| `apps/server/src/routes/enhance-prompt/routes/enhance.ts` | Use settings |
| `apps/server/src/routes/github/routes/validate-issue.ts` | Use settings |
| `apps/server/src/routes/context/routes/describe-file.ts` | Use settings |
| `apps/server/src/routes/context/routes/describe-image.ts` | Use settings |
| `apps/server/src/routes/app-spec/generate-spec.ts` | Use settings |
| `apps/server/src/routes/app-spec/generate-features-from-spec.ts` | Use settings |
| `apps/server/src/routes/backlog-plan/generate-plan.ts` | Use settings |
| `apps/server/src/routes/auto-mode/routes/analyze-project.ts` | Use settings |
---
## Testing Strategy
### Unit Tests
- Settings migration preserves existing values
- Default values applied correctly
- Model resolution works for both providers
### Integration Tests
- Each phase uses configured model
- Provider factory routes correctly
- Cursor fallback when CLI not available
### E2E Tests
- Settings UI saves correctly
- Changes persist across restarts
- Each feature works with non-default model
---
## Rollback Plan
If issues arise:
1. All routes have fallback to hardcoded defaults
2. Settings migration is additive (doesn't remove old fields)
3. Can revert individual routes independently
---
## Success Criteria
- [ ] Users can configure model for each phase via Settings UI
- [ ] All 8+ phases respect configured model
- [ ] Cursor models work for all applicable phases
- [ ] Graceful fallback when Cursor CLI not available
- [ ] Settings persist across app restarts
- [ ] No regression in existing functionality
---
---
## Phase 8: Quick Model Override Component (P1)
**Effort**: 4-6 hours
**Files**:
- `apps/ui/src/components/shared/model-override-popover.tsx` (new)
- `apps/ui/src/components/shared/model-override-trigger.tsx` (new)
### 8.1 Concept
Global defaults are great, but users often want to override for a specific run:
- "Use Opus for this complex feature"
- "Use Cursor for this quick fix"
- "Use Haiku to save costs on this simple task"
### 8.2 Component: ModelOverrideTrigger
A small gear/settings icon that opens the override popover:
```typescript
interface ModelOverrideTriggerProps {
// Current effective model (from global settings or explicit override)
currentModel: string;
// Callback when user selects override
onModelChange: (model: string | null) => void;
// Optional: which phase this is for (shows recommended models)
phase?: keyof PhaseModelConfig;
// Size variants for different contexts
size?: 'sm' | 'md' | 'lg';
// Show as icon-only or with label
variant?: 'icon' | 'button' | 'inline';
}
```
### 8.3 Component: ModelOverridePopover
```
┌──────────────────────────────────────────────┐
│ Model Override [x] │
├──────────────────────────────────────────────┤
│ │
│ Current: Claude Sonnet (from settings) │
│ │
│ ○ Use Global Setting │
│ └─ Claude Sonnet │
│ │
│ ● Override for this run: │
│ │
│ CLAUDE │
│ ┌──────┐ ┌──────┐ ┌───────┐ │
│ │ Opus │ │Sonnet│ │ Haiku │ │
│ └──────┘ └──────┘ └───────┘ │
│ │
│ CURSOR │
│ ┌──────┐ ┌────────┐ ┌───────┐ │
│ │ Auto │ │Sonnet45│ │GPT-5.2│ │
│ └──────┘ └────────┘ └───────┘ │
│ │
│ [Clear Override] [Apply] │
│ │
└──────────────────────────────────────────────┘
```
### 8.4 Usage Examples
**In Feature Modal (existing model selector enhancement):**
```tsx
<div className="flex items-center gap-2">
<Label>Model</Label>
<ModelOverrideTrigger
currentModel={feature.model || globalDefault}
onModelChange={(model) => setFeature({ ...feature, model })}
phase="featureExecution"
size="md"
variant="button"
/>
</div>
```
**In Kanban Card Actions:**
```tsx
<CardActions>
<Button onClick={handleImplement}>Implement</Button>
<ModelOverrideTrigger
currentModel={feature.model}
onModelChange={handleQuickModelChange}
size="sm"
variant="icon"
/>
</CardActions>
```
**In Enhancement Dialog:**
```tsx
<DialogHeader>
<DialogTitle>Enhance Feature</DialogTitle>
<ModelOverrideTrigger
currentModel={settings.phaseModels.enhancementModel}
onModelChange={setEnhanceModel}
phase="enhancement"
size="sm"
variant="icon"
/>
</DialogHeader>
```
**In GitHub Issue Import:**
```tsx
<div className="flex justify-between">
<span>Validating issue...</span>
<ModelOverrideTrigger
currentModel={validationModel}
onModelChange={setValidationModel}
phase="validation"
size="sm"
variant="inline"
/>
</div>
```
### 8.5 Visual Variants
```
Icon Only (size=sm):
┌───┐
│ ⚙ │ <- Just gear icon, hover shows current model
└───┘
Button (size=md):
┌─────────────────┐
│ ⚙ Claude Sonnet │ <- Gear + model name
└─────────────────┘
Inline (size=sm):
Using Claude Sonnet ⚙ <- Text with gear at end
```
### 8.6 State Management
```typescript
// Hook for managing model overrides
function useModelOverride(phase: keyof PhaseModelConfig) {
const { settings } = useSettings();
const [override, setOverride] = useState<string | null>(null);
const effectiveModel = override || settings.phaseModels[phase];
const isOverridden = override !== null;
const clearOverride = () => setOverride(null);
return {
effectiveModel,
isOverridden,
setOverride,
clearOverride,
globalDefault: settings.phaseModels[phase],
};
}
```
### 8.7 Visual Feedback for Overrides
When a model is overridden from global:
- Show small indicator dot on the gear icon
- Different color tint on the trigger
- Tooltip shows "Overridden from global setting"
```tsx
// Indicator when overridden
<div className="relative">
<GearIcon />
{isOverridden && <div className="absolute -top-1 -right-1 w-2 h-2 bg-blue-500 rounded-full" />}
</div>
```
---
## Phase 9: Integration Points for Quick Override
**Effort**: 3-4 hours
### 9.1 Feature Modal
**File**: `apps/ui/src/components/views/board-view/components/feature-modal.tsx`
Replace current model selector with ModelOverrideTrigger:
- Shows inherited model from AI Profile
- Allows quick override for this feature
- Clear override returns to profile default
### 9.2 Kanban Card
**File**: `apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx`
Add small gear icon next to "Implement" button:
- Quick model change before running
- Doesn't persist to feature (one-time override)
### 9.3 Enhancement Dialog
**File**: `apps/ui/src/components/views/board-view/components/enhance-dialog.tsx`
Add override trigger in header:
- Default from global settings
- Override for this enhancement only
### 9.4 GitHub Import
**File**: `apps/ui/src/components/views/github-view/`
Add override for validation model:
- Default from global settings
- Override for this import session
---
## Updated Implementation Order
```
FOUNDATION:
├── Phase 1: Types & Settings Structure
├── Phase 2: Settings UI (Phase Models tab)
├── Phase 8: Quick Model Override Component
└── Phase 9: Integration Points (wire override to feature modal, kanban, etc.)
ROUTE WIRING (test both global settings + quick override for each):
├── Phase 3: Enhancement Route + Test global + override
├── Phase 4: Validation Route + Test global + override
├── Phase 5: Context Routes + Test global + override
├── Phase 6: Generation Routes + Test global + override
└── Phase 7: Remaining Routes + Test global + override
FINALIZATION:
├── Full Integration Testing
└── Documentation
```
---
## Architecture: Global vs Override
```
┌─────────────────────────────────────────────────────────────┐
│ Settings Hierarchy │
├─────────────────────────────────────────────────────────────┤
│ │
│ Level 1: Global Defaults (DEFAULT_PHASE_MODELS) │
│ │ │
│ ▼ │
│ Level 2: User Global Settings (settings.phaseModels) │
│ │ │
│ ▼ │
│ Level 3: Feature-Level Override (feature.model) │
│ │ │
│ ▼ │
│ Level 4: Run-Time Override (via ModelOverridePopover) │
│ │
│ Resolution: First non-null value wins (bottom-up) │
│ │
└─────────────────────────────────────────────────────────────┘
```
```typescript
function resolveModel(
phase: keyof PhaseModelConfig,
feature?: Feature,
runtimeOverride?: string
): string {
// Runtime override takes precedence
if (runtimeOverride) return runtimeOverride;
// Feature-level override
if (feature?.model) return feature.model;
// User global settings
const settings = getSettings();
if (settings.phaseModels?.[phase]) return settings.phaseModels[phase];
// Default
return DEFAULT_PHASE_MODELS[phase];
}
```
---
## Future Enhancements
1. **Per-Project Overrides**: Allow project-level phase model config
2. **Quick Presets**: "Cost Optimized", "Quality First", "Balanced" presets
3. **Usage Stats**: Show which models used for which phases
4. **Auto-Selection**: ML-based model selection based on task complexity
5. **Model History**: Remember last-used model per phase for quick access
6. **Keyboard Shortcuts**: Cmd+Shift+M to quickly change model anywhere