fix(ui): stop the echo of a Default send from erasing the recorded Default

The send write was fixed, but the message that send echoes back arrives with
model metadata and no effort, and its model matches the one the send just
saved. That is exactly when `shouldPreserveManualModelOverride` declines to
protect the selection, so the history branch ran with no variant and recorded
"no choice" over the user's explicit `Default`. Picking Default, sending, then
switching agent and back still put the settings default in the picker.

A message carrying no effort is not evidence that the session has none. The
history branch now keeps whatever the session already recorded when the message
carries no variant, and a concrete historical effort still replaces it.

The behavior test ran the real guard through a mock that returned a fixed
answer, so the failing branch had no coverage. It now calls the real function
unless a test opts out, and the new case fails without this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZuVVgziiLjD81W5vaxdH2
This commit is contained in:
Iuliia Ivashko
2026-09-04 18:39:05 +03:00
co-authored by Claude Opus 5
parent 9299e2a28d
commit 16f6f27027
2 changed files with 53 additions and 6 deletions
@@ -878,11 +878,24 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
&& getModelVariantOptions(latestLoadedUserChoice.providerID, latestLoadedUserChoice.modelID).includes(latestLoadedUserChoice.variant)
? latestLoadedUserChoice.variant
: undefined;
const restoreAgentName = latestLoadedUserChoice.agent || currentAgentName || undefined;
// A message carrying no effort is not evidence that the user has none:
// a send under an explicit "Default" carries none either, and the echo
// of that very send arrives here. Keep what the session already
// recorded, and let a concrete historical effort replace it.
const restoredVariant = historicalVariant ?? (currentSessionId && restoreAgentName
? getAgentModelVariantForSession(
currentSessionId,
restoreAgentName,
latestLoadedUserChoice.providerID,
latestLoadedUserChoice.modelID,
)
: undefined);
const applyResult = applyModelSelectionWithVariant(
latestLoadedUserChoice.providerID,
latestLoadedUserChoice.modelID,
historicalVariant,
latestLoadedUserChoice.agent || currentAgentName || undefined,
restoredVariant,
restoreAgentName,
);
if (applyResult !== 'applied') {
return;
@@ -906,6 +919,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
latestLoadedUserChoice,
setAgent,
applyModelSelectionWithVariant,
getAgentModelVariantForSession,
getModelVariantOptions,
getSessionModelSelection,
resolveModelVariantSelection,
@@ -40,7 +40,7 @@ const provider = { id: PROVIDER_ID, name: PROVIDER_ID, models: [model] };
const agent = { name: AGENT, mode: 'primary' as const };
let latestUserChoice: UserModelChoice | null = null;
let preserveManualOverride = false;
let forcePreserveManualOverride: boolean | null = null;
/** Every effort written for the session, in order, including `undefined`. */
const variantWrites: VariantChoice[] = [];
@@ -175,9 +175,19 @@ const useUIStore = create(() => ({
const passthrough = ({ children }: React.PropsWithChildren) => <div>{children}</div>;
// Captured by value before the module is replaced: reading it back off the
// namespace afterwards would resolve to the replacement and recurse.
const { shouldPreserveManualModelOverride: realShouldPreserveManualModelOverride } =
await import('@/lib/messages/userModelChoice');
mock.module('@/lib/messages/userModelChoice', () => ({
findLatestUserModelChoice: () => latestUserChoice,
shouldPreserveManualModelOverride: () => preserveManualOverride,
// The real guard, unless a test opts out: whether it fires decides which
// restore branch runs, and the branch that erased a recorded Default is the
// one it declines to protect.
shouldPreserveManualModelOverride: (args: Parameters<typeof realShouldPreserveManualModelOverride>[0]) => (
forcePreserveManualOverride ?? realShouldPreserveManualModelOverride(args)
),
}));
mock.module('@/stores/useConfigStore', () => ({ useConfigStore }));
@@ -316,7 +326,7 @@ describe('ModelControls effort restore', () => {
variantWrites.length = 0;
overrideWrites.length = 0;
latestUserChoice = null;
preserveManualOverride = false;
forcePreserveManualOverride = null;
useSelectionStore.setState({ savedVariant: undefined });
useConfigStore.setState({
currentProviderId: PROVIDER_ID,
@@ -356,9 +366,32 @@ describe('ModelControls effort restore', () => {
}
});
test('the echo of a Default send does not erase the recorded Default', async () => {
// The reported repro. The send under "Default" carried no effort, so the
// message it echoes back carries none either, and its model matches the one
// the send saved — which is exactly when the manual-override guard declines
// to protect the selection and the history branch runs.
latestUserChoice = { id: 'msg-echo', agent: AGENT, providerID: PROVIDER_ID, modelID: MODEL_ID };
useSelectionStore.setState({ savedVariant: null });
useConfigStore.setState({
selectionSource: 'manual',
settingsDefaultVariant: 'low',
currentVariantSelection: { override: null, inherited: 'low' },
});
const { cleanup } = await renderModelControls();
try {
expect(useSelectionStore.getState().savedVariant).toBeNull();
expect(useConfigStore.getState().currentVariantSelection.override).toBeNull();
expect(useConfigStore.getState().currentVariant).toBe(undefined);
} finally {
await cleanup();
}
});
test('a preserved manual override keeps a recorded explicit Default', async () => {
latestUserChoice = { id: 'msg-3', agent: AGENT, providerID: PROVIDER_ID, modelID: MODEL_ID, variant: 'high' };
preserveManualOverride = true;
forcePreserveManualOverride = true;
useSelectionStore.setState({ savedVariant: null });
useConfigStore.setState({ selectionSource: 'manual' });