Uh oh!
There was an error while loading. Please reload this page.
fix(desktop): rebuild the Claude subscription model inventory from the registry - #2482
Conversation
Astro-Han
commented
Aug 8, 2026
Thanks — the diagnosis and overall direction look right. A persisted fallback snapshot should not shadow the current curated registry, and rebuilding it on sync is a small, appropriate fix for newly added models disappearing after selection. I found one compatibility edge case that seems important to handle before merge: P2 — Existing Haiku 4.5 users can be silently switched to Opus 5 The previous inventory included Because reconciliation currently compares IDs literally, an existing connection with: is repaired to the first item in the new list, currently Please normalize the known dated ID to its current alias before reconciliation: A focused regression test should assert that an existing Haiku default remains Haiku after sync. The current “default belongs to the curated list” assertion is too broad and would allow this cross-family replacement. P3 — The settings catalog and sync inventory still have separate authorities Sync reads It would be cleaner to project both paths from one curated authority and add a small alignment test. P3 — The Runtime Host 401 note appears unrelated to this change This PR does not modify the Runtime Host refresh path, and #2336 already marked the provider as fallback-only so model refresh should not make the network request. I suggest removing or narrowing that claim in the PR description. The production change can remain small; it just needs explicit alias migration and a single inventory authority. Thanks! |
Review on apache#2482 caught a regression this PR introduced: an existing `claude-haiku-4-5-20251001` selection was repaired to `claude-opus-5`. The dated id and the curated `claude-haiku-4-5` name one model — Anthropic publishes a pinned release id and a "latest" alias — but reconciliation compares ids literally, so the stored form read as a model the catalog had dropped and repair fell through to the first live id. That moved the user across model family and price tier with no prompt. The curated list caused it by mixing forms: it kept a pinned `claude-sonnet-4-5-20250929` while switching Haiku to its alias, so only Haiku users were orphaned. Resolve the id against the inventory before matching. Whether an id is superseded is a property of the inventory, not of the id: the API-key Anthropic catalog deliberately lists a pinned release beside its alias so users can pin, while the subscription catalog lists aliases only. So the rewrite applies only when the stored id is absent and its alias present — the exact case a literal comparison misreads. Retirement stays untouched: `claude-opus-4-1-20250805` is still repaired against the live list, and a test asserts via `lifecycle` metadata that no deprecated model can be added to the alias table. Also collapses the two inventory authorities the review flagged: sync read `PROVIDER_DEFAULTS[...].fallbackModels` while the settings catalog read `CURATED_CATALOG_FALLBACK_MODELS`, agreeing only while both were edited together. Both now project from `connectionFallbackModelIds`, with a test asserting the sync inventory equals what the catalog offers. Tests tightened accordingly: the retired-default case asserted only that the new default was in the curated list, which the Haiku regression satisfied.
Joob1n
commented
Aug 8, 2026
Thanks — all three land, and P2 was a real regression this PR introduced. Reproduced it against a live workspace record before fixing: an existing P2 — fixed, and I'd argue it was worse than P2. The switch crosses price tiers (Haiku → Opus 5) silently and takes effect on the next message, so the cost lands before the user has any way to notice. Worth calling out because the root cause is broader than the one id: my curated list mixed forms — it kept a pinned On the normalization itself, I made resolution inventory-relative rather than a global rewrite, because your P3-1 point turned out to constrain it: the API-key Retirement stays separate: P3-1 — fixed. Both paths project from P3-2 — correct, removed.#2336 marked the provider fallback-only, so that path stopped making the request there, not here. My misattribution. You were also right that the old assertion was too broad — Verification: |
Joob1n
commented
Aug 8, 2026
Correction to my verification note above: I reported "20 pre-existing failures" in the desktop suite. That was wrong, and it was my own build hygiene rather than anything in the repo. I had been running Through the documented entry point, this branch is 1612/1612, zero failures: The stash comparison I cited was still valid as far as it went — those failures were not caused by this change — but characterising them as pre-existing in the repo was not something I had actually verified. Apologies for the noise; the PR description now carries the corrected number. |
Astro-Han
commented
Aug 8, 2026
Thanks again for the careful alias migration work. I found one scope issue in the follow-up, and I’m sorry I did not make this boundary explicit in the previous review. The alias migration itself is necessary, but Could we keep the generic reconciliation provider-agnostic and pass the alias map as an explicit optional input only from the This is a small scope correction to the migration requested in my previous review, not a change in the overall direction. Sorry for the extra round, and thank you for handling the earlier feedback so thoroughly. |
Joob1n
commented
Aug 8, 2026
You're right, and the boundary was mine to see — thanks for catching it. Fixed in The relay-profile consequence you flagged is worse than a rewritten selection, and I confirmed it: On the regression test: my first attempt asserted the right value for the wrong reason. With the alias absent, a stored Verification: No apology needed for the extra round — the two rounds each removed a real defect, and this one would have been expensive to find in the field. |
Two follow-ups, found by re-checking the call sites rather than by review:
core 833, storage 754, desktop 1612, runtime-host 729 — green. Description updated. |
Astro-Han
commented
Aug 9, 2026
Thanks for working through the follow-ups. The provider-scoped alias fix makes sense. The rebased PR could likely stay focused on the Haiku compatibility and Runtime Host wiring, without the catalog helper or Sonnet rewrite. A small Storage regression test would be nice if convenient. Happy to take another look once it’s rebased. |
An existing `claude-haiku-4-5-20251001` selection was repaired onto `claude-opus-5`. The curated inventory lists that model under its `claude-haiku-4-5` alias, but reconciliation compares ids literally, so the stored form read as a model the catalog had dropped and repair fell through to the first live id — across model family and price tier, with nothing on screen. The rename table is passed in rather than assumed, and selected per provider by modelIdAliasesForProvider. Reconciliation is shared by every provider that commits a fetched inventory, and a relay may serve `claude-*` ids as its own opaque identifiers — the rule connection storage states where it prunes relay profiles across endpoints. Under a global table such a relay would have had its selection rewritten and the profile keyed on the old id pruned with it. Retirement is untouched: an id the table does not name is still repaired against the live list, and a test asserts through `lifecycle` metadata that no deprecated model can be added to it.
92ed85a to
7f02265CompareUh oh!
There was an error while loading. Please reload this page.
Summary
Newly curated models showed up in the Claude OAuth connection sheet but could not be selected. The settings catalog reads the curated list, while sync rebuilt the selection against the inventory persisted on disk, so a newly picked model was filtered back out on the next refresh.
Sync now rebuilds from the same curated authority the catalog projects from, through
connectionFallbackModelIds.A stored id that was renamed rather than retired now migrates instead of being repaired away — without it, an existing
claude-haiku-4-5-20251001default was moved toclaude-opus-5, across model family and price tier. The rename table is selected per provider bymodelIdAliasesForProvider, soreconcileConnectionAfterModelFetchstill treats ids as opaque for every other provider, including relays that serveclaude-*ids of their own. Both the desktop sync and the Runtime Host catalog resolve it through that one lookup.Retirement is unaffected:
claude-opus-4-1-20250805is still repaired against the live list, and a test asserts vialifecyclemetadata that no deprecated model can enter the table.Fixes nothing tracked — found while using the app.
Verification
@maka/core833/833,@maka/storage754/754,@maka/desktop1612/1612,@maka/runtime-host729/729lintandformat:checkcleanChecklist
Does this PR entail a change in behavior?