feat: fall back to the Everyone group for AI spend attribution (#27364)

## Description

Previously, a user with no per-user override and no membership in a budgeted group had no effective group, so their AI spend was attributed nowhere and was, therefore, untracked. This change falls back to the organization's Everyone group when no override or group budget applies.

Since every user in an organization is implicitly a member of that org's Everyone group, spend is now attributed and tracked for any user with organization membership. A user with no organization membership resolves to no group, so their daily spend is not incremented and a warning is logged.

The fallback is unlimited, so enforcement is unaffected: only override and group budgets can block requests. For users in multiple organizations, an existing budget on any Everyone group is still chosen by the "highest" policy; when none is budgeted, the fallback prefers the default org, then orders by organization name.

## Changes

- Add `ResolveUserEffectiveGroup` and the `GetUserEveryoneFallbackGroup` query: resolve override → group budget → Everyone group fallback.
- Attribute token-usage spend and the user AI spend endpoint via the fallback, so unbudgeted users resolve to their Everyone group instead of null.
- Update `GetGroupMembersAISpend` to surface the Everyone fallback as the effective group.
- Update `GetHighestGroupAIBudgetByUser` to break ties by organization name then group name, keeping multi-org resolution deterministic and consistent with the fallback.
- For multi-org users with no budget anywhere, the fallback picks the Everyone group deterministically: prefer the default org, then order by organization name.

Closes https://linear.app/codercom/issue/AIGOV-509/fall-back-to-the-everyone-group-for-spend-attribution

> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
This commit is contained in:
Susana Ferreira
2026-07-23 09:26:25 +01:00
committed by GitHub
parent 671173b498
commit c23f2c0223
20 changed files with 1096 additions and 272 deletions
+9 -7
View File
@@ -34,12 +34,14 @@ type AIGroupBudget struct {
LimitSource AIBudgetLimitSource `json:"limit_source"`
}
// UserAIBudgetSummary is the effective AI budget for a user. When no
// budget applies, all fields except UserID are null.
// UserAIBudgetSummary is the effective AI budget for a user. When no budget
// applies, the effective group falls back to the Everyone group with a null
// limit and source.
type UserAIBudgetSummary struct {
UserID uuid.UUID `json:"user_id" format:"uuid"`
// EffectiveGroupID is the group the spend is attributed to. Null when
// no budget applies.
// EffectiveGroupID is the group the spend is attributed to, falling back to
// the Everyone group when no budget applies. Null only when the user has no
// organization membership.
EffectiveGroupID *uuid.UUID `json:"effective_group_id" format:"uuid"`
// SpendLimitMicros is the effective spend limit in micro-units.
// Null when no budget applies to the user (unlimited).
@@ -101,9 +103,9 @@ type GroupMembersAISpend struct {
type GroupMemberAISpend struct {
UserID uuid.UUID `json:"user_id" format:"uuid"`
// EffectiveGroupID is the user's effective budget group within the queried
// group's organization. Null when no effective budget group is visible in
// this organization, including when the user's budget resolves to a group
// in another organization.
// group's organization, falling back to the Everyone group when no budget
// applies. Null when the effective group belongs to a different organization
// than the queried group.
EffectiveGroupID *uuid.UUID `json:"effective_group_id" format:"uuid"`
// GroupBudget is the budget when the queried group is this user's
// effective budget source. Null when the user's budget resolves to another