fix(usage): address review — order email before project, add tests, nits

- BLOCKING: reorder resolveProviderCredentialIdentityKey so email
  identity takes priority over project — two users with different
  emails on the same GCP project no longer get merged/hard-deleted.

- Added #getUsageReportScopeProjectId helper so Gemini CLI reports
  (which set projectId on limit.scope but not metadata) still get
  dedup coverage. Both metadata and scope projectId paths checked.

- formatAggregateAmount now falls back to limits.length when no
  scope.accountId values are present, preserving pre-existing
  behaviour for providers that don't set accountId on limits.

- Added 9 contract tests for the antigravity usage merge logic:
  tier dedup, worst-fraction-wins, mixed-case collapsing,
  reset-time-from-other-entry, windowId separation, metadata,
  sort order, and null-on-no-project.

- Nits: label='Usage' (so formatLimitTitle renders 'Usage (Default)'
  not bare 'Default'), id uses params.provider instead of hardcoded
  string, tier field drops redundant ?? undefined.
This commit is contained in:
basedcorp99
2026-06-06 20:42:15 +02:00
parent 15c0dff28e
commit 794a64aae1
4 changed files with 197 additions and 6 deletions
@@ -1373,8 +1373,10 @@ function formatAggregateAmount(limits: UsageLimit[]): string {
const uniqueAccountIds = new Set(
limits.map(limit => limit.scope.accountId).filter((id): id is string => typeof id === "string" && id.length > 0),
);
if (uniqueAccountIds.size === 0) return "";
return `${uniqueAccountIds.size} ${uniqueAccountIds.size === 1 ? "acct" : "accts"}`;
if (uniqueAccountIds.size > 0) return `${uniqueAccountIds.size} ${uniqueAccountIds.size === 1 ? "acct" : "accts"}`;
// No account IDs available — keep the pre-existing fallback so providers
// that don't populate scope.accountId still show a summary.
return `${limits.length} accts`;
}
function resolveResetRange(limits: UsageLimit[], nowMs: number): string | null {