mirror of
https://github.com/coder/coder.git
synced 2026-06-03 21:18:24 +00:00
3fb7c6264f
## Summary Adds an entitlement-gated **AI add-on** column to both the **Users** table and the **Organization Members** table. When `ai_governance_user_limit` is entitled, each row shows whether the user is consuming an AI seat. ## Background The AI governance add-on tracks which users are consuming AI seats. Admins need visibility into per-user seat consumption directly from the user management tables. This change surfaces that information through both the site-wide Users table and the per-organization Members table, gated behind the `ai_governance_user_limit` entitlement so the column only appears when the feature is licensed. ## Implementation ### Backend - **New SQL query** `GetUserAISeatStates` (`coderd/database/queries/aiseatstate.sql`) — returns user IDs consuming an AI seat, derived from: - Users with entries in `aibridge_interceptions` (AI Bridge usage) - Users who own workspaces with `has_ai_task = true` builds (AI Tasks usage) - **SDK types** — added `has_ai_seat: boolean` to `codersdk.User` and `codersdk.OrganizationMemberWithUserData` - **Handler wiring** — both the Users list endpoint (`coderd/users.go`) and all Members endpoints (`coderd/members.go`) query AI seat state per page of user IDs and populate the response field - **dbauthz** — per-user `ActionRead` checks on `ResourceUserObject` ### Frontend - **Shared `AISeatCell` component** (`site/src/modules/users/AISeatCell.tsx`) — green `CircleCheck` for consuming, gray `X` for non-consuming - **`TableColumnHelpTooltip`** — extended with `ai_addon` variant with tooltip: *"Users with access to AI features like AI Bridge, Boundary, or Tasks who are actively consuming a seat."* - **Column visibility** gated behind `useFeatureVisibility().ai_governance_user_limit` ## Validation - Backend: dbauthz full method suite (`TestMethodTestSuite`) passes including new `GetUserAISeatStates` test - Backend: `TestGetUsers`, `TestUsersFilter`, CLI golden file tests pass - Frontend: 7/7 tests pass across `UsersPage.test.tsx` and `OrganizationMembersPage.test.tsx` (column visibility gating both directions) - `go build ./coderd/...` compiles clean - `pnpm --dir site run lint:types` passes - `make gen` clean ## Risks - **Pagination performance**: The AI seat query is scoped to the current page's user IDs (not a full table scan), keeping it efficient for paginated views. - **Semantic scope**: The workspace-side AI seat derivation uses "any build with `has_ai_task = true`" rather than "latest build only". If the product intent is latest-build-only, this can be tightened in a follow-up. --- _Generated with `mux` • Model: `anthropic:claude-opus-4-6` • Thinking: `xhigh` • Cost: `$27.25`_ <!-- mux-attribution: model=anthropic:claude-opus-4-6 thinking=xhigh costs=27.25 -->
39 lines
957 B
Plaintext
39 lines
957 B
Plaintext
[
|
|
{
|
|
"id": "==========[first user ID]===========",
|
|
"username": "testuser",
|
|
"name": "Test User",
|
|
"email": "testuser@coder.com",
|
|
"created_at": "====[timestamp]=====",
|
|
"updated_at": "====[timestamp]=====",
|
|
"last_seen_at": "====[timestamp]=====",
|
|
"status": "active",
|
|
"login_type": "password",
|
|
"organization_ids": [
|
|
"===========[first org ID]==========="
|
|
],
|
|
"roles": [
|
|
{
|
|
"name": "owner",
|
|
"display_name": "Owner"
|
|
}
|
|
],
|
|
"has_ai_seat": false
|
|
},
|
|
{
|
|
"id": "==========[second user ID]==========",
|
|
"username": "testuser2",
|
|
"email": "testuser2@coder.com",
|
|
"created_at": "====[timestamp]=====",
|
|
"updated_at": "====[timestamp]=====",
|
|
"last_seen_at": "====[timestamp]=====",
|
|
"status": "dormant",
|
|
"login_type": "password",
|
|
"organization_ids": [
|
|
"===========[first org ID]==========="
|
|
],
|
|
"roles": [],
|
|
"has_ai_seat": false
|
|
}
|
|
]
|