Skip to content

Usage limits need a stable logical account identity beyond email and plan labels #13992

Description

@kvnloo

Problem

The current Usage → Limits identity layer is forced to infer subscription identity from display-ish fields:

That works for some cases, but it cannot represent the actual invariant: which logical subscription/workspace/quota bucket does this snapshot belong to?

Real failures already exist:

Existing stronger identities

The provider edges already expose stronger IDs:

  • Codex/CLIProxy ID tokens carry chatgpt_account_id.
  • Claude keeps oauthAccount.organizationUuid in .claude.json; reset-credit redemption already reads that UUID.
  • Hub auth records already have stable per-account IDs.

The shared contracts currently discard these before packages/shared/src/usageLimits.ts.

Proposed contract

Add one optional stable logical identity to both native and hub account snapshots:

ServerProviderAuth {
  ...
  accountId?: string
}

UsageLimitSourceAccount {
  ...
  accountId?: string
}

Semantics:

  • accountId identifies the logical subscription/workspace/quota source for grouping.
  • It is provider-scoped; collectors key on driver + accountId.
  • It is not a display label and should not be rendered raw by default.
  • Email, plan and organization name remain display/fallback metadata.
  • Missing accountId keeps today's conservative fallback behavior.

Provider mappings:

  • Codex → chatgpt_account_id when present.
  • Claude → organizationUuid when present.
  • Hub adapters → the same provider-native identity when available.

Grouping precedence

For account dedupe/merge:

  1. driver + accountId when both observations provide it;
  2. provider-specific fallback such as email + org/plan when stable identity is missing;
  3. environment/source instance identity when equivalence cannot be established.

An unknown ID must never be treated as matching a known ID merely because email/plan happens to match when multiple known accounts exist.

Why this shape

This is the same separation Tokenomics now uses with measurement_source.logical_source_id: observer identity, human labels and logical quota identity are different concepts.

It also lets #10845 and #13089 remain useful incremental fixes without making plan strings or organization display names the long-term identity contract.

Acceptance

  • optional stable account id added without breaking older snapshots;
  • Codex native + hub map chatgpt_account_id;
  • Claude maps organizationUuid;
  • collectors prefer stable id and fall back safely;
  • same account across environments/native+hub dedupes once;
  • same email + same plan but different stable ids stays separate;
  • unknown-id observations do not attach arbitrarily when multiple known ids share an email;
  • reset-credit targets follow the same logical identity as the displayed quota.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateThis issue or pull request already existsvia-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions