Problem
Live QA found a severe provider/model isolation bug across multiple CodeWhale terminals. Hunter had one CodeWhale session open with GLM 5.2 / Z.ai active, then in another terminal repeatedly changed provider/model back to DeepSeek / deepseek-v4-pro. On send, the second terminal showed an impossible route: the visible model chip said deepseek-v4-pro, the provider badge still said Z.ai, and the request failed with:
Invalid request (400): Unknown Model, please check the model code.
That means CodeWhale can currently combine a model from one provider with another provider route, probably due to shared/global provider-model persistence and/or non-atomic in-memory route updates. This breaks parallel QA, multi-terminal work, Fleet workers, and any user running different projects/models at once.
Why this matters
Changing a model in one terminal must not mutate the active provider/model route of another already-running terminal. A running TUI session should own its route until the user changes it in that session. Global config should only affect new sessions, or explicit save as default actions.
Current code pointers to inspect
crates/tui/src/commands/groups/core/core.rs
/model updates app.provider_models and calls Settings::persist_provider_model_selection(app.api_provider, model)
- that persistence also writes
default_provider, so a model change can become a global provider/default change
crates/tui/src/settings.rs
default_provider, default_model, and provider_models are shared settings
set_provider_model_selection writes both default_provider and provider-scoped model
crates/tui/src/tui/ui.rs
- model picker persists provider-scoped model changes with
settings.save()
- provider switch persists root
provider plus shared default_provider
- route changes need to update
app.api_provider, app.model, config.provider, provider model override, engine config, compaction budget, and header/status together
Desired behavior
-
Session-local by default. /model, model picker, and /provider should change the current TUI session route only unless the user explicitly chooses to persist/save as default.
-
Atomic provider/model route. CodeWhale must not send deepseek-v4-pro to Z.ai, GLM to DeepSeek, or any provider/model mismatch. Provider, model, base URL, auth mode, reasoning effort, compaction budget, and engine client should switch as one validated route.
-
No cross-terminal mutation. Terminal A changing model/provider must not affect terminal B until B explicitly reloads config or the user chooses a global default action. Already-running sessions should not silently rehydrate shared settings mid-turn.
-
Clear persistence UX. Separate commands/actions:
use this model/provider for this session
save this provider/model as global default for future sessions
- possibly
save for this workspace
-
Visible route truth. Header/status should show one coherent route: provider + model + effective route. If the route is invalid, block before API call with a local diagnostic that says which provider/model pair is incompatible.
-
Fleet/exec consistency. codewhale exec, Fleet workers, sub-agents, and the TUI should all receive an explicit resolved route from their launcher/session instead of racing global defaults.
Tests / verification
-
Add a two-session settings isolation test using separate App instances and the same temp settings/config path:
- Session A selects Z.ai / GLM.
- Session B selects DeepSeek /
deepseek-v4-pro.
- Sending from B must build a DeepSeek route, not Z.ai + DeepSeek model.
- Session A must remain Z.ai / GLM.
-
Add a route validation test that rejects mismatched tuples before network call: provider=zai + deepseek-v4-pro should fail locally with a precise error.
-
Add a regression test around /model proving it does not mutate shared default_provider unless an explicit persistence flag/action is used.
-
Add a provider-switch test proving app state, config state, engine config, and header display stay in sync after switching providers and immediately sending a message.
-
Add a Fleet/exec smoke where a TUI session using GLM and another using DeepSeek can launch workers without overwriting each other's route.
Related
Problem
Live QA found a severe provider/model isolation bug across multiple CodeWhale terminals. Hunter had one CodeWhale session open with GLM 5.2 / Z.ai active, then in another terminal repeatedly changed provider/model back to DeepSeek /
deepseek-v4-pro. On send, the second terminal showed an impossible route: the visible model chip saiddeepseek-v4-pro, the provider badge still saidZ.ai, and the request failed with:That means CodeWhale can currently combine a model from one provider with another provider route, probably due to shared/global provider-model persistence and/or non-atomic in-memory route updates. This breaks parallel QA, multi-terminal work, Fleet workers, and any user running different projects/models at once.
Why this matters
Changing a model in one terminal must not mutate the active provider/model route of another already-running terminal. A running TUI session should own its route until the user changes it in that session. Global config should only affect new sessions, or explicit
save as defaultactions.Current code pointers to inspect
crates/tui/src/commands/groups/core/core.rs/modelupdatesapp.provider_modelsand callsSettings::persist_provider_model_selection(app.api_provider, model)default_provider, so a model change can become a global provider/default changecrates/tui/src/settings.rsdefault_provider,default_model, andprovider_modelsare shared settingsset_provider_model_selectionwrites bothdefault_providerand provider-scoped modelcrates/tui/src/tui/ui.rssettings.save()providerplus shareddefault_providerapp.api_provider,app.model,config.provider, provider model override, engine config, compaction budget, and header/status togetherDesired behavior
Session-local by default.
/model, model picker, and/providershould change the current TUI session route only unless the user explicitly chooses to persist/save as default.Atomic provider/model route. CodeWhale must not send
deepseek-v4-proto Z.ai, GLM to DeepSeek, or any provider/model mismatch. Provider, model, base URL, auth mode, reasoning effort, compaction budget, and engine client should switch as one validated route.No cross-terminal mutation. Terminal A changing model/provider must not affect terminal B until B explicitly reloads config or the user chooses a global default action. Already-running sessions should not silently rehydrate shared settings mid-turn.
Clear persistence UX. Separate commands/actions:
use this model/provider for this sessionsave this provider/model as global default for future sessionssave for this workspaceVisible route truth. Header/status should show one coherent route: provider + model + effective route. If the route is invalid, block before API call with a local diagnostic that says which provider/model pair is incompatible.
Fleet/exec consistency.
codewhale exec, Fleet workers, sub-agents, and the TUI should all receive an explicit resolved route from their launcher/session instead of racing global defaults.Tests / verification
Add a two-session settings isolation test using separate
Appinstances and the same temp settings/config path:deepseek-v4-pro.Add a route validation test that rejects mismatched tuples before network call:
provider=zai+deepseek-v4-proshould fail locally with a precise error.Add a regression test around
/modelproving it does not mutate shareddefault_providerunless an explicit persistence flag/action is used.Add a provider-switch test proving app state, config state, engine config, and header display stay in sync after switching providers and immediately sending a message.
Add a Fleet/exec smoke where a TUI session using GLM and another using DeepSeek can launch workers without overwriting each other's route.
Related