Skip to content

fix(cursor): a completed catalog refresh drops models it no longer lists - #28018

Open
juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-28008
Open

juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-28008

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

The Cursor model list could only grow. Every provider refresh merged the previous snapshot's models into the new catalog, and the result was persisted to caches/cursor.json. Models from a previous Cursor account, a downgraded plan, or a retired model therefore stayed in the picker indefinitely, even when the catalog read succeeded and listed fewer models.

Reproduction on main (b823dcf): with Cursor ready on a fresh home, I appended three models the catalog does not return to caches/cursor.json. A restart and Settings > Providers > "Refresh provider status" both completed with status ready, and both still listed all 51 models, the three stale ones included.

Change

shouldRetainMissingProviderModels had no Cursor clause, so Cursor fell through to return true (always keep the missing models). Cursor now shares Grok's existing rule. Previous models are kept only while the probe is not installed && status === "ready". Cursor's status probe reports ready only when the SDK catalog read succeeded with at least one model. The initial placeholder, an empty catalog, a failed or timed-out read, and a missing key are all warning or error, and those still keep the previous models. A completed refresh replaces the list, and that replacement also reaches the persisted cache.

Two existing registry tests used Cursor as a stand-in for generic retention, with a ready refresh that returned no models. The Cursor probe never reports that state. Those fixtures now use warning, which is what an empty catalog reports, so they still exercise the same retention behaviour.

Other drivers that fall through to the default (claudeAgent, fx, muse) are deliberately left unchanged.

Scope and approval

Fixes #28008

Triaged via the automated issue flow.

Verification

  • New test drops Cursor models a completed catalog read no longer returns in apps/server/src/provider/ProviderRegistry.test.ts. It fails on main (retired-model-x is still appended after a ready refresh) and passes with the fix. Its second assertion checks that a failed (error) refresh still keeps the previous models.
  • vp test run apps/server/src/provider/ProviderRegistry.test.ts: 49/49 pass. vp run check:changed after merging main: format, lint, knip, typecheck (3 packages) and tests (2 files, 50 tests) pass.
  • Runtime transcript from the real dev server (T3CODE_DEV_SEED=0 vp run dev, fresh database, Cursor via CURSOR_API_KEY). The stale models are synthetic slugs written into the cache file:
BEFORE, main b823dcf5c6
06:54:36  first probe, fresh home     -> status=ready auth=authenticated models=48 (live catalog only)
          server stopped; appended old-account-only-a, old-account-only-b, retired-model-x -> cache has 51
06:55:10  probe after restart         -> status=ready auth=authenticated models=51  stale present: all 3
06:56:06  Settings > Providers > Cursor > "Refresh provider status"
                                      -> status=ready auth=authenticated models=51  stale present: all 3
          UI lists "Old Account Only A", "Old Account Only B", "Retired Model X" under Cursor

AFTER, fix/issue-28008 8b2004be5f
          server stopped; restored the same 51-model seeded cache file
07:02:48  probe after restart         -> status=ready auth=authenticated models=48  stale present: none
07:03     "Refresh provider status"   -> models=48; the UI shows none of the three stale models
  • Server-only change with no render path touched. Not checked: a real switch between two Cursor accounts (only one account was available, so the cache was seeded instead), and the macOS desktop app the reporter used. The change is in platform-independent server code.

Model and harness: Claude Opus 5.5 (claude-opus-5-5) and GPT-6.1-Sol in T3 Code via Claude Code and Codex.

🤖 Generated with Claude Code

juliusmarminge and others added 3 commits October 31, 2026 06:57
…refresh

These tests reproduce #28008: a ready Cursor probe should replace the model
list, while a failed probe keeps the previous models.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cursor fell through to the default "retain missing models" rule, so a
refresh could only add models and a previous account's or retired plan's
models stayed in the snapshot and provider cache forever. Cursor reports
ready only after a non-empty SDK catalog read, the same contract as Grok,
so it now shares Grok's rule: a ready probe replaces the list, while
warning/error probes keep the previous models.

Two generic retention tests used Cursor with a ready, empty refresh, a
state the Cursor probe never produces; they now use the warning status
an empty catalog actually reports.

Fixes #28008

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 31, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at ba75e85

Macroscope's review found this PR approvable — Completed Cursor catalog refreshes now remove stale models from the picker and cache. Failed, pending, and empty-catalog probes continue preserving previously discovered models, limiting the change to successfully refreshed inventory.

You can add or adjust custom eligibility rules. Learn more.

@juliusmarminge juliusmarminge added the bugfix Fix PR opened by an issue lead; enters the PR loop label Oct 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.6 KiB 21.6 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB −29 B (−0.6%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.3 KiB 1.2 KiB −29 B (−2.3%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB −41 B (−0.2%) 24.4 KiB ✅
Claude Live turn messages 2 1 −1 (−50.0%) 8 ✅

Baseline: 50c1c5d · PR result: ba75e85 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@juliusmarminge juliusmarminge added the pr:gate-failed A land gate failed label Oct 31, 2026
@coderabbitai

coderabbitai Bot commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 50c1c5d and ba75e85.

Files selected for processing: 2
  • apps/server/src/provider/ProviderRegistry.test.ts
  • apps/server/src/provider/ProviderRegistry.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Walkthrough

Cursor now uses the Grok model-retention rule during snapshot merges. Tests cover completed refreshes that remove retired models and error refreshes that retain prior models.

Changes: Cursor model refresh

Layer / File(s) Summary
Model retention and refresh coverage
apps/server/src/provider/ProviderRegistry.ts, apps/server/src/provider/ProviderRegistry.test.ts
Cursor retains previous models unless it is installed and ready. Tests verify that a completed refresh drops unlisted models, an error refresh retains prior models, and warning-status snapshots exercise the retention behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested labels: size:XS

Suggested reviewers: t3dotgg

Merge Risk: Minimal · up to ba75e

Ready Cursor refreshes replace stale models while failed discovery retains them. No merge-blocking risk remains.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title names the Cursor fix directly: a completed catalog refresh now drops models the catalog no longer lists. The raw summary and the objectives both describe a Cursor retention change in Provide…
Description check Passed The description follows the repository template. It has a Problem section that gives the reproduction on main and the expected behavior. It has a Change section that explains the Cursor retention rule…
Linked Issues check Passed Issue #28008 requires Cursor refreshes to remove models absent from a successful catalog read while retaining models when discovery fails. ProviderRegistry.ts applies the existing Grok rule to Curso…
Out of Scope Changes check Passed The changes stay within issue #28008. They modify Cursor model-retention behavior and update two Cursor empty-catalog fixtures from ready to warning, matching the provider contract. The added test…
Full details: Title check

Explanation

The title names the Cursor fix directly: a completed catalog refresh now drops models the catalog no longer lists. The raw summary and the objectives both describe a Cursor retention change in ProviderRegistry that does this. The title is specific and concise and follows the conventional commit form. It is 72 characters, which is slightly above the usual 72-character guideline, but this does not affect the assessment.

Full details: Description check

Explanation

The description follows the repository template. It has a Problem section that gives the reproduction on main and the expected behavior. It has a Change section that explains the Cursor retention rule and the test fixture updates. It has a Scope and approval section that links issue #28008 and notes the automated triage flow. It has a Verification section with the focused test results, the check:changed results, and a runtime transcript. It also lists what was not checked. The template asks for a maintainer-approval link or an explicit exemption for a small fix. The author gives a link to the issue and says it was triaged through an automated flow, but does not show explicit maintainer approval. The template also asks that the agent model and harness be named at the end, and the description does this.

Resolution

Optional: confirm that the triage of issue #28008 meets the approval rule in the template. The description links the issue but does not include an explicit maintainer approval comment or discussion. If no maintainer approval exists, add a link to it or explain why this small, focused fix of an obvious bug does not need one.

Full details: Linked Issues check

Explanation

Issue #28008 requires Cursor refreshes to remove models absent from a successful catalog read while retaining models when discovery fails. ProviderRegistry.ts applies the existing Grok rule to Cursor: an installed ready provider replaces missing models, while non-ready providers retain them. The new registry test verifies both Cursor model removal after a ready refresh and retention after an error refresh. The suggested identity-mismatch safeguard is complementary and is not required for the refresh-based behavior implemented here.

Full details: Out of Scope Changes check

Explanation

The changes stay within issue #28008. They modify Cursor model-retention behavior and update two Cursor empty-catalog fixtures from ready to warning, matching the provider contract. The added test covers the requested success and failure paths. No unrelated provider behavior or unrelated files changed.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cursor model list never drops models after an account change; refreshes only ever add to it

1 participant