Skip to content

fix(desktop): stop a session's effort and fast mode moving the composer pick - #322

Merged
OmarB97 merged 1 commit into
mainfrom
fix/spawn-model-override-composer-leak-fork-20260802
Aug 2, 2026
Merged

fix(desktop): stop a session's effort and fast mode moving the composer pick#322
OmarB97 merged 1 commit into
mainfrom
fix/spawn-model-override-composer-leak-fork-20260802

Conversation

@OmarB97

@OmarB97 OmarB97 commented Aug 2, 2026

Copy link
Copy Markdown
Owner

What this is

#318 stopped a spawned session's model from overwriting the composer's persisted selection. Reasoning effort and fast mode are sticky in exactly the same way, are read by the same function for the same purpose, and were left with exactly the same bug. This is that fix, applied to the two remaining fields.

I found this while fixing the context meter (#316) and originally wrote the model half too — #318 landed first and did it better, so this keeps only what is still missing and follows #318's pattern rather than competing with it.

The bug

$currentReasoningEffort and $currentFastMode persist to localStorage, and desktopSessionCreateParams reads both as what the next new chat runs. Four paths wrote session runtime values into them using the persisting setters, so whatever session you had open silently became your stored default:

Path When it fired
syncRuntimeMetadataToView every state flush of the focused session
applyRuntimeInfo session create
applyStoredSessionPreviewRuntimeInfo sidebar preview of a stored session
session.info handler in gateway-event.ts every heartbeat

The last one deserves a look. It sits inside the if (apply) block whose own comment explains why model/provider must not be written there — the guard was placed and then walked around by the lines below it. And apply is also true for a global broadcast when no session is active, so a heartbeat could rewrite the user's stored effort while they sat on a fresh draft, with nothing on screen to explain it.

The fix

Straight from #318: $activeSessionReasoningEffort / $activeSessionFastMode hold what the open session runs (unpersisted, null = no session owns the display), $primaryReasoningEffort / $primaryFastMode compute live ?? picked for the primary chat surface, and session-view.tsx displays those. Only a deliberate pick — model menu, presets, Settings — or the profile-default seed still writes $currentReasoningEffort / $currentFastMode.

clearActiveSessionModel became clearActiveSessionRuntime, since it now hands back all four mirrors. desktopSessionCreateParams is untouched and still reads the stored pick, which is the point.

One thing to check me on

Two existing assertions in use-session-state-cache.test.tsx moved from $currentReasoningEffort to $primaryReasoningEffort. That is not a failing test being edited into submission: those assertions test what the composer displays, and the atom holding that answer changed identity — the identical edit #318 made to the model assertions sitting beside them. Their meaning is unchanged, and the test still proves a background session's metadata does not paint the foreground.

Evidence

Proven both directions rather than assumed:

  • Revert only the use-session-state-cache.ts swap → 3 tests fail (expected 'high' to be 'low').
  • Revert only the gateway-event.ts swap → 2 tests fail (expected 'low' to be 'high').
  • End to end: with a stored pick of effort=high / fast=true, after a session reporting low / false, the next chat's session.create params still carry reasoning_effort: 'high' and fast: true.

Checks

🤖 Generated with Claude Code

…er pick

#318 stopped a spawned session's MODEL from overwriting the composer's
persisted selection. Reasoning effort and fast mode are sticky in exactly
the same way and were left with exactly the same bug.

Both persist to localStorage, and `desktopSessionCreateParams` reads both as
what the NEXT new chat runs — so writing a session's runtime values into them
re-aims every chat the user starts afterwards. Four paths did that, all with
the persisting setters: `syncRuntimeMetadataToView` on every state flush,
`applyRuntimeInfo` at session create, `applyStoredSessionPreviewRuntimeInfo`
on a sidebar preview, and the `session.info` handler in gateway-event.ts.

That last one is worth a reviewer's attention: it sits inside the `if (apply)`
block whose own comment explains why model/provider must not be written there,
and `apply` is also true for a global broadcast when NO session is active — so
a heartbeat could rewrite the stored effort while the user sat on a fresh
draft, with no session on screen to explain it.

The fix is #318's, applied to its two remaining fields:
$activeSessionReasoningEffort / $activeSessionFastMode hold what the open
session runs, `$primaryReasoningEffort` / `$primaryFastMode` compute
`live ?? picked` for the primary chat surface, and only a deliberate pick
(model menu, presets, Settings) or the profile-default seed still writes
$currentReasoningEffort / $currentFastMode. `clearActiveSessionModel` became
`clearActiveSessionRuntime` since it now hands back all four.

Two existing state-cache assertions moved from $currentReasoningEffort to
$primaryReasoningEffort. They are testing what the composer DISPLAYS, and the
atom holding that answer changed identity — the same edit #318 made to the
model assertions beside them. Their meaning is unchanged: a background
session's metadata still must not paint the foreground.

Proven both ways rather than assumed. Reverting just the state-cache swap
fails three tests (`expected 'high' to be 'low'`); reverting just the
gateway-event swap fails two (`expected 'low' to be 'high'`). End to end: with
a stored pick of effort=high/fast=true, a session reporting low/false leaves
the next chat's session.create params on high/true.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@OmarB97
OmarB97 merged commit 63639fd into main Aug 2, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant