[multi-team P3c] Move LLMPreferences off its flat catalog onto Team/Workspace - #15567
Merged
tylerlam-warp merged 17 commits intoAug 27, 2026
Merged
Conversation
Adds Team.feature_model_choice and Workspace.feature_model_choice, sourced from the server's per-team/workspace Team.featureModelChoice (new teams[].featureModelChoice query field, schema mirror line) and the existing Workspace.featureModelChoice, via gql_convert's new feature_model_choice_from_gql. Persisted through new feature_model_choice_json sqlite columns (migration included) so the warm-start cache round-trips it too. Nothing reads this yet -- LLMPreferences keeps master's flat models_by_feature and behaves exactly as today. This PR is inert by design: dead data plus a conversion test (team_feature_model_choices_conversion_keeps_each_teams_choice_distinct) proving each team keeps its own payload through the fold.
Seeds Team/Workspace.feature_model_choice from the legacy, pre-team-keyed MODELS_BY_FEATURE_CACHE_KEY cache as a one-release migration in UserWorkspaces::new: a Workspace restored from the SQLite cache predates the feature_model_choice column (or the app hasn't fetched since upgrading), so its catalog is still the bare default, and this seeds it from the last-known model list instead so an offline launch right after upgrading isn't stuck on just the auto default for the rest of the offline session. migrate_legacy_feature_model_choices_cache understands both real shapes an older client could have written: the (more recent) single ModelsByFeature, and (older still) a bare AvailableLLMs, which becomes the agent_mode field. Still nothing reads Team/Workspace.feature_model_choice for model selection -- LLMPreferences keeps master's flat models_by_feature. legacy_cache_migration_yields_a_usable_teamless_catalog proves the older bare-AvailableLLMs shape migrates to a catalog whose agent_mode bucket resolves the cached model.
Deletes LLMPreferences's flat, non-team-aware models_by_feature field and its MODELS_BY_FEATURE_CACHE_KEY read/write, and the old WorkspacesMetadataResponse.feature_model_choices field PR 1 left alone. Every accessor now reads through UserWorkspaces instead, resolved against a caller-supplied TeamScope (or a raw team uid): - UserWorkspaces gains feature_model_choice_for_scope/_for_team_uid (reads, via the same scoped_or_workspace_setting pattern every other team-scoped setting uses; the absent/no-workspace-at-all case falls back to the resolved-teamless `pre_login_models_by_feature` seeded by PR 1/PR 2's migration or a pre-login fetch) and set_feature_model_choice_for_team_uid (the narrow one-off authed-refresh write path). - get_llm_info/_for_team_uid/_for_scope, get_active_base_model, and the various choice/default getters gained an app/ctx parameter and a TeamScope parameter accordingly, with the ~85 call sites across the app updated: terminal/, settings_view/, ai/blocklist/, ai/agent_sdk/, pane_group/, search/, workspace/, and crates/warp_tui/. - Preserves the earlier review-round behavioral fixes carried since PR 1's first draft: teamless eager fetch for agent_mode_evals builds, refresh-through-workspace-metadata (not the unscoped get_feature_model_choices) for blocklist triggers, cross-team get_llm_info display lookups, and stale-bucket eviction (implied by the wholesale Team/Workspace replacement on every response). This is the bulk of the three-PR stack, but every design decision here -- the storage shape, the legacy-cache conversion -- was already reviewed in PR 1 (#15463) and PR 2 (#15565); this should read as fan-out from those two, not new design.
2 tasks
… home (#15565) ## Description Stacked on #15463 (PR 1 of the three-PR stack — "Parse and store the team catalog into UserWorkspaces"). PR 1 added `Team`/`Workspace.feature_model_choice` but nothing populates it for a user restored from a pre-upgrade SQLite cache. PR 3 (to follow, stacked on this branch) moves `LLMPreferences` off its flat catalog onto these fields; this PR makes sure a warm-started, pre-upgrade cache has something real to read once that happens. Seeds `Team`/`Workspace.feature_model_choice` from the legacy, pre-team-keyed `MODELS_BY_FEATURE_CACHE_KEY` cache as a one-release migration in `UserWorkspaces::new`: a `Workspace` restored from the SQLite cache predates the `feature_model_choice` column (or the app hasn't fetched since upgrading), so its catalog is still the bare default. This seeds it from the last-known model list instead, so an offline launch right after upgrading isn't stuck on just the `auto` default for the rest of the offline session. `migrate_legacy_feature_model_choices_cache` understands both real shapes an older client could have written: the (more recent) single `ModelsByFeature`, and (older still) a bare `AvailableLLMs`, which becomes the `agent_mode` field. Still nothing reads `Team`/`Workspace.feature_model_choice` for model selection — `LLMPreferences` keeps master's flat `models_by_feature`. `legacy_cache_migration_yields_a_usable_teamless_catalog` proves the older bare-`AvailableLLMs` shape migrates to a catalog whose `agent_mode` bucket resolves the cached model. ## Testing - `cargo check --workspace --all-targets` — clean. - `cargo clippy -p warp --lib --tests -- -D warnings` — clean. - `./script/format` — clean. - `./script/bundle --channel oss --nouniversal --check-only` — clean. - `cargo clippy --locked --target wasm32-unknown-unknown --profile release-wasm-debug_assertions -- -D warnings` — clean. - `cargo nextest run -p warp -E 'test(user_workspaces) or test(legacy_cache) or test(gql_convert)'` — 107 passed. - No UI surface changed; this is a data-layer change, so no screenshots. - This branch is stacked on a non-default base, so it does not get a real CI matrix in this repo; the above was run locally on this exact branch tip. ## Linked Issue - [x] N/A — stacked continuation of #15463. ## Agent Mode - [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode --------- Co-authored-by: warp-agent-staging[bot] <240773466+warp-agent-staging[bot]@users.noreply.github.com> Co-authored-by: Oz <oz-agent@warp.dev>
Base automatically changed from
factory/multi-team-pr3b-legacy-cache-conversion
to
factory/multi-team-pr3a-catalog-team-scope
August 26, 2026 16:38
…team-scope' into pr3-work # Conflicts: # app/src/workspaces/gql_convert.rs # app/src/workspaces/user_workspaces/mod.rs
Base automatically changed from
factory/multi-team-pr3a-catalog-team-scope
to
master
August 26, 2026 17:17
# Conflicts: # app/src/workspaces/user_workspaces/mod.rs # app/src/workspaces/user_workspaces/user_workspaces_tests.rs
Strip the narration-style comments this PR introduced, restoring the pre-existing doc text where a comment had merely been reworded. Co-Authored-By: Warp <agent@warp.dev>
BlocklistAIContextModel and TuiAttachmentModel now resolve their team scope through the existing UserWorkspaces::team_context_resolver rather than a duplicated window-id resolver gadget. Co-Authored-By: Warp <agent@warp.dev>
Drops the constructor generics on BlocklistAIContextModel::new and TuiAttachmentModel::new; callers build the resolver where the concrete view type is already known. Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Warp <agent@warp.dev>
The field holds the fallback model catalog whenever no current workspace exists, which includes logged-in users whose only workspace is the filtered-out server placeholder — not just the pre-login state. Co-Authored-By: Warp <agent@warp.dev>
Team-scoped settings (including per-team model choices) were only as fresh as the periodic metadata poll, so switching teams could show up to ~10 minutes of stale data. Kick off an out-of-band metadata refresh from the GUI team-switcher action and the TUI team menu switch path. Co-Authored-By: Warp <agent@warp.dev>
tylerlam-warp
approved these changes
Aug 27, 2026
…pr3c-move-llm-preferences # Conflicts: # app/src/ai/llms_tests.rs # app/src/workspaces/user_workspaces/team_workspace_settings.rs
tylerlam-warp
enabled auto-merge (squash)
August 27, 2026 03:14
Co-Authored-By: Warp <agent@warp.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Stacked on #15565 (PR 2), which is stacked on #15463 (PR 1). This is the third and final PR of the stack: it deletes
LLMPreferences's flat, non-team-awaremodels_by_featurefield and itsMODELS_BY_FEATURE_CACHE_KEYread/write, and the oldWorkspacesMetadataResponse.feature_model_choicesfield PR 1 deliberately left alone. Every accessor now reads throughUserWorkspacesinstead, resolved against a caller-suppliedTeamScope(or a raw team uid).UserWorkspacesgainsfeature_model_choice_for_scope/_for_team_uid(reads, via the samescoped_or_workspace_settingpattern every other team-scoped setting uses; the absent/no-workspace-at-all case falls back to the resolved-teamlesspre_login_models_by_featureseeded by PR 1/PR 2's migration or a pre-login fetch) andset_feature_model_choice_for_team_uid(the narrow one-off authed-refresh write path).get_llm_info/_for_team_uid/_for_scope,get_active_base_model, and the various choice/default getters gained an app/ctx parameter and aTeamScopeparameter accordingly, with the ~85 call sites across the app updated:terminal/,settings_view/,ai/blocklist/,ai/agent_sdk/,pane_group/,search/,workspace/, andcrates/warp_tui/.agent_mode_evalsbuilds, refresh-through-workspace-metadata (not the unscopedget_feature_model_choices) for blocklist triggers, cross-teamget_llm_infodisplay lookups, and stale-bucket eviction (implied by the wholesaleTeam/Workspacereplacement on every response).This is the bulk of the three-PR stack by line count, but every design decision here — the storage shape, the legacy-cache conversion — was already reviewed in PR 1 and PR 2; this should read as fan-out from those two, not new design.
Note on ancestry
This branch's only ancestors beyond
masterare PR 1's and PR 2's commits — verified withgit merge-base --is-ancestorin both directions before pushing, since a three-deep stack is easy to get backwards.Testing
Since this branch is stacked on a non-default base, it does not get a real CI matrix in this repo (only PR 1 does); all of the below was run locally on this exact branch tip:
cargo check --workspace --all-targets— clean.cargo clippy -p warp --lib --tests -- -D warnings— clean../script/format— clean (2 files needed reformatting, included)../script/bundle --channel oss --nouniversal --check-only— clean.cargo clippy --locked --target wasm32-unknown-unknown --profile release-wasm-debug_assertions -- -D warnings— clean (no dead-code-on-wasm surprises from the call-site fan-out).cargo nextest run -p warp -E 'test(llms) or test(execution_profile) or test(update_manager) or test(gql_convert) or test(user_workspaces) or test(profile_model_selector) or test(agent_profiles_page) or test(legacy_cache)'— 262 passed.cargo nextest run --workspace -E 'package(warp_tui) and (test(terminal_session_view) or test(model_menu))'— 187 passed.Linked Issue
Agent Mode