feat(models): offer Fable 5.1 in the model picker and task routing

Claude Code 2.1.258's baked-in model catalog carries `claude-fable-5-1`
("Fable 5.1", June 2026 cutoff), but App Settings only offered Fable 5, so
pinning it meant hand-editing a case's .claude/settings.local.json.

Fable 5.1 is added exactly the way Fable 5 already is: a base option carrying
`data-ctx="1"` plus its `[1m]` companion row. The card grid and the context
switch are built from those options, so this needs no settings-ui.js logic
change at all - the card appears with the "1M capable" badge, the switch stays
live for it, and base + switch compose back into `claude-fable-5-1[1m]`.
Both strings are accepted by the CLI's catalog (verified: neither warns
`unrecognized_model`, unlike a made-up id).

The four per-agent-type routing selects and Default-for-tasks get the plain
`claude-fable-5-1` value, matching how Fable 5 appears there.

Deliberately NOT claimed: that Fable 5.1's 1M window is what sets it apart.
The catalog marks BOTH fable entries `native_1m` with a 1e6 window, so a
picker that gave 5.1 an always-on window and left 5 switchable would be
drawing a distinction the models do not have.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHZgohZC5TbetqgETDnAve
This commit is contained in:
Codeman maintainer
2026-09-02 08:52:00 +02:00
parent 71ffbf18e4
commit a78a3b3c69
3 changed files with 28 additions and 3 deletions
+19 -1
View File
@@ -120,13 +120,31 @@ describe('App Settings modal structure', () => {
const select = modal.match(/id="appSettingsClaudeModel"([\s\S]*?)<\/select>/)?.[1] ?? '';
// The cards render the base models; the [1m] rows exist so that base + the
// context switch can compose back into a real claudeModel value.
for (const value of ['opus[1m]', 'claude-fable-5[1m]', 'claude-opus-4-6[1m]']) {
for (const value of ['opus[1m]', 'claude-fable-5[1m]', 'claude-fable-5-1[1m]', 'claude-opus-4-6[1m]']) {
expect(select).toContain(`value="${value}"`);
}
expect(select).toContain('data-ctx="1"');
expect(modal).toContain('id="appSettingsOpusContext1m"');
});
it('models: offers Fable 5.1 as a card and to task routing', () => {
const modal = settingsModal();
const select = modal.match(/id="appSettingsClaudeModel"([\s\S]*?)<\/select>/)?.[1] ?? '';
// The cards are built from these options, so data-ctx is what keeps the 1M
// switch live for the model rather than greying the row out.
expect(select).toMatch(/value="claude-fable-5-1"[^>]*data-ctx="1"/);
for (const id of [
'appSettingsDefaultModel',
'appSettingsModelExplore',
'appSettingsModelImplement',
'appSettingsModelTest',
'appSettingsModelReview',
]) {
const routing = modal.match(new RegExp(`id="${id}"([\\s\\S]*?)</select>`))?.[1] ?? '';
expect(routing, `${id} does not offer Fable 5.1`).toContain('value="claude-fable-5-1"');
}
});
it('has retired the modal-tab chrome everywhere, not just here', () => {
// Session Options and Add Case moved onto this same `set-*` surface, so the
// old tab classes have no users left. A reappearance means a modal drifted