Skip to content

Fix: keep each tab's model across a restart - #57

Merged
DevMando merged 1 commit into
mainfrom
fix/per-tab-model-restore
Sep 7, 2026
Merged

DevMando merged 1 commit into
mainfrom
fix/per-tab-model-restore

Conversation

@DevMando

@DevMando DevMando commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

An agent whose model you had switched could come back on the default model after restarting the app, with the switch permanently lost. This holds each tab's model across a restart.

What was happening

Restoring a workspace starts every tab initializing at the same time, and each tab boots on the default model before being moved onto the model it was saved with.

The workspace file records the current model of every tab, not just the one that changed — and it gets written constantly during startup, because ordinary UI refreshes (connection status, model validation, MCP startup) all funnel into a save. A save landing in that window wrote the default over the model of every tab that had not switched yet.

The saved value was then gone. That tab came back on the default on the next launch, and the launch after that, with nothing left to recover from.

This is why it only appeared after a restart, and why it could look intermittent: whether the damage stuck depended on which tab won the race.

What changed

  • Workspace writes are held until every tab has finished restoring. The tab that finishes last performs a single save, at which point every tab reports its real model.
  • A tab whose restore never finishes keeps its saved model. If restoration is interrupted — the window is closed during startup, or a tab errors part way through — that tab is still sitting on the default it was seeded with. Writing that would discard the user's choice, so the saved model is kept until the tab has actually settled.

Scope and risk

Low, and confined to workspace persistence at startup. No change to how models are chosen, applied, or validated; per-agent settings and the saved default were already correct and are untouched. The worst case if the new guard misbehaved is a workspace write being skipped, which leaves the previous saved state in place rather than losing anything.

Verification

  • 303 Desktop tests pass; build clean with no warnings.
  • Verified by hand: two tabs on different non-default models, restart, both return on their own models.

Not covered by automated tests — this needs a live window and a real workspace file, which no test harness creates. It was checked manually instead, including watching the workspace file during startup to confirm a single write occurs after the tabs settle rather than several with defaults in them.

Note for reviewers

Investigation ruled out the more obvious suspect: per-agent config is deep-cloned from the defaults, only one class may write the config file, and the model field on the defaults is never assigned anywhere in Desktop. Switching a model in one agent has never affected the saved default or any other agent. The bug was entirely in what the workspace file recorded during restore.

Restoring a workspace starts every tab at once, and a tab boots on the default
model before InitTabAsync moves it onto its own saved one. SaveWorkspace records
the current model of EVERY tab, and it is reached constantly during startup:
StateChanged raises UpdateHeader, which raises HeaderChanged, which saves. Any
of those firing mid-restore stamped the default over a tab that had not switched
yet, and that tab came back on the default next launch with nothing to recover
from.

Workspace writes are now suppressed until the last tab finishes restoring, and
the tab that finishes last performs one save with every model settled.

A tab whose restore never completes is still sitting on the default it was
seeded with, so persisting that would discard the user's choice for good.
ModelRestorePending keeps the saved model in the workspace until RestoreTabAsync
runs to completion; it is cleared after the await rather than in the finally, so
a throw part way through leaves the saved model protected. This matters more now
that the final save is guaranteed rather than incidental.
@DevMando
DevMando merged commit 1acdeb5 into main Sep 7, 2026
1 check passed
@DevMando
DevMando deleted the fix/per-tab-model-restore branch September 7, 2026 23:50
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