Keep composer mode preferences stable#1658
Conversation
Treat undefined provider preference updates as no-ops, and keep saved mode selections instead of silently defaulting them when provider metadata is incomplete or does not list the saved mode.
|
| Filename | Overview |
|---|---|
| packages/app/src/create-agent-preferences/preferences.ts | Adds mergeDefinedRecord and applyProviderPreferenceUpdates helpers to replace the spread-based merge that could clobber saved fields with undefined; logic is correct and well-tested. |
| packages/app/src/provider-selection/resolve-agent-form.ts | Extracts resolvePreferredModeId to preserve saved mode IDs without validating against the provider's current mode list; three call sites updated consistently. |
| packages/app/src/create-agent-preferences/preferences.test.ts | Adds a targeted regression test covering the partial-update-with-undefined-mode scenario; tests cross the module interface and assert the full output shape. |
| packages/app/src/provider-selection/resolve-agent-form.test.ts | Adds two regression tests — one for the loading-snapshot case (no modes in definition) and one for a saved mode absent from the current mode list — both asserting specific modeId values. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Preference update arrives\nwith mode: undefined] --> B{applyProviderPreferenceUpdates}
B --> C{updates.mode !== undefined?}
C -- No --> D[Keep existing mode\nsaved value intact]
C -- Yes --> E[Write new mode value]
F[Form resolution triggered\neg. provider snapshot arrives] --> G{resolveModeId}
G --> H{userModified.modeId?}
H -- Yes --> I[Return currentModeId as-is]
H -- No --> J[resolvePreferredModeId]
J --> K{initialModeId non-empty?}
K -- Yes --> L[Return initialModeId\nno mode-list check]
K -- No --> M{preferredModeId non-empty?}
M -- Yes --> N[Return preferredModeId\nno mode-list check]
M -- No --> O[Return providerDef.defaultModeId\nor first mode or empty]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Preference update arrives\nwith mode: undefined] --> B{applyProviderPreferenceUpdates}
B --> C{updates.mode !== undefined?}
C -- No --> D[Keep existing mode\nsaved value intact]
C -- Yes --> E[Write new mode value]
F[Form resolution triggered\neg. provider snapshot arrives] --> G{resolveModeId}
G --> H{userModified.modeId?}
H -- Yes --> I[Return currentModeId as-is]
H -- No --> J[resolvePreferredModeId]
J --> K{initialModeId non-empty?}
K -- Yes --> L[Return initialModeId\nno mode-list check]
K -- No --> M{preferredModeId non-empty?}
M -- Yes --> N[Return preferredModeId\nno mode-list check]
M -- No --> O[Return providerDef.defaultModeId\nor first mode or empty]
Reviews (2): Last reviewed commit: "fix(app): document mode preference prese..." | Re-trigger Greptile
Linked issue
N/A
Type of change
What does this PR do
The new workspace composer no longer silently drops a saved mode preference when provider metadata is temporarily incomplete or when a later partial preference update omits
mode.Provider preference updates now treat
undefinedas "leave this saved value alone," while real values still update the selected model, mode, thinking choice, and feature values. The form resolver also preserves explicit saved mode selections instead of defaulting them just because the current mode list cannot validate them.How did you verify it
Reproduced the failure with red unit tests first:
full-accessmode was reset toautowhen a loading provider snapshot omitted modesmode: undefinederased the saved modeConfirmed green after the fix with:
npx vitest run packages/app/src/provider-selection/resolve-agent-form.test.ts packages/app/src/create-agent-preferences/preferences.test.ts packages/app/src/hooks/use-form-preferences.test.ts --bail=1npm run lint -- packages/app/src/create-agent-preferences/preferences.ts packages/app/src/create-agent-preferences/preferences.test.ts packages/app/src/provider-selection/resolve-agent-form.ts packages/app/src/provider-selection/resolve-agent-form.test.tsnpm run typechecknpm run format:files -- packages/app/src/create-agent-preferences/preferences.ts packages/app/src/create-agent-preferences/preferences.test.ts packages/app/src/provider-selection/resolve-agent-form.ts packages/app/src/provider-selection/resolve-agent-form.test.tsThe pre-commit hook also reran lint, format check, and typecheck successfully.
Checklist
npm run typecheckpassesnpm run lintpassesnpm run formatran (Biome)