# Clavue v8.13.8 — model role registry + /provider routing integration + mixed-routing safety

## Summary

Single source of truth for "which Claude tier does this background task run on", with full integration into the existing `/provider modelRouting` system and defensive guards for mixed Claude+GPT routing.

Pre-v8.13.8, four backend LLM call sites were hard-coded to `getMainLoopModel()` or `getDefaultSonnetModel()`:
- `/compact` summarization → billed Opus rates for prose Sonnet handles equivalently
- SessionMemory auto-extract → forked sub-agent on Opus to do a structured `.md` edit
- `yoloClassifier` → Opus per tool call, for a yes/no classification
- `findRelevantMemories` → hard-coded Sonnet, ignoring users who set Haiku as default

## What changed

### Role registry (new)
- `src/utils/model/modelRolesCore.ts` — pure policy table + `getModelForRoleWith` resolver
- `src/utils/model/modelRoles.ts` — production wiring (`getMainLoopModel` / `getSmallFastModel` / `getDefaultSonnetModel` / `parseUserSpecifiedModel` + `getResolvedCurrentProviderProfile`)
- 7 roles: `mainLoop`, `summarize`, `extract`, `classify`, `searchMemory`, `quickSummary`, `background`

### /provider routing integration
Resolution order:
1. `CLAVUE_MODEL_<ROLE>` env var (operator override; takes effect without restart)
2. Active `/provider modelRouting` slot, walked through the same fallback chain as `providerModes.ts:getProviderModeSlotValues`
3. First-party default (Sonnet for prose roles, smallFast for short structured roles)

Per-role routing fallback chain (mirrors `providerModes.ts:111-137`):
- `summarize` → `generalModel` → `subagentModel` → `primaryModel`
- `extract` → `subagentModel` → `generalModel` → `primaryModel`
- `classify`, `searchMemory`, `quickSummary`, `background` → `smallFastModel` → `subagentModel` → `primaryModel`

A user who sets `smallFastModel = gpt-5.3-codex` in `/provider` now gets that model for `searchMemory` / `quickSummary` / `background` instead of the table silently falling through to first-party Haiku. No new settings schema introduced — reuses the existing 8-slot routing block.

### Mixed-routing safety (Claude + GPT)
- `src/utils/sideQuery.ts` — gates `output_config.format` on `modelSupportsStructuredOutputs(model)`. Mixed routing landing on GPT/Bedrock/Vertex no longer 400s on the Claude-only structured-outputs beta.
- `src/memdir/findRelevantMemories.ts` — short-circuits to `selectLocalRelevantMemoriesFromRanked` when the role lands on a model without structured outputs support (saves a doomed RTT, no behavior regression — local rank already produced a usable shortlist).
- `yoloClassifier` — already gated upstream by `modelSupportsAutoMode` (firstParty-only for external users); no change needed.

### Call sites migrated
- `src/services/compact/compact.ts` → `getModelForRole('summarize')`
- `src/services/SessionMemory/sessionMemory.ts` → `getModelForRole('extract')` on both auto and `/summary` paths
- `src/utils/permissions/yoloClassifier.ts` → `getModelForRole('classify')` fallback
- `src/memdir/findRelevantMemories.ts` → `getModelForRole('searchMemory')` + structured-outputs gate

### Tests
- `tests/model-roles.test.mjs` — 20 tests (defaults / env override / routing fallback chains / env-beats-routing / mixed-routing source-grep guards)

### Docs
- `docs/model-roles.md` — full spec: roles, resolution order, per-role fallback chains, mixed-routing safety table, example resolution for `gpt-5.5 + claude-opus-4-7-high + gpt-5.3-codex`

## Verification
- 1610 pass / 6 skip / 0 fail (`npm test`)
- `npm run check` full gate green (vendor:ripgrep → validate:repo → verify:dist → verify:source-build → check:source-purity → test)
- `npm run build` green; tarball `clavue-8.13.8.tgz`
