From 53836cc62ee2c31db6461f923fef275c8c93fc4c Mon Sep 17 00:00:00 2001 From: danielhanchen Date: Fri, 17 Jul 2026 23:18:52 +0000 Subject: [PATCH] Never let legacy migration evict existing per-model configs Protecting the just-migrated keys during budget enforcement meant legacy entries could evict the user's existing current-schema configs to make room, and could still deadlock when unevictable future-schema records were present. Protect the existing entries instead and let only the just-migrated legacy entries be evicted when over budget: importing old settings never discards a newer config, and since existing entries already fit the budget it cannot deadlock. --- .../model-config/per-model-config.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/studio/frontend/src/features/model-picker/model-config/per-model-config.ts b/studio/frontend/src/features/model-picker/model-config/per-model-config.ts index 5c0cb97bad..f1b4140718 100644 --- a/studio/frontend/src/features/model-picker/model-config/per-model-config.ts +++ b/studio/frontend/src/features/model-picker/model-config/per-model-config.ts @@ -262,6 +262,10 @@ function migrateLegacyLoadSettingsOnce(): void { return; } const map = readMapRaw(); + // Snapshot the user's existing entries before merging: legacy settings are + // imported only into spare budget and must never evict a config the user + // already saved in the current schema. + const existingKeys = new Set(Object.keys(map)); const migratedKeys = mergeLegacyEntries( map, legacy as Record, @@ -270,16 +274,11 @@ function migrateLegacyLoadSettingsOnce(): void { localStorage.setItem(LEGACY_MIGRATION_FLAG, "1"); return; } - // Protect the just-migrated entries from eviction, but cap the protected set - // to MAX_ENTRIES: an oversized legacy store would otherwise deadlock the - // budget loop and never finish migrating. On failure the flag stays unset so - // migration retries once space frees. - const protectedKeys = new Set( - migratedKeys.length > MAX_ENTRIES - ? migratedKeys.slice(0, MAX_ENTRIES) - : migratedKeys, - ); - if (!enforceStorageBudget(map, protectedKeys)) { + // Protect existing entries so only the just-migrated legacy entries are + // evicted when over budget: importing old settings never discards a newer + // config, and since existing entries already fit the budget this cannot + // deadlock. On failure the flag stays unset so migration retries. + if (!enforceStorageBudget(map, existingKeys)) { return; } if (writeMap(map)) {