Skip to content

feat: add organization-aware chat model discovery#27337

Closed
mafredri wants to merge 1 commit into
mainfrom
mathias/codagt-709-org-chat-model-discovery
Closed

feat: add organization-aware chat model discovery#27337
mafredri wants to merge 1 commit into
mainfrom
mathias/codagt-709-org-chat-model-discovery

Conversation

@mafredri

Copy link
Copy Markdown
Member

Why

Organization-owned chat model configs need an organization-qualified read contract before the destructive clone-and-remap migration can land safely. This expansion PR adds that contract while preserving every existing global route and caller.

The requested organization is an eligibility boundary during the expansion phase. Members receive the existing global catalog behavior, and deployment configuration readers retain visibility of disabled configs. Later stacked PRs can migrate consumers before organization ownership is activated.

Verification

  • make fmt
  • make gen
  • focused coderd and codersdk tests
  • focused site API and React Query tests, 140 passed
  • make lint
  • make build
  • mandatory pre-commit hook

The mandatory pre-push Storybook phase has inherited failures that reproduce on origin/main. They are being investigated independently on mathias/fix-chat-storybook-tests; this PR does not change the failing stories or components.

Approved implementation plan

CODAGT-709: reviewable expansion-first stack for organization-scoped chat model configs

Direction for approval

Replace the previous hard-cut implementation with an expansion-first stack. The existing global chat_model_configs rows remain authoritative until every supported consumer can supply a concrete organization. New organization-qualified read contracts are additive and preserve the legacy global behavior while source data is still global. The clone, remap, ACL activation, cache cutover, and removal of global routes occur only in the final cutover PR after all supported callers use organization-qualified contracts.

This sequencing is required for correctness. The final migration creates a distinct model-config ID for every legacy-config and organization pair. After that point, an unscoped request has no correct answer for both an existing chat in a non-default organization and a new chat created in a selected non-default organization. A default-organization legacy adapter would return the wrong cloned rows for one of those flows.

The current staged diff is discarded and rebuilt as the stack below. CODAGT-714 remains separate. It owns selected-organization settings and model-management UI work. CODAGT-709 includes only contract migrations in the existing Agents creation and chat-detail flows where the organization is already an existing resource property or the existing form selector state.

Observable end state

  • chat_model_configs is organization-owned, with one non-deleted default per organization.
  • Every legacy config has one new-ID clone per active organization before the legacy global row is removed.
  • Chats, messages, queued messages, and debug runs reference the clone belonging to their chat organization.
  • Model-bearing configuration, personal overrides, compaction thresholds, runtime resolution, and cache entries are organization-isolated.
  • ACL grants use read only. The organization Everyone group has read on migrated configs.
  • The organization-qualified API is deployed and used before the data cutover. No supported unscoped caller remains when global rows and global routes are removed.
  • CODAGT-714 is not implemented, stacked, or included in a CODAGT-709 PR.

Settled decisions

  • The legacy global model rows remain the only writable source until the final migration. Expansion PRs do not create organization-owned rows, dual-write model configs, or expose ACL management against a shadow resource.
  • New organization-qualified read endpoints require authentication and membership in the requested organization. While rows remain global, they preserve legacy visibility rules after that eligibility check:
    • the model catalog returns global enabled models subject to the caller's existing provider availability logic;
    • model-config list returns all global configs to deployment-config readers and enabled global configs to other eligible members.
  • Legacy routes and existing SDK methods remain unchanged during the expansion phase.
  • New SDK methods use distinct organization-qualified names. Go does not support signature overloads.
  • New TypeScript API and React Query helpers require an explicit organization. Their cache keys include organization from the first additive PR.
  • Existing-chat UI uses chat.organization_id, never a deployment default.
  • The existing new-chat form uses its existing selected organization. Moving its model-related reads into the component that already owns selection is contract wiring, not CODAGT-714 picker work.
  • Settings and model-management pages that need a selected organization remain outside CODAGT-709. CODAGT-714 owns their selected-organization UI migration. They continue using legacy global routes until their migration lands.
  • The final migration retains the previously approved clone, remap, setting migration, ACL, default, and down-migration semantics without modification.
  • The final cutover PR is opened only after the route-consumer stack and CODAGT-714 have merged, the repository has no legacy route callers, and the preceding release has deployed the new client contracts. It removes global rows and global routes in the same release boundary. No default-organization compatibility adapter is retained after clone-and-remap.

Ruled out

  • A default-organization adapter after clone-and-remap. It returns an incorrect clone for non-default chat and selected-organization flows.
  • A global route that aggregates all organization clones. It leaks model configuration across organization boundaries and does not preserve the old resource contract.
  • A shadow org model table or dual writes before cutover. It adds conflict resolution and migration risk without enabling a consumer that cannot already use the additive read contract.
  • Changing existing model visibility or authorization in the expansion route. New model ACL resource permissions activate only with the final org-owned data cutover.
  • Folding CODAGT-714's settings, sharing, or picker UI into CODAGT-709.
  • Splitting the destructive data migration from the runtime, database interface, RBAC, and server cutover. Those are one atomic application boundary because the migration removes global IDs and configuration keys.

Constraints and risks

  • Release ordering: The final cutover depends on consumer migration. Trigger: any supported caller still invokes an unscoped route. Response: do not open or merge the cutover PR.
  • Temporary semantic overlap: Before cutover, different organization-qualified reads return the same global source data. This is intentional compatibility behavior. Trigger: a consumer assumes a unique per-org ID before cutover. Response: consumers treat the new route as a scoped contract but do not persist a new org-specific model ID until the final migration.
  • External experimental API consumers: Existing routes remain available through the expansion phase. The final removal is a documented experimental breaking change coordinated with the release containing all in-repo client migrations.
  • CODAGT-714 dependency: The final cutover cannot happen before its selected-organization settings pages are merged. CODAGT-709 does not implement that ticket.

Reviewable PR stack

PR 1: additive organization-qualified discovery contract

Title: feat(coderd): add organization-aware chat model discovery

Purpose: Establish a merge-safe contract that can be consumed before model rows become organization-owned.

Include:

  • Add GET /api/experimental/organizations/{organization}/chats/models.
  • Add GET /api/experimental/organizations/{organization}/chat-model-configs.
  • Resolve the organization with httpmw.ExtractOrganizationParam and require httpmw.UserAuthorization(ctx).HasOrganizationMembership(organization.ID).
  • Delegate to the existing global catalog and config-list implementations after membership authorization. Do not add database columns, model ACLs, new resource policy, cache changes, or model mutation routes.
  • Preserve GET /api/experimental/chats/models and GET /api/experimental/chats/model-configs unchanged.
  • Add named SDK methods such as ListChatModelsForOrganization and ListChatModelConfigsForOrganization; retain the existing methods and signatures.
  • Add explicit TypeScript API methods and React Query factories taking organization. Organization is part of the query key.
  • Add API, SDK, and query tests for route shape, organization resolution, non-member rejection, ordinary-member visibility, deployment-config-reader visibility, and organization-isolated cache keys.
  • Update generated API references only for the additive endpoints and methods.

Exclude:

  • all migrations and schema changes;
  • model-config create, update, delete, and ACL endpoints;
  • chatd, pubsub, telemetry, CLI, scaletest, or site page changes;
  • any change to legacy routes or method signatures.

Invariants:

  • Every legacy request has its current path, method, response shape, and visibility behavior.
  • New organization-qualified reads cannot be called by a non-member.
  • A new organization-qualified read is global-data compatible until the cutover, not prematurely ACL-filtered.

Verification:

  • focused coderd endpoint tests for both legacy and additive endpoints;
  • focused codersdk URL and response tests;
  • TypeScript API and React Query factory tests;
  • make gen, make fmt, make lint, relevant Go package tests, and git diff --check.

PR 2: existing chat detail consumes its resource organization

Title: feat(site/AgentsPage): scope chat model reads to the chat organization

Purpose: Migrate the existing-chat flow to the additive read contract without adding UI state.

Include:

  • AgentChatPage requests catalog, model configs, and user compaction thresholds using chat.organization_id.
  • Story query seeds use organization-keyed cache keys.
  • Model-related React Query invalidation uses the matching organization key.
  • Tests prove a chat in a non-default organization requests that organization, not the dashboard default.

Exclude:

  • creation-form changes;
  • settings pages;
  • model-management and sharing UI;
  • database, runtime, and server mutations.

Invariants:

  • Existing chat pages never derive model scope from a default organization or current membership ordering.
  • The PR remains correct before and after final clone-and-remap because its scope source is the persisted chat organization.

Verification:

  • targeted API-query tests;
  • Agent chat Storybook coverage for a non-default-organization chat;
  • pnpm lint:types, relevant site tests, and git diff --check.

PR 3: existing new-chat form consumes its selected organization

Title: feat(site/AgentsPage): scope new chat model reads to selected organization

Purpose: Migrate the existing form's model-dependent reads to the organization selector it already owns.

Include:

  • Move only catalog, model-config, and personal-override reads from AgentCreatePage into AgentCreateForm, which already owns selectedOrg and sends organization_id with the create request.
  • Query organization-qualified discovery helpers using the selected organization.
  • Preserve the current organization selector, workspace filtering, model selection precedence, and create request behavior.
  • Update form stories to seed organization-specific query data and prove switching the existing selector refreshes model options from the selected organization.

Exclude:

  • a new organization picker or any new picker behavior;
  • model management, ACL sharing, settings pages, or AI Settings work;
  • database, runtime, or mutation contract changes.

Invariants:

  • A selected non-default organization never receives catalog data from the default organization.
  • The form continues to use existing selection state. No CODAGT-714 UI is introduced.

Verification:

  • targeted form stories and interaction coverage;
  • TypeScript typecheck and relevant site tests;
  • git diff --check.

PR 4: CODAGT-714 consumer migration, owned separately

Purpose: Migrate selected-organization settings and model-management callers to the additive contract.

Owner and scope: CODAGT-714, not CODAGT-709. This is a dependency record only. CODAGT-709 does not create this PR or change its files without a separate assignment and approved plan.

Required result before final cutover:

  • AI Settings model and Coder Agents pages, Agent Settings API keys, compaction, and user-agent pages use an explicit selected organization.
  • All model-bearing settings reads and mutations pass organization and use organization-keyed caches.
  • The repository has no caller for a legacy global model or model-bearing settings endpoint.

PR 5: atomic data, authorization, runtime, and API cutover

Title: feat(coderd): scope chat model configs to organizations

Purpose: Activate organization ownership only after the contract consumers are ready.

Include as one coherent review boundary:

  • The final migration pair and fixtures:
    • add organization_id, user_acl, and group_acl to chat_model_configs;
    • clone every legacy config, including soft-deleted historical configs, to every non-deleted organization using a fresh UUID per source-config and organization pair;
    • seed each clone with empty user ACL and Everyone-group read ACL;
    • remap chat, message, queued-message, and debug-run model IDs by chat organization;
    • add child organization IDs and same-org composite foreign keys;
    • migrate model-bearing site settings and user settings to organization-keyed forms;
    • delete legacy global rows and global setting keys only after remapping;
    • preserve the approved down-migration collapse behavior.
  • Database queries and generated code for organization-owned rows.
  • ResourceChatModelConfig with create, read, update, delete, and share. No ActionUse. Direct user and group ACL roles map only to read.
  • RegoSQL, dbauthz, role, and ACL endpoint behavior, including Template RBAC entitlement behavior and item 404 concealment.
  • Organization-scoped model-config mutation and model-bearing settings routes.
  • Removal of all legacy model and model-bearing settings routes and legacy SDK methods after the consumer gate has passed.
  • Chatd, pubsub, cache, and telemetry organization isolation.
  • CLI, scaletest, SDK, generated API output, and remaining operational caller migration to the final scoped contract.

Invariants:

  • A model ID, model-bearing setting, cache entry, or query result cannot cross organization boundaries.
  • Each organization has at most one non-deleted default. Disabled defaults remain allowed.
  • A direct user or group grant provides read, not mutation or share authority.
  • Existing chats continue with their remapped model clone. No per-turn ACL revocation behavior is introduced.
  • The final PR has no default-organization legacy adapter and no global fallback for model-bearing settings.

Verification:

  • migration fixture and up/down migration execution;
  • same-org foreign-key assertions and cross-org rejection;
  • organization defaults, ACL user and group behavior, Template RBAC, and item concealment;
  • API, SDK, CLI, scaletest, and frontend cross-org request tests;
  • chatd default, override, advisor, personal override, compaction threshold, cache key, invalidation, and singleflight isolation tests;
  • make gen, make fmt, make lint, relevant package tests, and build entry points;
  • a repository-wide search proving no legacy endpoint or old SDK method caller remains before route removal.

Dependencies and implementation order

  1. PR 1 merges first. It introduces the scoped discovery contract without changing data ownership.
  2. PR 2 and PR 3 stack on PR 1 and may be reviewed independently. They migrate only the resource-aware Agents flows.
  3. CODAGT-714 migrates the selected-organization settings and model-management UI in separate PRs.
  4. PR 5 starts only when PRs 1 through 3 and CODAGT-714 have merged, the supported release has the scoped clients deployed, and repository search confirms legacy callers are gone.
  5. PR 5 is atomic across migration, database interfaces, RBAC, runtime, and removal. It is intentionally not split because any intermediate deployment would see cloned IDs and missing global model-bearing settings with an application still reading global state.

Evidence

  • The current migration clones IDs per organization and deletes global rows. Therefore a post-migration unscoped request cannot be correct for every organization.
  • Existing AgentChatPage has chat.organization_id; the resource organization is already available without UI work.
  • Existing AgentCreateForm owns selectedOrg and sends organization_id; the required source already exists without adding a picker.
  • Existing global catalog and config-list handlers have different visibility behavior for ordinary users and deployment-config readers. PR 1 preserves it behind organization membership rather than prematurely activating new ACL behavior.
  • Existing httpmw.ExtractOrganizationParam is the established route resolver for organization-qualified APIs.
  • Go SDK method signatures cannot be overloaded, so new scoped methods must have distinct names until the final removal.

🤖 This PR was created with the help of Coder Agents, and will be reviewed by a human. 🏂🏻

Expose organization-qualified read paths while keeping the existing global contract intact. Explicit membership checks prevent site-wide roles from granting access to an unrelated organization.
@linear-code

linear-code Bot commented Jul 20, 2026

Copy link
Copy Markdown

CODAGT-709

@github-actions

Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/reference/api/schemas.md

@mafredri mafredri changed the title feat(coderd): add organization-aware chat model discovery feat: add organization-aware chat model discovery Jul 20, 2026

Copy link
Copy Markdown
Member Author

$Closing this stack because its merge-safe compatibility direction was incorrect. CODAGT-709 will be rebuilt as review-only dependent diffs whose combined head is merged atomically.\n\n> 🤖 This review was automatically generated with Coder Agents.

@mafredri mafredri closed this Jul 20, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant