Skip to content

Copilot: surface AI credit usage for token-based-billing seats - #2647

Open
KSEGIT wants to merge 17 commits into
steipete:mainfrom
KSEGIT:copilot-ai-credits
Open

Copilot: surface AI credit usage for token-based-billing seats#2647
KSEGIT wants to merge 17 commits into
steipete:mainfrom
KSEGIT:copilot-ai-credits

Conversation

@KSEGIT

@KSEGIT KSEGIT commented Aug 4, 2026

Copy link
Copy Markdown

Closes #2593.

The problem

On a Copilot Business seat with token_based_billing: true, GET /copilot_internal/user reports every quota snapshot as unlimited: true, entitlement: 0, remaining: 0, percent_remaining: 100. The #1258 guards correctly drop those so no misleading "0% used" bar appears — but the result is a Copilot card showing only the plan label and no usage at all.

Real consumption is available, in two places CodexBar didn't read:

  1. credits_used, already present on each quota snapshot in the response CodexBar already fetches.
  2. GET /orgs/{org}/settings/billing/ai_credit/usage — org-wide, per-model.

Scope is larger than the issue described

#2593 said this would be "contained to the model decoder plus the fetcher's token-billing branch". That assumed a NamedRateWindow would work. It doesn't: Copilot's extraRateWindows are hidden entirely unless Budget extras is enabled (MenuCardView+ModelHelpers.swift:795), and usageKnown: false renders as the literal string "Unavailable". Credit rows are core data, so they needed their own path. Flagging the change rather than letting it pass unremarked.

GitHub publishes no credit entitlement anywhere

This is the finding that shaped the design. I probed all 8 documented billing endpoints plus budgets, cost-centers, included_credits, ai_credit/entitlement, copilot/metrics and usage/summary. None expose the included-credit ceiling — the "6,000" that the org billing page displays. discountQuantity reveals only what included credits absorbed, so the ceiling is observable only once exceeded.

It is derivable as seats × per-seat allowance, but the per-seat figure is currently a promotional 3,000 against a standard 1,900 — a hardcoded table would silently produce a wrong bar when the promo ends. So the denominator is user-entered and never inferred. A lane with no entitlement renders a text row, never a bar with a fabricated ceiling.

If you know of an endpoint I missed, that would simplify this considerably.

What this adds

Seat lane (default on). Numerator from premium_interactions.credits_used — deliberately not summed across snapshots, since chat/completions may report the same pool. No new endpoint and no new OAuth scope; it reads a field already in the existing response. Created only when it carries real signal (token-billed / unlimited quota / credits > 0 / entitlement set), so metered accounts reporting credits_used: 0 don't gain a permanent empty row.

Org lane (opt-in, off by default). GET /orgs/{org}/settings/billing/ai_credit/usage, org read from organization_login_list.first, summing usageItems[].grossQuantity filtered to unitType == "ai-credits". Strictly best-effort, shaped like the existing addBudgetWindowsIfNeeded: any failure returns the snapshot untouched. The device flow only requests read:user, so rejection is the expected common case, not an error — it now logs a warning rather than failing silently.

On my account the org endpoint returns 31.13 + 49.97 = 81.10, matching the billing page's numerator exactly.

Testing

  • swift test --filter Copilot — 125 tests / 13 suites green
  • make check — 0 violations across 1,706 files
  • make test — full suite green
  • All new tests use ProviderHTTPTransportStub or the existing URLProtocol stub. No live network, no Keychain access, per AGENTS.md.

Regression coverage worth calling out: a test asserts the #1258 behaviour still holds — a Business token-billing payload yields credits and primary == nil, secondary == nil. No guard line in CopilotUsageFetcher.swift is modified anywhere in this branch.

Two tests came out of review rather than the plan, and both were mutation-checked (the change was reverted to confirm the test actually fails): the tokenBasedBilling disjunct in the seat-lane gate, and the org-path percent-encoding.

Known gaps

  • Localization: L("AI credits"), L("Org credits"), L("credits used") have no Localizable.strings entries. Scripts/check-app-locales.mjs enforces all 23 catalogs together, so a partial addition fails make check — left out deliberately rather than done badly. Happy to add all 23 here if you'd prefer.
  • CLI parity: CLIRenderer doesn't render copilotCredits, so codexbar usage still shows nothing for these accounts.
  • Entitlement lives on the fetch path rather than being applied at render time. Consequence: the bar appears only after a successful refresh, and clearing the field leaves the cached denominator until one succeeds. Applying it in copilotCreditMetric would be smaller and instant — happy to rework if you prefer that shape.

Commits

17 focused commits, each with its own tests, left unsquashed so they can be reviewed in sequence. Squash on merge if you'd rather.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM

KSEGIT and others added 17 commits August 3, 2026 16:57
Copilot Business seats billed by credit report every quota as
unlimited with zeroed entitlement/remaining, which previously
rendered as a blank Copilot card. Decode credits_used and
organization_login_list so a later task can build a usage model
and UI on top.

QuotaSnapshot gains a creditsUsed field. Because a token-based-
billing snapshot is a placeholder by isPlaceholder's existing
definition (entitlement == 0 && remaining == 0), QuotaSnapshots
now captures premium_interactions' raw decode before the
placeholder filter nils it out, storing creditsUsed separately
so it survives. CopilotUsageResponse forwards it via
premiumInteractionsCreditsUsed and threads the value through the
quotaSnapshots rebuild in its own init(from:).

Task 1 of the copilot-ai-credits plan; decoding only, no model or
UI changes.
Implements CopilotCreditsUsage value type modeling credit consumption with Lane
subtype for seat and organization scopes. Includes usedPercent computed property
for usage rendering and full Codable/Equatable/Sendable conformance.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Attach CopilotCreditsUsage as an optional field on UsageSnapshot so
later tasks can populate and render it: stored property, CodingKeys,
memberwise init (parameter + assignment, defaulted nil so no existing
call site breaks), Codable persistence in init(from:)/encode(to:), and
a Replacement case in replacing(...).
- CopilotUsageFetcher gains seatEntitlement: Double? = nil, placed before
  transport: so existing call sites keep compiling unchanged.
- fetch() builds a CopilotCreditsUsage via a new makeCreditsUsage() helper
  and passes it through UsageSnapshot(copilotCredits:), riding alongside
  the existing steipete#1258 guards (unlimited-quota guard and
  tokenBasedBilling||hasUnlimitedQuota branch) without touching them.
- Adds two tests: seat credits surface for token-billing accounts while
  primary/secondary stay nil (regression guard), and credits are omitted
  (with the percent bar intact) for metered accounts with no credits_used.
Best-effort fetcher for org-wide AI credit usage from GitHub's billing
API. Returns nil on any failure (non-200, network error, malformed
JSON, unbuildable URL) since the OAuth device flow only requests
read:user and this call may be rejected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
.urlPathAllowed deliberately leaves "/" unescaped since it is meant for
encoding a full multi-segment path, but org is a single path segment.
An org value like "../../repos/x" passed through unchanged, letting an
authenticated request be pointed at an arbitrary path on api.github.com.

Escape "/" by subtracting it from the allowed set (matches the
CopilotDeviceFlow.formEncode / AntigravityLoginRunner.urlQueryValueAllowed
convention elsewhere in the repo), and add a regression test that
verifies the separator survives on the wire as %2F.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Adds CopilotCreditEntitlementParser (validates a user-entered credit
allowance: blank/non-numeric/non-positive -> nil) and three new
CopilotProviderSettings fields (orgCreditsEnabled, seatCreditEntitlement,
orgCreditEntitlement). GitHub publishes no credit entitlement on any
billing endpoint, so the denominator for a usage bar must be user-supplied.

Entitlements are stored as raw strings in SettingsStore
(copilotSeatCreditEntitlementRaw / copilotOrgCreditEntitlementRaw) and
parsed only at the copilotSettingsSnapshot boundary, so a partially-typed
value is never destroyed mid-edit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Wires the seat entitlement into CopilotUsageFetcher and, when the user
opts into org billing, fetches the org credit lane via
CopilotOrgCreditsFetcher and merges it onto the snapshot. The org fetch
is strictly best-effort: any missing toggle, missing org login, or
failed fetch leaves the snapshot unchanged, so it can never degrade the
seat lane, plan label, or budget windows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Task 7's mergingOrgLane unit test proved the pure merge function was
correct, but nothing exercised addOrgCreditsIfNeeded itself, so a
swapped entitlement field or a reordered toggle guard would have
stayed green. Add three tests that drive the real fetchOutcome/fetch()
path through CopilotProviderDescriptor:

- the org lane merges with entitlement sourced from orgCreditEntitlement
  (not seatCreditEntitlement) and resetsAt sourced from the seat lane
- the seat lane survives untouched when the org billing call is
  rejected (the common case: most tokens lack org billing access)
- the org billing endpoint is never contacted when the toggle is off

Reuses CopilotBudgetWebFetcherTests' existing
CopilotBudgetBindingStubURLProtocol (extending its canInit allowlist)
instead of registering a second global URLProtocol stub for the same
/copilot_internal/user endpoint -- an earlier attempt at a dedicated
stub class raced with it under Swift Testing's default parallel
execution across suites, since `@Suite(.serialized)` only serializes
within a suite, not across suites sharing global URLProtocol state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Adds copilotCreditMetrics to the general (non-antigravity) metrics path in
MenuCardView.swift, appended after extraRateWindowMetrics as a separate,
ungated call so credit rows are never hidden by copilotBudgetExtrasEnabled.
A lane without a user-entered entitlement renders as a text row
(statusText) rather than a bar with a fabricated denominator, per the
intent of upstream issue steipete#1258. Settings gains a per-seat/org entitlement
field pair and an "Organization AI credits" fetch toggle.

Deviates from the task brief in two ways, both intentional:
- The metrics builder is registered in MenuCardView.swift's `metrics(input:)`
  (right after its extraRateWindowMetrics append), not inside
  MenuCardView+ModelHelpers.swift's antigravityMetrics — that function only
  runs for .antigravity and never executes for .copilot.
- formatCredits pins every NumberFormatter property explicitly rather than
  just the locale. en_US_POSIX alone still yields "3000" not "3,000"
  because it sets usesGroupingSeparator to false by default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Turning off "Organization AI credits" only refreshed the provider, so a
failed follow-up refresh (offline, lost org access, 401) kept the last-good
snapshot's org row rendering for a disabled feature. Add
clearCopilotOrgCredits(), mirroring the sibling clearCopilotBudgetExtras()
synchronous strip, and wire it into the toggle's else branch.

Also adds UsageSnapshot.with(copilotCredits:), a public wrapper needed
because the app-target UsageStore extension cannot reach the
module-internal replacing(copilotCredits:) directly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
GitHub reports credits_used: 0 on metered (non-credit-billed) snapshots
too, so the field's mere presence isn't exclusive to token-billed seats.
Without a gate, any Copilot Pro/Individual seat could grow a permanent,
unremovable "0 credits used" row.

Thread hasUnlimitedQuota (already computed in fetch()) into
makeCreditsUsage and only build the lane when it carries actual signal:
token/unlimited billing, nonzero credits, or a user-configured
entitlement. The steipete#1258 guards in fetch() and makeRateWindow are untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
The org-lane fetch swallowed every failure via try? with zero
diagnostics, while the sibling budget-extras fetch logs a matching
warning. Since a token without org billing access is the common case
(device flow only requests read:user), the prior UX was "I enabled it
and nothing happened." Split the single guard chain into three explicit
failure points (transport error, non-200 status, decode failure), each
logging "Copilot org credits unavailable" before returning nil. The
best-effort return-nil behavior is unchanged.

Also filter usageItems to unitType == "ai-credits" before summing:
the endpoint is credit-scoped today, but an unrelated line item would
otherwise silently inflate the total.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
A user in multiple orgs would silently see org steipete#1's numbers with no
indication of which org the row described. Parenthesize the org login
in the row title, following the .doubao team-title qualifier pattern
already used elsewhere in MenuCardView+ModelHelpers.swift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
The binding -> UserDefaults -> copilotSettingsSnapshot chain for the
org-credits toggle and both entitlement fields was untested. Add three
tests following the existing "openai exposes project id setting"
pattern, including that the org entitlement field's isVisible follows
the toggle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Add an Unreleased entry for steipete#2593 (thanks @KSEGIT), and document the
org credits endpoint (headers, opt-in/best-effort framing), the seat
lane's snapshot mapping (and why it isn't summed across snapshots), the
new AI credit entitlements section explaining the user-entered
denominators, and the two new key files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nai7wSG88xfy6HiMxYc4uM
Four small, independent cleanups ahead of the upstream PR:

- Fix a stale "below" reference in a code comment; the .doubao
  team-title pattern it points to is above, not below.
- Document the enterprise-host (GHES) API host in docs/copilot.md's
  org-credits section, matching section 2's phrasing -- the fetcher
  already routes through the same apiHost(enterpriseHost:) helper.
- Add a test isolating the tokenBasedBilling disjunct in the seat-lane
  signal gate, so a Business account reporting zero credits early in
  the billing month still gets a seat lane even when no other disjunct
  is true.
- Stop CopilotOrgCreditsFetcher from fabricating a zero when usageItems
  is non-empty but nothing matches unitType == "ai-credits"; that case
  now returns nil (unknown) instead of a misleading zero, while a
  genuinely empty usage list still returns zero.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aaeb632f41

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

else { return nil }
return CopilotDeviceFlow.makeRequestURL(
host: CopilotUsageFetcher.apiHost(enterpriseHost: enterpriseHost),
path: "/orgs/\(encoded)/settings/billing/ai_credit/usage")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the documented organizations billing path

For the organization AI-credit flow, GitHub's REST docs list this endpoint as GET /organizations/{org}/settings/billing/ai_credit/usage, not /orgs/{org}/... (checked https://docs.github.com/en/rest/billing/usage#get-billing-ai-credit-usage-report-for-an-organization). With the current path, authorized org admins receive a 404 and fetchCreditsUsed returns nil, so the new organization credits row never appears.

Useful? React with 👍 / 👎.

return 0
}

let creditItems = report.usageItems.filter { $0.unitType == "ai-credits" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept the org AI-credit unit type

For organization AI-credit reports, GitHub's example response uses unitType: "credits", while "ai-credits" is shown for user-level reports (checked https://docs.github.com/en/rest/billing/usage#get-billing-ai-credit-usage-report-for-an-organization). After the path is corrected, real org responses will fall into the “none matched” branch here and return nil, so organization usage still will not render unless this accepts the org unit type.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. labels Aug 4, 2026
@clawsweeper

clawsweeper Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed August 4, 2026, 3:59 PM ET / 19:59 UTC.

ClawSweeper review

What this changes

This PR adds Copilot AI-credit decoding and menu rows, user-entered credit allowances, and an opt-in organization billing usage fetch.

Merge readiness

Blocked until real behavior proof is added - 13 items remain

Keep open. The seat-credit portion addresses a source-proven Copilot gap, but the optional organization lane still lacks a supported authorization path, uses the previously flagged REST route and unit assumptions, and has no inspectable after-fix runtime proof.

Priority: P2
Reviewed head: aaeb632f41d13122978b3f8422a6d104bd470edb
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) The proposal has substantial unit coverage, but unresolved authorization/API blockers and missing real runtime proof make it not merge-ready.
Proof confidence 🦪 silver shellfish (2/6) Needs real behavior proof before merge: The body claims live observations but provides no inspectable redacted menu capture, terminal output, runtime log, or linked artifact showing the after-fix seat or organization behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦪 silver shellfish (2/6) 4 actionable review findings remain.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: The body claims live observations but provides no inspectable redacted menu capture, terminal output, runtime log, or linked artifact showing the after-fix seat or organization behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 5 items Current main still lacks the requested credit metric: Current main decodes the Copilot usage response and deliberately clears both rate windows for token-based or unlimited quotas, but does not decode or store a credit-usage alternative.
Standard login lacks organization-billing authorization: The current device flow requests only read:user, while the PR's organization lane reuses that token for an organization billing endpoint.
Prior blockers remain on the same head: The prior ClawSweeper review and two inline review comments identified the unsupported authorization path plus the organization route and unit-type mismatches; the current head remains aaeb632.
Findings 4 actionable findings [P1] Gate organization billing behind an authorized login path
[P2] Use the documented organization billing route
[P2] Accept the documented organization credit unit
Security None None.

How this fits together

CodexBar authenticates a Copilot account, fetches GitHub usage data, stores it in a provider snapshot, and renders that snapshot as menu-bar metrics. This PR adds credit-based metrics for token-billed seats and an optional organization-wide billing enrichment.

flowchart TD
A[GitHub device login] --> B[Copilot usage response]
B --> C[Credit usage decoding]
C --> D[Provider usage snapshot]
D --> E[Menu bar credit rows]
A --> F[Optional organization billing request]
F --> D
Loading

Decision needed

Question Recommendation
Should CodexBar support organization-wide Copilot billing by expanding or separately configuring GitHub authorization, or should this PR be narrowed to the existing-response seat-credit metric? Land seat credits only: Remove or defer the organization toggle, billing request, and organization allowance while preserving the existing-response seat metric.

Why: The standard login only obtains read:user, so adding organization billing is not a mechanical implementation choice; it changes the provider authorization and organization-data access contract.

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: The body claims live observations but provides no inspectable redacted menu capture, terminal output, runtime log, or linked artifact showing the after-fix seat or organization behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Gate organization billing behind an authorized login path (P1) - The normal Copilot device flow obtains only read:user, yet enabling this setting immediately reuses that token for organization billing. Most standard app logins will therefore only produce a warning and no organization row; add a maintainer-approved reauthorization/configured-token path with visible status, or defer the organization lane and retain seat credits only.
  • Use the documented organization billing route (P2) - The existing review correctly noted that GitHub documents the organization report under /organizations/{org}/..., not this /orgs/{org}/... path. Authorized users will receive no organization data until the route and its fixture are corrected.
  • Accept the documented organization credit unit (P2) - Organization billing examples use unitType: "credits"; filtering only for "ai-credits" makes valid organization reports fall through as unavailable. Accept the documented organization unit and cover it with a response fixture.
  • Apply allowance changes without waiting for another fetch (P2) - This stores the user-entered allowance in the fetched snapshot, so editing or clearing the setting leaves the cached metric showing its old denominator until a successful network refresh. Apply the allowance while deriving the menu metric, or synchronously update cached credit lanes and add coverage for both edit and clear; this was visible on the earlier reviewed head, which is the current head.
  • Resolve merge risk (P1) - The organization setting will normally make a billing request with a read:user device-flow token, so users can enable a feature that has no supported path to return data.
  • Resolve merge risk (P1) - The branch is currently reported as dirty/merge-conflicted and needs a rebase before any final correctness or upgrade review.
  • Resolve merge risk (P1) - Credit allowances are stored in fetched snapshots, so changing or clearing an allowance can leave a cached progress bar with the old denominator until a successful refresh.
  • Complete next step (P2) - A maintainer must decide the organization authorization and data-access contract before an automated repair can safely narrow or retain that lane.
  • Improve patch quality - Resolve the organization authorization direction and the documented route and unit contract.
  • Improve patch quality - Apply allowance edits to the cached/rendered metric immediately and cover that behavior.
  • Improve patch quality - Post redacted after-fix proof from a freshly built app or terminal/log output, then update the PR body for re-review.

Findings

  • [P1] Gate organization billing behind an authorized login path — Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift:129-132
  • [P2] Use the documented organization billing route — Sources/CodexBarCore/Providers/Copilot/CopilotOrgCreditsFetcher.swift:44-46
  • [P2] Accept the documented organization credit unit — Sources/CodexBarCore/Providers/Copilot/CopilotOrgCreditsFetcher.swift:89-93
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production versus tests production +562/-10; tests +969/-3 The broad 26-file implementation has substantial fixture coverage, but the central authorized runtime path still needs real behavior proof.

Merge-risk options

Maintainer options:

  1. Narrow to the authorized seat path (recommended)
    Remove the organization billing lane and retain only credit data already returned by the existing Copilot usage request.
  2. Approve and implement an organization authorization contract
    Adopt explicit consent and a supported reauthorization or configured-token path before retaining the organization toggle.
  3. Pause the combined PR
    Defer this branch if maintainers do not want to expand Copilot authorization or expose organization billing data.

Technical review

Best possible solution:

Land the no-new-scope seat-credit metric as a focused change, and defer organization billing until maintainers approve an explicit authorization, consent, endpoint, and response-contract design.

Do we have a high-confidence way to reproduce the issue?

Yes for the underlying gap: current main explicitly suppresses token-billed quota bars and has no alternative credit field, matching the supplied account payload. The after-fix live behavior has not been independently demonstrated with an inspectable artifact.

Is this the best way to solve the issue?

No. Decoding seat credits from the existing response is a narrow solution, but coupling it to an organization billing fetch without an approved authorization path and verified response contract is not the safest implementation.

Full review comments:

  • [P1] Gate organization billing behind an authorized login path — Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift:129-132
    The normal Copilot device flow obtains only read:user, yet enabling this setting immediately reuses that token for organization billing. Most standard app logins will therefore only produce a warning and no organization row; add a maintainer-approved reauthorization/configured-token path with visible status, or defer the organization lane and retain seat credits only.
    Confidence: 0.96
  • [P2] Use the documented organization billing route — Sources/CodexBarCore/Providers/Copilot/CopilotOrgCreditsFetcher.swift:44-46
    The existing review correctly noted that GitHub documents the organization report under /organizations/{org}/..., not this /orgs/{org}/... path. Authorized users will receive no organization data until the route and its fixture are corrected.
    Confidence: 0.93
  • [P2] Accept the documented organization credit unit — Sources/CodexBarCore/Providers/Copilot/CopilotOrgCreditsFetcher.swift:89-93
    Organization billing examples use unitType: "credits"; filtering only for "ai-credits" makes valid organization reports fall through as unavailable. Accept the documented organization unit and cover it with a response fixture.
    Confidence: 0.92
  • [P2] Apply allowance changes without waiting for another fetch — Sources/CodexBarCore/Providers/Copilot/CopilotUsageFetcher.swift:75-80
    This stores the user-entered allowance in the fetched snapshot, so editing or clearing the setting leaves the cached metric showing its old denominator until a successful network refresh. Apply the allowance while deriving the menu metric, or synchronously update cached credit lanes and add coverage for both edit and clear; this was visible on the earlier reviewed head, which is the current head.
    Confidence: 0.95
    Late finding: first raised on code an earlier review cycle already covered.

Overall correctness: patch is incorrect
Overall confidence: 0.94

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against a82f509ea8e7.

Labels

Label justifications:

  • P2: This is a useful Copilot usage improvement, but it is not an emergency because the current behavior avoids false quota reporting.
  • merge-risk: 🚨 auth-provider: The optional organization fetch changes how the existing GitHub OAuth token is used and lacks a supported authorization path.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The body claims live observations but provides no inspectable redacted menu capture, terminal output, runtime log, or linked artifact showing the after-fix seat or organization behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

What I checked:

Likely related people:

  • Peter Steinberger: Recent Copilot device-flow and provider-setting history is heavily associated with this area, including the current release baseline. (role: recent area contributor; confidence: high; commits: 6a16c23313a7, ad33b32773bd; files: Sources/CodexBarCore/Providers/Copilot/CopilotDeviceFlow.swift, Sources/CodexBar/Providers/Copilot/CopilotProviderImplementation.swift)
  • Luís Miguel: Introduced the existing optional Copilot budget-extra fetch pattern that this PR extends. (role: introduced adjacent optional-enrichment behavior; confidence: high; commits: 7a703369f453; files: Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift)
  • Zihao Qi: Introduced the unlimited Copilot quota guard that correctly prevents misleading rate bars and defines the boundary for the new credit metric. (role: introduced current quota-suppression behavior; confidence: high; commits: 6d71af30b84d; files: Sources/CodexBarCore/Providers/Copilot/CopilotUsageFetcher.swift)
  • Yash Raj Pandey: Added the earlier token-billing unavailable-quota handling that is directly related to the affected account type. (role: token-billing behavior contributor; confidence: medium; commits: ffd8d75a9f39; files: Sources/CodexBarCore/Providers/Copilot/CopilotUsageFetcher.swift)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (3 earlier review cycles)
  • reviewed 2026-08-04T12:34:46.303Z sha aaeb632 :: needs real behavior proof before merge. :: [P1] Defer the unauthorizable organization-credit toggle | [P2] Use the documented organization billing route | [P2] Accept the organization report credit unit
  • reviewed 2026-08-04T15:03:11.007Z sha aaeb632 :: needs real behavior proof before merge. :: [P1] Gate organization billing behind an authorized path | [P2] Use the documented organization billing endpoint | [P2] Accept the documented organization credit unit
  • reviewed 2026-08-04T16:39:28.276Z sha aaeb632 :: needs real behavior proof before merge. :: [P1] Gate organization credits behind a supported authorization path | [P2] Use the documented organization billing route | [P2] Accept the documented organization credit unit

@steipete

steipete commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Thanks for this, and for the excellent original report in #2593 — you found both the credits_used field and the org-billing endpoint. Heads-up on sequencing: #2613 is green and staged to land stage 1 of this (decoding credits_used from the already-fetched copilot_internal/user response). Rather than closing anything, the ask is: once #2613 merges, could you rebase this PR on top so it carries just the remainder (the org billing endpoint and the UI surface beyond stage 1)? Your issue and this PR shaped the direction here, so we'd love to land the rest with your name on it.

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

Labels

merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Copilot Business (token-based billing): surface GitHub AI credit usage — card is blank because every quota reports unlimited/zero-entitlement

2 participants