feat: use organization-scoped chat models - #27960
Conversation
0505466 to
143f1aa
Compare
143f1aa to
5413373
Compare
5413373 to
4394a8f
Compare
4394a8f to
8c54edb
Compare
3a23489 to
6d5cda4
Compare
6d5cda4 to
c05908d
Compare
65ff6ac to
cfd9238
Compare
cfd9238 to
9cea220
Compare
|
@codex review |
|
/coder-agents-review |
|
Chat: Review posted | View chat Review historydeep-review v0.9.0 | Round 2 | Last posted: Round 2, 47 findings (14 P2, 15 P3, 1 P4, 15 Nit, 2 Note), COMMENT. Review Finding inventoryFinding inventory - PR #27960Findings
Round logRound 1Netero-only pre-panel gate: 2 P3, 4 Nit findings. Passed to panel. Round 2 updateBLOCKED. Churn guard classified 38 addressed, 5 contested, 4 silent, 0 acknowledged, 0 deferred. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9cea2208d6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
9cea220 to
a51bbfa
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca70e01502
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| organizations.map((organization) => [ | ||
| organization.id, | ||
| organization.display_name, | ||
| ]), |
There was a problem hiding this comment.
Fall back to the organization slug in compaction rows
When an organization was created without the optional display_name, this map stores an empty string even though organization.name is available. UserCompactionThresholdSettings treats that empty value as resolved, so it renders no organization subtitle and generates labels ending in for . If multiple organizations expose similarly named models, users cannot tell which organization's threshold they are editing and may save the override for the wrong model; use organization.display_name || organization.name here.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca70e01502
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ca70e01 to
7142fa4
Compare
7142fa4 to
2db9e5b
Compare
2db9e5b to
334fbdb
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 334fbdbd0e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91c4fa41f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const canViewModels = | ||
| !canViewAISettings && accessibleModelOrgsQuery.organizations.length > 0; |
There was a problem hiding this comment.
Route model-only access through existing AI settings
For a member whose model visibility comes only from a model ACL, canAccessAnyModel and the other global AI permissions are false, but a successful organization probe makes canViewModels true while leaving canViewAISettings false. Checked NavbarView and MobileMenu: this combination hides Admin settings and adds Models as a standalone dashboard destination, so these users cannot follow the same commit's documented Admin settings > AI > Models navigation. Include accessible organization models in canViewAISettings and reuse the existing AI settings navigation instead.
AGENTS.md reference: site/AGENTS.md:L9-L9
Useful? React with 👍 / 👎.
Adds audit logging for chat model config creates, updates, deletes, ACL changes, and automatic default-model transitions. Audit records use the config display name or ID as the target and are emitted only after successful transactions. Depends on #27960.
## Summary Repair the Storybook interaction and Pixel failures currently present on `main`. The failures came from organization-scoped model changes leaving stories with incomplete providers and query fixtures, product copy and route changes leaving stale assertions, and several interaction tests depending on implementation details or teardown timing. ## Broken stories and fixes ### Organization-scoped chat models PR #27960 introduced organization-scoped chat models and the following regressions: - `OrganizationModelsLayout / Switch Organization Preserves Auxiliary Parameters`, `Invalid Requested Organization Falls Back To Default`, `Invalid Requested Organization Denies Add`, `Duplicate Display Names Are Disambiguated`, and `No Readable Organization Is Not Found`: the stories only populated permission query keys for individual organizations, while the accessible-organization lookup requests authorization for all visible organization IDs together. The unmatched `/api/v2/authcheck` request returned a Storybook proxy 502, so the layout rendered an error instead of the intended picker, fallback, denied, disambiguation, or not-found state. Add fixtures for the combined organization-permission keys, preserve the intentionally denied permission map, and return an explicit empty authorization result for the no-readable-organization case. - `ModelFormProviderConfig / Provider Config Open AI`, `Provider Config Anthropic`, and `Provider Config Open AI Web Search`: `ModelForm` began consuming `OrganizationModelsContext`, but these stories were not wrapped in its provider and rendered the router error boundary. Add the same organization-model context decorator used by the sibling model form stories. - `AgentChatPage / Queued For Capacity After Polling`: the story retained a manually assembled chat-and-messages fixture after the page gained organization-model, provider, workspace, prompt, diff, chat-list, and authorization dependencies. Those missing queries prevented the polling request from being reached. Replace the partial fixture list with the shared `buildQueries()` setup. - `DashboardLayout / Custom Organization Role Can Open Models`, `DashboardLayout / ACL Readable Member Can Open Models`, and `NavbarView / For Member With Model Access`: these stories also landed in #27960 and inherited Pixel's tablet-and-desktop matrix, but their play functions exercise the desktop `Models` link. Pixel's 744px tablet viewport renders that link inside the closed mobile menu, so the desktop query always failed there. Restrict these authorization-to-navigation stories to the desktop matrix; mobile Models navigation remains covered by the dedicated `MobileMenu` story. ### Premium copy and navigation - `DeploymentSidebarView / Premium Tab Visible` and `Premium Tab Hidden`: PR #28226 renamed the production navigation item from `Premium` to `Trial Upgrade`, but added stories that still queried the old name. Update both the positive and negative assertions so the hidden-state story cannot pass while the real CTA is present. - `PremiumPageView / No License`: PR #28226 changed the production heading to `Start an unlimited 30-day Coder trial` while the story asserted the previous Premium wording. Update the accessible heading assertion to the rendered copy. - `AgentChatPageView / Queued For Capacity Community Admin`: PR #28437 intentionally moved the trial CTA from `https://coder.com/trial` to the internal `/deployment/premium` route, leaving the story's href assertion stale. Update the expected route while retaining the link-name and callout checks. ### MCP server refetch behavior - `AgentCreateForm / MCP Servers Error Shows Alert And Disables Send` and `MCP Servers Refetch Error Keeps Send Enabled`: the MCP coverage was introduced in #27942. PR #28442 later added a second unconditional MCP `ErrorAlert`, so a background refetch error appeared even when cached MCP data remained usable. The refetch story also called `refetchQueries()` without a key, which began refetching unrelated active model queries as the form's query surface expanded and produced unmatched API failures. Remove the duplicate unconditional alert, refetch only the organization's MCP query, and use semantic alert and heading assertions. Initial-load failures still disable Send, while background failures with cached data keep Send enabled without replacing the form with an error. ### Interaction and teardown stability - `IconField / Open Picker`: PR #27674 changed this story to wait for the `em-emoji-picker` custom element. That implementation-specific query races the lazy-loaded picker chunk and violates the component's observable contract. Keep the button state assertion and wait for the visible dialog instead. - `AgentChatPage / Slash Compact Command Submits` and `Slash Compact Yields To Personal Skill`: the command story added in #27081 waited on cmdk's `Commands` group heading, which is accessibility-hidden, while the skill variant queried raw implementation text. Menu placement and visibility are asynchronous, especially after the positioning changes in #28411. Wait for the visible selectable options by role before pressing Enter. - `AgentChatPageView / Terminal Focus On Tab Switch`: the focus coverage added in #24677 exposed an xterm teardown race rather than a product navigation regression. xterm queues its initial viewport synchronization, but Storybook could synchronously dispose the terminal first, leaving the queued callback to read a cleared renderer and report an unhandled error. Clear React state immediately, defer xterm disposal by one timer turn, query the labeled terminal textbox semantically, and remove the unnecessary empty WebSocket message fixture.
Move chat model settings to `/ai/settings/organizations/:organization/models`, add an organization switcher, and redirect legacy settings paths. The pages use organization permissions for read, create, update, and delete controls. They use redacted provider descriptors. Agent creation loads models for the selected organization. Existing chats load models for the chat organization. Chat views warn about unavailable historical models, select a usable local recovery model, and disable generation when none exists. Settings that aggregate organizations retain successful results when one request fails. Depends on #27959 _This pull request description was generated by Coder Agents._
Adds audit logging for chat model config creates, updates, deletes, ACL changes, and automatic default-model transitions. Audit records use the config display name or ID as the target and are emitted only after successful transactions. Depends on #27960.
## Summary Repair the Storybook interaction and Pixel failures currently present on `main`. The failures came from organization-scoped model changes leaving stories with incomplete providers and query fixtures, product copy and route changes leaving stale assertions, and several interaction tests depending on implementation details or teardown timing. ## Broken stories and fixes ### Organization-scoped chat models PR #27960 introduced organization-scoped chat models and the following regressions: - `OrganizationModelsLayout / Switch Organization Preserves Auxiliary Parameters`, `Invalid Requested Organization Falls Back To Default`, `Invalid Requested Organization Denies Add`, `Duplicate Display Names Are Disambiguated`, and `No Readable Organization Is Not Found`: the stories only populated permission query keys for individual organizations, while the accessible-organization lookup requests authorization for all visible organization IDs together. The unmatched `/api/v2/authcheck` request returned a Storybook proxy 502, so the layout rendered an error instead of the intended picker, fallback, denied, disambiguation, or not-found state. Add fixtures for the combined organization-permission keys, preserve the intentionally denied permission map, and return an explicit empty authorization result for the no-readable-organization case. - `ModelFormProviderConfig / Provider Config Open AI`, `Provider Config Anthropic`, and `Provider Config Open AI Web Search`: `ModelForm` began consuming `OrganizationModelsContext`, but these stories were not wrapped in its provider and rendered the router error boundary. Add the same organization-model context decorator used by the sibling model form stories. - `AgentChatPage / Queued For Capacity After Polling`: the story retained a manually assembled chat-and-messages fixture after the page gained organization-model, provider, workspace, prompt, diff, chat-list, and authorization dependencies. Those missing queries prevented the polling request from being reached. Replace the partial fixture list with the shared `buildQueries()` setup. - `DashboardLayout / Custom Organization Role Can Open Models`, `DashboardLayout / ACL Readable Member Can Open Models`, and `NavbarView / For Member With Model Access`: these stories also landed in #27960 and inherited Pixel's tablet-and-desktop matrix, but their play functions exercise the desktop `Models` link. Pixel's 744px tablet viewport renders that link inside the closed mobile menu, so the desktop query always failed there. Restrict these authorization-to-navigation stories to the desktop matrix; mobile Models navigation remains covered by the dedicated `MobileMenu` story. ### Premium copy and navigation - `DeploymentSidebarView / Premium Tab Visible` and `Premium Tab Hidden`: PR #28226 renamed the production navigation item from `Premium` to `Trial Upgrade`, but added stories that still queried the old name. Update both the positive and negative assertions so the hidden-state story cannot pass while the real CTA is present. - `PremiumPageView / No License`: PR #28226 changed the production heading to `Start an unlimited 30-day Coder trial` while the story asserted the previous Premium wording. Update the accessible heading assertion to the rendered copy. - `AgentChatPageView / Queued For Capacity Community Admin`: PR #28437 intentionally moved the trial CTA from `https://coder.com/trial` to the internal `/deployment/premium` route, leaving the story's href assertion stale. Update the expected route while retaining the link-name and callout checks. ### MCP server refetch behavior - `AgentCreateForm / MCP Servers Error Shows Alert And Disables Send` and `MCP Servers Refetch Error Keeps Send Enabled`: the MCP coverage was introduced in #27942. PR #28442 later added a second unconditional MCP `ErrorAlert`, so a background refetch error appeared even when cached MCP data remained usable. The refetch story also called `refetchQueries()` without a key, which began refetching unrelated active model queries as the form's query surface expanded and produced unmatched API failures. Remove the duplicate unconditional alert, refetch only the organization's MCP query, and use semantic alert and heading assertions. Initial-load failures still disable Send, while background failures with cached data keep Send enabled without replacing the form with an error. ### Interaction and teardown stability - `IconField / Open Picker`: PR #27674 changed this story to wait for the `em-emoji-picker` custom element. That implementation-specific query races the lazy-loaded picker chunk and violates the component's observable contract. Keep the button state assertion and wait for the visible dialog instead. - `AgentChatPage / Slash Compact Command Submits` and `Slash Compact Yields To Personal Skill`: the command story added in #27081 waited on cmdk's `Commands` group heading, which is accessibility-hidden, while the skill variant queried raw implementation text. Menu placement and visibility are asynchronous, especially after the positioning changes in #28411. Wait for the visible selectable options by role before pressing Enter. - `AgentChatPageView / Terminal Focus On Tab Switch`: the focus coverage added in #24677 exposed an xterm teardown race rather than a product navigation regression. xterm queues its initial viewport synchronization, but Storybook could synchronously dispose the terminal first, leaving the queued callback to read a cleared renderer and report an unhandled error. Clear React state immediately, defer xterm disposal by one timer turn, query the labeled terminal textbox semantically, and remove the unnecessary empty WebSocket message fixture.
Repair the Storybook interaction and Pixel failures currently present on `main`. The failures came from organization-scoped model changes leaving stories with incomplete providers and query fixtures, product copy and route changes leaving stale assertions, and several interaction tests depending on implementation details or teardown timing. PR #27960 introduced organization-scoped chat models and the following regressions: - `OrganizationModelsLayout / Switch Organization Preserves Auxiliary Parameters`, `Invalid Requested Organization Falls Back To Default`, `Invalid Requested Organization Denies Add`, `Duplicate Display Names Are Disambiguated`, and `No Readable Organization Is Not Found`: the stories only populated permission query keys for individual organizations, while the accessible-organization lookup requests authorization for all visible organization IDs together. The unmatched `/api/v2/authcheck` request returned a Storybook proxy 502, so the layout rendered an error instead of the intended picker, fallback, denied, disambiguation, or not-found state. Add fixtures for the combined organization-permission keys, preserve the intentionally denied permission map, and return an explicit empty authorization result for the no-readable-organization case. - `ModelFormProviderConfig / Provider Config Open AI`, `Provider Config Anthropic`, and `Provider Config Open AI Web Search`: `ModelForm` began consuming `OrganizationModelsContext`, but these stories were not wrapped in its provider and rendered the router error boundary. Add the same organization-model context decorator used by the sibling model form stories. - `AgentChatPage / Queued For Capacity After Polling`: the story retained a manually assembled chat-and-messages fixture after the page gained organization-model, provider, workspace, prompt, diff, chat-list, and authorization dependencies. Those missing queries prevented the polling request from being reached. Replace the partial fixture list with the shared `buildQueries()` setup. - `DashboardLayout / Custom Organization Role Can Open Models`, `DashboardLayout / ACL Readable Member Can Open Models`, and `NavbarView / For Member With Model Access`: these stories also landed in #27960 and inherited Pixel's tablet-and-desktop matrix, but their play functions exercise the desktop `Models` link. Pixel's 744px tablet viewport renders that link inside the closed mobile menu, so the desktop query always failed there. Restrict these authorization-to-navigation stories to the desktop matrix; mobile Models navigation remains covered by the dedicated `MobileMenu` story. - `DeploymentSidebarView / Premium Tab Visible` and `Premium Tab Hidden`: PR #28226 renamed the production navigation item from `Premium` to `Trial Upgrade`, but added stories that still queried the old name. Update both the positive and negative assertions so the hidden-state story cannot pass while the real CTA is present. - `PremiumPageView / No License`: PR #28226 changed the production heading to `Start an unlimited 30-day Coder trial` while the story asserted the previous Premium wording. Update the accessible heading assertion to the rendered copy. - `AgentChatPageView / Queued For Capacity Community Admin`: PR #28437 intentionally moved the trial CTA from `https://coder.com/trial` to the internal `/deployment/premium` route, leaving the story's href assertion stale. Update the expected route while retaining the link-name and callout checks. - `AgentCreateForm / MCP Servers Error Shows Alert And Disables Send` and `MCP Servers Refetch Error Keeps Send Enabled`: the MCP coverage was introduced in #27942. PR #28442 later added a second unconditional MCP `ErrorAlert`, so a background refetch error appeared even when cached MCP data remained usable. The refetch story also called `refetchQueries()` without a key, which began refetching unrelated active model queries as the form's query surface expanded and produced unmatched API failures. Remove the duplicate unconditional alert, refetch only the organization's MCP query, and use semantic alert and heading assertions. Initial-load failures still disable Send, while background failures with cached data keep Send enabled without replacing the form with an error. - `IconField / Open Picker`: PR #27674 changed this story to wait for the `em-emoji-picker` custom element. That implementation-specific query races the lazy-loaded picker chunk and violates the component's observable contract. Keep the button state assertion and wait for the visible dialog instead. - `AgentChatPage / Slash Compact Command Submits` and `Slash Compact Yields To Personal Skill`: the command story added in #27081 waited on cmdk's `Commands` group heading, which is accessibility-hidden, while the skill variant queried raw implementation text. Menu placement and visibility are asynchronous, especially after the positioning changes in Enter. - `AgentChatPageView / Terminal Focus On Tab Switch`: the focus coverage added in #24677 exposed an xterm teardown race rather than a product navigation regression. xterm queues its initial viewport synchronization, but Storybook could synchronously dispose the terminal first, leaving the queued callback to read a cleared renderer and report an unhandled error. Clear React state immediately, defer xterm disposal by one timer turn, query the labeled terminal textbox semantically, and remove the unnecessary empty WebSocket message fixture. (cherry picked from commit 9b5f47e)
…n area to release/2.37 (#28673) Batch backport of @tracyjohnsonux's PRs that merged into `main` after the `release/2.37` branch point (`c275327f`, #28369) and were not yet on the release branch. Commits are `git cherry-pick -x` of the original squash commits in `main` merge order. Ten are verbatim; the last two carry manual conflict resolutions, documented below. `pnpm run lint:types` and the affected Storybook suites (311 tests) pass on the branch. ## Included (12) | PR | Commit on main | Notes | |---|---|---| | #28427 fix(site/src): delete DateTimeRangeFilter and unify on the picker's time range type | `607a1d06` | verbatim | | #28481 fix(site/src/pages/AIBridgePage): move AI Sessions docs link inline into subtitle | `0452ff7e` | verbatim | | #28477 fix(site/src/pages/AIBridgePage): align token badges with neighboring badge sizing | `e98e2b9b` | verbatim | | #28561 fix(site): use robot icon for subagent toggle in chat kebab menu | `94f312df` | verbatim | | #28557 fix: use medium badges and shorter shared key label on agent API keys page | `c07bde80` | verbatim | | #28560 fix(site/src/pages/AgentsPage): use outlined empty state and kebab menu for personal skills | `74e5a680` | verbatim | | #28559 fix(site/src/pages/AgentsPage): add organization filter to compaction settings | `2eee703e` | verbatim | | #28527 fix(site/src): use md badges for provisioner tags and network call pills | `f31b7597` | verbatim | | #28613 fix(site/src): use default combobox dropdown surface | `2eb9e4fb` | verbatim | | #28478 fix(site): match sessions date/time picker icon to search field icon | `769decbe` | verbatim | | #28612 fix(site/src): move deployment docs links inline as View docs text links | `046a532a` | resolved | | #28564 fix(site/src): reposition org pickers in AI settings models and MCP pages | `78c65ea5` | resolved, ported | ## Conflict resolutions - **#28612**: conflicted with #28664 (the backport of #28660) in `SecuritySettingsPageView.{tsx,stories.tsx}` because the backports landed in reverse `main` order. Resolved by taking `main`'s content; since only #28612 and #28660 touched those files on either side, the resolved files are byte-identical to `main`. - **#28564**: written on top of #28593 (MCP server sharing), which is not release material. 24 of 29 files applied verbatim; the 5 conflicting files were ported by hand against pre-#28593 code: `organizationPicker` prop threading and the edit-requires-dirty submit gate without the `canShareServer` context, the form header rebuilt as `main`'s version minus the share button, and story assertions ported (the #28593 share stories do not exist on this branch). `ModelFormProviderConfig.stories.tsx` takes `main`'s version, deliberately folding in the stories-only decorator fix from #28462 because `release/2.37` already has #27960 and those stories were broken without the provider wrap. Validated with the full MCPServersPage + ModelsPage Storybook suites (140/140, matching the original PR) and the `mcpServerFormLogic` unit tests. ## Excluded - #28492 (built-in emoji avatar inset): reverted on `main` by #28533, so it is not backported. - #28558 (show disabled state on AI provider rows): handled individually in #28662; blocked only on the #28624 backport (see analysis there). <details> <summary>Decision log</summary> - Branch point of `release/2.37` from `main`: `c275327fb724dea6c4428263d948e13815041b3e` (2026-08-24, #28369), computed via `git merge-base`. - 15 of the author's merged PRs landed on `main` after that point; none had been backported yet (verified by ancestry, patch-id equivalence, and `(#NNNN)` references in `release/2.37` history since the branch point). - Cherry-picks were applied oldest-first in `main` topological order; conflicting picks were initially skipped, then #28612 and #28564 were resolved on the same base and appended (no file overlap with the verbatim picks except `docs/ai-coder/agents/models.md`, which merges cleanly). - #28492 was initially included, then rebased out after confirming it was reverted on `main` by #28533 (`bef6b4c30b`). - The bot's individual cherry-pick PRs were closed as superseded: #28661, #28663, #28665, #28667, #28668, #28669, #28671, and after consolidation #28670 and #28666. - Validation: `pnpm install`, `pnpm run lint:types` (tsc), Biome on changed files, `mcpServerFormLogic` unit tests, and the AISettingsPage + SecuritySettingsPage Storybook suites (311 tests) pass after each change. </details> > Created by Coder Agents on behalf of @tracyjohnsonux. --------- Co-authored-by: Michael Suchacz <[email protected]>
Move chat model settings to
/ai/settings/organizations/:organization/models, add an organization switcher, and redirect legacy settings paths. The pages use organization permissions for read, create, update, and delete controls. They use redacted provider descriptors.Agent creation loads models for the selected organization. Existing chats load models for the chat organization. Chat views warn about unavailable historical models, select a usable local recovery model, and disable generation when none exists. Settings that aggregate organizations retain successful results when one request fails.
Depends on #27959
This pull request description was generated by Coder Agents.