fix(chat): an explicitly requested router model fails loudly, not silently
Adversarial review of this stack, pre-push: the allowlist choke point protected the operator's wallet but lied to the user. WHY THIS MATTERS When a request body names `openrouter/pricy/frontier` and that model is not in the user's saved selection, the previous behaviour was to answer anyway - on gemini-3-flash-preview - with nothing in the response saying so. The user asked one model a question and got another model's answer, attributed to the one they picked. That is worse than an error: it is undetectable from the client, and it makes model comparison, cost reasoning, and bug reports meaningless. WHY A SILENT FALLBACK IS STILL RIGHT ELSEWHERE There are two different situations behind "model not in selection": - The user named it in THIS request. They are present, they can act, and the wrong answer is expensive. Tell them. - A stored preference names it (title_model / tabular_model, resolved by getUserModelSettings). The user set it long ago, is not watching, and the alternative to degrading is bricking every background title generation. Degrade and warn the operator. So `resolveRequestedModel` gains an explicit `onOutsideSelection` mode rather than one hard-coded policy. runLLMStream - reached from chat, project chat and Word chat with the request body's model - passes "throw"; getUserModelSettings keeps its own silent guard for stored preferences, untouched. Tabular reaches runLLMStream with a preference getUserModelSettings has ALREADY normalized, so it cannot trip the throw. WHAT THE USER SEES "Model openrouter/pricy/frontier is not in your saved OpenRouter models - add it in Settings → BYOK → Routers." Because R3 moved the resolution inside the stream's try block, that message arrives as a normal SSE error event and is persisted with the turn, not as a dead socket. Tests: streamingModelAllowlist.test.ts's two fallback cases become rejection cases (and assert the adapter is never called), plus a new case pinning the message onto the stream's error event; routerModels.test.ts pins both modes directly. Pre-fix, all four fail - "promise resolved 'gemini-3-flash-preview' instead of rejecting" for the unit case and "promise resolved undefined instead of rejecting" for the stream cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Repository | open-legal-products/mike |
|---|---|
| Author | Amal <mamalanand3@gmail.com> |
| Authored | |
| Committed | |
| Parents | 3849a329 |
| Stats | 4 files changed , +108 , -18 |
| 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-8fdeac67.md
from inside the repo you want the change in.