fix(composer): a degraded profile must not wipe the saved model
Found by adversarial review of this stack before it was pushed: one dropped GET /user/profile permanently cleared the user's composer model selection. WHY THIS MATTERS The stale-selection reset added earlier in this branch is destructive by design - when a stored `openrouter/*` / `vercel/*` id is absent from the user's saved router lists, useSelectedModel rewrites localStorage to the default so the composer never sends an id the backend would reject. That is right when the lists are TRUE. It is wrong when they are UNKNOWN. HOW THE WIPE HAPPENED UserProfileContext answers a failed profile fetch (after its one retry) with a local fallback profile: `apiKeysDegraded` goes true and every field is a placeholder, including `openRouterModels: []` and `vercelModels: []`. ChatInput passed those empty arrays into useSelectedModel exactly as if they had loaded, the reset effect saw the stored router id "missing from the saved lists", and persisted the default over it. The user's pick was gone for good - reconnecting could not bring it back, because the evidence had been overwritten. THE FIX ChatInput passes `null` (the hook's "still loading, leave it alone" signal) whenever the profile is degraded, so the reset runs only on a profile the server actually answered. A healthy profile that genuinely reports no saved router models still resets, unchanged. The `apiKeysDegraded` doc comment is widened to say what it really means: NOTHING on a degraded profile is an answer, so no normalization may key off it. Not fixed here, by design: a database that predates the 20260818_01 migration answers with real empty lists (the deploy-before- migrate tolerance in getUserRouterModels), so a router selection does reset there. That is a genuine "you have no saved router models" state from the server's point of view, and the selection would not work anyway until the migration lands. Tests: ChatInput.modelSelection.test.tsx renders the real hook behind a degraded and a healthy profile. Pre-fix the degraded case fails with `Expected "openrouter/pricy/frontier" / Received "gemini-3-flash-preview"` in localStorage; the healthy-empty case passes before and after, pinning that the reset itself was not weakened. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Repository | open-legal-products/mike |
|---|---|
| Author | Amal <mamalanand3@gmail.com> |
| Authored | |
| Committed | |
| Parents | c03145f3 |
| Stats | 3 files changed , +134 , -4 |
| Part of | Harden model-router selection and failure handling |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-e414b530.md
from inside the repo you want the change in.