Skip to content

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

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

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

Conversation

@KSEGIT

@KSEGIT KSEGIT commented Aug 4, 2026

Copy link
Copy Markdown

Closes #2593.

Rebased 2026-08-14 onto e915ca13f. Since #2613 landed stage 1 (decoding credits_used and rendering it through the shared provider-detail contract), this branch carries only the remainder: user-entered per-account credit entitlements and the progress-bar display beyond stage 1. Seat credits flow through main's shared detail rows — no parallel credit state.

The organization lane has been removed from this PR (was deferred per review). This is now seat credits only. Happy to bring the org lane back in a follow-up once an authorization model is approved.

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 credits_used, already present on each quota snapshot in the response CodexBar already fetches (landed in #2613).

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 row with no entitlement renders as text, never a bar with a fabricated ceiling.

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

What this adds

Seat credit bar (builds on #2613). Extends the shared provider-detail row contract with an optional row id, progress ratio, and retained numeric usage (ProviderDetailSection.Row), so the existing "Credits used" row becomes a "31 / 3000" bar when a seat entitlement is set, and stays plain text otherwise. 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. No guard line in CopilotUsageFetcher.swift is modified — the #1258 guard block is byte-identical to main.

Per-account entitlements. The seat allowance is stored on the selected ProviderTokenAccount (following the z.ai per-account fields precedent), resolved in copilotSettingsSnapshot with the existing global UserDefaults value as fallback — so existing single-account setups keep working unchanged, and two Copilot accounts with different allowances each get the right denominator. The settings field writes to the selected account when one exists.

Cached rows repair immediately. Changing or clearing the entitlement rewrites the cached "Credits used" row synchronously, in both the live snapshot and the last-known-reset baseline. The numerator comes from the row's structured progress, falling back to the retained usageValue on text-only rows — never re-parsed from display text. This is what lets a cached text-only row grow a bar the moment an entitlement is entered even when the follow-up refresh never lands (offline, token lost, 401), and it is why the row carries usageValue at all.

Which surfaces this covers

Worth being explicit, since it's a menu bar app:

Surface Covered
Menu card (provider card rows) ✅ yes
Menu bar icon ❌ no
Widget ❌ no
codexbar usage CLI ❌ no

Credits are detail rows, not a RateWindow, because a row with no user-entered entitlement has no percentage to show. MenuBarLayout, IconRemainingResolver and the widget all build from primary/secondary/tertiary/extraRateWindows, so they see nothing.

The consequence: a Business token-billing user still gets an empty menu bar icon and must open the menu to see credits. I'd rather state that than have you find it.

Making the icon work needs a decision I didn't want to make unilaterally: publishing credits as a NamedRateWindow when an entitlement exists would light up the icon, widget and the existing "menu bar secondary metric" picker for free, but Copilot's extra windows are gated behind copilotBudgetExtrasEnabled in the card renderer, so it risks either double-rendering the row or coupling credits to an unrelated setting. Happy to do it in this PR if you'll say which shape you want.

Live behavior on the current head

Captured from a build of this branch against my live Copilot Business seat (token-based billing). Full sequence and caveats in this comment.

Bar renders, online — 2455 / 3000 online 3000
Cached row survives a failed refresh — offline banner, still 2455 / 3000 offline 3000
Changed entitlement re-renders the ratio — 2455 / 5000, shorter fill online 5000
And survives offline at the new entitlement offline 5000

Stated plainly: the two offline frames sit either side of a brief reconnect, so they do not form one unbroken offline stretch spanning an entitlement change. The exact text↔bar transition is covered by unit tests rather than by these captures — see UsageStoreCoverageTests.

Testing

Verified on the current head against e915ca13f:

  • 193 focused tests across the 15 touched suites green (fetcher, parser, settings, wiring, menu-card model, detail sections, cached-row repair, persistence round-trip).
  • make check — 0 violations, 0 serious across 1,878 files.
  • ProviderArchitectureGatekeeperTests — 38/38. The credit-entitlement additions shift 13 allowlisted provider constructs past the gatekeeper's two-line anchor tolerance, so this branch re-anchors those entries to their current lines; anchors, provider sets and fingerprints are unchanged.
  • Full suite, run in 4 shards: 18/18, 18/18 and 17/17 groups green, plus the 85 selections in the remaining shard re-run separately (714 tests / 59 suites green).

One pre-existing failure, unrelated to this PR: MiniMaxMenuCardBillingTests expects "1,234" but gets "1.234" (locale-dependent number grouping; this machine is en_PL). It reproduces identically on a pristine origin/main worktree with none of these changes applied. Note that a plain make test aborts on it rather than reporting and continuing — that is why the evidence above is sharded.

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. Per-account tests cover: account override wins over the global fallback, fallback when the account has no value, two accounts producing different snapshot entitlements, and a Codable round-trip through the config store (including legacy JSON without the new keys). Cached-repair tests cover text→bar transition, clear-restores-text-only, no-numeric-usage no-op, and the stale-reset-baseline path.

Known limitations

  • Localization: L("AI credits") and 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.
  • Account field semantics: an account without an override displays the global fallback value; saving the field unchanged freezes that value onto the account. Clearing the field removes the override and re-displays the fallback.
  • No pace/projection on credit rows yet, though resetsAt is available and a monthly credit budget is a good fit for it (cf. Show ahead/behind pace for all providers and in the menu bar #807, Add Projections to Copilot Tracker #2166).

Commits

12 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

@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".

Comment thread Sources/CodexBarCore/Providers/Copilot/CopilotOrgCreditsFetcher.swift Outdated
Comment thread Sources/CodexBarCore/Providers/Copilot/CopilotOrgCreditsFetcher.swift Outdated
@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 maintainer review before merge. Reviewed August 15, 2026, 10:58 AM ET / 14:58 UTC.

ClawSweeper review

What this changes

Adds a user-entered, per-Copilot-account AI-credit entitlement that turns token-billed seat consumption into a menu-card progress row and immediately rebuilds cached rows when the value changes.

Merge readiness

⚠️ Ready for maintainer review - 3 items remain

Keep open for maintainer product sign-off: the branch contains the still-missing menu-card UI and per-account setting beyond the merged decoding work, has no blocking code finding, and includes current-head live proof.

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

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) Strong current-head UI proof and focused coverage support a clean feature patch, pending the repository-required product sign-off.
Proof confidence 🦞 diamond lobster (5/6) ✨ media proof bonus Sufficient (screenshot): The supplied current-head screenshots directly show the live menu-card bar and the cached row surviving offline refresh failures at two entitlement values.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (screenshot): The supplied current-head screenshots directly show the live menu-card bar and the cached row surviving offline refresh failures at two entitlement values.
Evidence reviewed 6 items Seat-only rendering path: The PR head builds credit detail rows from the existing quota response while retaining nil primary and secondary windows for token-billed or unlimited quota accounts, preserving the earlier no-fake-percentage behavior.
Account migration and fallback: The selected account override falls back to the existing global value and parses the result only at the Copilot settings snapshot boundary, so legacy accounts without the optional field retain the prior behavior.
Offline cached-row repair: The head rebuilds the identified credit row from structured progress or retained numeric usage and updates both live and reset-baseline snapshots without a network call.
Findings None None.
Security None None.

How this fits together

CodexBar fetches Copilot quota data, converts it to shared provider detail rows, persists selected-account settings, and renders those rows in the menu card. This change supplies an optional user-entered credit ceiling between the fetched consumption value and that row renderer.

flowchart LR
A[Copilot usage API] --> B[Usage snapshot]
C[Selected account setting] --> D[Credit entitlement parser]
B --> E[Credit detail row]
D --> E
E --> F[Cached usage snapshot]
C --> G[Cached row update]
G --> F
F --> H[Menu card]
Loading

Decision needed

Question Recommendation
Should CodexBar ship a menu-card-only, per-account user-entered AI-credit ceiling for Copilot token-billed seats now, without adding icon, widget, or CLI exposure? Approve the seat-only menu-card path: Land the existing-endpoint, no-new-auth-scope implementation and treat other display surfaces as follow-up work.

Why: VISION.md requires sign-off for this new persisted feature, and source review cannot determine whether the card-only scope is the intended product contract.

Before merge

  • Resolve merge risk (P1) - The feature intentionally remains menu-card-only; menu-bar icon, widget, and CLI output will not expose this credit metric after merge.
  • Complete next step (P2) - A maintainer must choose whether the new persisted, menu-card-only entitlement surface is an accepted product contract; no mechanical repair is outstanding.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Diff scope 28 files; non-test +402/-39, tests +797 The feature spans shared detail serialization, account persistence, rendering, and focused regression coverage.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #2593
Summary: This PR is the candidate UI and per-account-setting completion for the original token-billed Copilot credit-visibility report; the merged prerequisite supplied only decoding.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Approve the narrow seat-only path if the stored, user-entered ceiling is acceptable, keeping credits in shared detail rows and deferring icon/widget/CLI semantics to a separately scoped decision.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Approve the narrow seat-only path if the stored, user-entered ceiling is acceptable, keeping credits in shared detail rows and deferring icon/widget/CLI semantics to a separately scoped decision.

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

Not applicable as a bug reproduction: the requested new behavior is directly shown on a live token-billed Business seat in four current-head screenshots.

Is this the best way to solve the issue?

Yes for the seat-only scope: it uses the existing Copilot response, avoids inventing an unknown entitlement, and reuses shared detail rows; accepting the new persisted setting remains a maintainer decision.

AGENTS.md: found and applied where relevant.

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

Labels

Label justifications:

  • P2: This is a bounded Copilot usability feature with demonstrated value but no urgent availability, security, or data-loss impact.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (screenshot): The supplied current-head screenshots directly show the live menu-card bar and the cached row surviving offline refresh failures at two entitlement values.
  • proof: sufficient: Contributor real behavior proof is sufficient. The supplied current-head screenshots directly show the live menu-card bar and the cached row surviving offline refresh failures at two entitlement values.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The supplied current-head screenshots directly show the live menu-card bar and the cached row surviving offline refresh failures at two entitlement values.

Evidence

What I checked:

Likely related people:

  • Yuxin-Qiao: Authored merged commit 2d76cd9 that introduced the credits_used foundation this branch extends. (role: introduced the merged credit-decoding prerequisite; confidence: high; commits: 2d76cd9e9fca; files: Sources/CodexBarCore/Providers/Copilot/CopilotUsageFetcher.swift)
  • steipete: The owner directed the staged approach in the PR discussion and authored the current-main commit opening the active unreleased release section. (role: prior scope reviewer and recent current-main contributor; confidence: high; commits: f15f142a7787; files: VISION.md, CHANGELOG.md)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Obtain maintainer approval for the menu-card-only persisted-entitlement scope.

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 (32 earlier review cycles; latest 8 shown)
  • reviewed 2026-08-09T22:02:41.600Z sha 14599df :: needs real behavior proof before merge. :: [P2] Preserve numeric usage for text-only credit rows
  • reviewed 2026-08-10T20:46:31.316Z sha 766101c :: needs real behavior proof before merge. :: none
  • reviewed 2026-08-12T12:09:11.863Z sha 766101c :: needs real behavior proof before merge. :: none
  • reviewed 2026-08-14T10:54:47.693Z sha 885b10e :: needs real behavior proof before merge. :: none
  • reviewed 2026-08-14T11:27:00.534Z sha ddd77ff :: needs real behavior proof before merge. :: [P2] Move the feature note out of released v0.49.6
  • reviewed 2026-08-14T22:17:20.929Z sha ddd77ff :: needs real behavior proof before merge. :: [P2] Remove the entry from released v0.49.6
  • reviewed 2026-08-15T00:43:51.820Z sha 2d25ce6 :: needs real behavior proof before merge. :: [P2] Keep the rebuilt row in the account cache
  • reviewed 2026-08-15T10:25:31.460Z sha 2d25ce6 :: found issues before merge. :: [P3] Remove the release-owned changelog entry

@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.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Aug 5, 2026
@KSEGIT

KSEGIT commented Aug 7, 2026

Copy link
Copy Markdown
Author

Re status: 📣 needs proof — here's what I can evidence, and what I can't.

The upstream data is real, not inferred

Live from a Copilot Business seat (redacted), via the endpoint CodexBar already calls:

"quota_snapshots": {
  "premium_interactions": { "unlimited": true, "entitlement": 0, "remaining": 0,
                            "percent_remaining": 100.0, "credits_used": 31,
                            "token_based_billing": true, "overage_permitted": true },
  "chat":        { "unlimited": true, "entitlement": 0, "credits_used": 0 },
  "completions": { "unlimited": true, "entitlement": 0, "credits_used": 0 }
},
"organization_login_list": ["<ORG>"],
"copilot_plan": "business", "token_based_billing": true,
"quota_reset_date": "2026-09-01"

Note credits_used: 0 on chat/completions — that's why the seat lane reads only premium_interactions rather than summing, and why lane creation is gated on real signal instead of on the field merely decoding.

And the org endpoint:

{ "usageItems": [
    { "product": "Copilot", "sku": "Copilot AI Credits",  "model": "Code Review model",
      "unitType": "ai-credits", "pricePerUnit": 0.01, "grossQuantity": 31.13 },
    { "product": "Copilot", "sku": "Copilot Cloud Agent", "model": "Coding Agent model",
      "unitType": "ai-credits", "pricePerUnit": 0.01, "grossQuantity": 49.97 } ] }

31.13 + 49.97 = 81.10, which matches the "81 / 6,000 AI credits" numerator on the org billing page exactly. That agreement is the strongest evidence I have that the org lane reads the right thing.

What the card actually renders

Asserted on exact strings through the real UsageMenuCardView.Model.make(...) path, not on internals:

Case Assertion Location
Entitlement set title == "AI credits", detailLeftText == "31 / 3000", statusText == nil (bar renders) CopilotMenuCardModelTests.swift:149-151
No entitlement statusText == "31 credits used" (text row, bar suppressed) :161
Org lane row id copilot-org-credits, title carries the org login :170
Org via real fetch path org?.creditsUsed == 81.1, org?.entitlement == 6000 CopilotBudgetWebFetcherTests.swift:516-518
Org call rejected (403) copilotCredits?.org == nil, seat lane survives :582
Toggle off endpoint never contacted (Issue.record on any request) :641

The org tests drive descriptor.fetchPlan.fetchOutcome, so they exercise the production strategy rather than a shortcut.

What I can't evidence yet

No screenshot. Producing one means building this branch, packaging it, and running it against a real Copilot Business account — which per AGENTS.md is exactly the kind of validation that can raise Keychain prompts, so I didn't do it unasked. Happy to if you want it.

Two of three surfaces are untouched. I've added a table to the PR description rather than leave it implied: the menu card shows credits; the menu bar icon, the widget, and codexbar usage do not. Credits are a new UsageSnapshot field rather than a RateWindow (a lane without a user-entered entitlement has no percentage), and those three surfaces all build from primary/secondary/tertiary/extraRateWindows.

So today a Business token-billing user still gets an empty icon and has to open the menu. I can wire the icon up in this PR — publishing credits as a NamedRateWindow when an entitlement exists would cover icon, widget and the existing secondary-metric picker at once — but Copilot's extra windows are gated behind copilotBudgetExtrasEnabled in the card renderer, so it needs a call on whether to reuse that gate, add a new one, or exclude these windows from the card path to avoid double-rendering. Tell me which and I'll do it.

Since the PR was opened

  • Swapped a private number formatter for UsageFormatter.creditsNumberString (bfe422a), so Copilot and Codex credit numbers no longer disagree on grouping in the same menu.
  • Documented the global-entitlement limitation for multi-account setups, and the org-selection behaviour, in the PR description.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Aug 7, 2026
@KSEGIT
KSEGIT force-pushed the copilot-ai-credits branch from bfe422a to b7406fb Compare August 8, 2026 21:04
@KSEGIT

KSEGIT commented Aug 8, 2026

Copy link
Copy Markdown
Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Aug 8, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. and removed merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. labels Aug 8, 2026
@clawsweeper clawsweeper Bot added the rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. label Aug 14, 2026
@KSEGIT
KSEGIT force-pushed the copilot-ai-credits branch from ddd77ff to 2d25ce6 Compare August 15, 2026 00:39
@KSEGIT

KSEGIT commented Aug 15, 2026

Copy link
Copy Markdown
Author

Fixed the P2 release-note finding. Rebased onto current main; new head 2d25ce67b.

The entry had drifted into a released section through no intent of mine: an earlier rebase auto-merged it under what was then 0.49.7 — Unreleased, and main subsequently shipped 0.49.6 — 2026-08-14 and opened a new 0.49.7, leaving the entry inside the released one. Good catch — merging that would have credited this feature to a release that never contained it.

It now sits under ## 0.49.7 — Unreleased in ### Added, and the 0.49.6 section is byte-identical to main again (my commit had also introduced an ### Added heading there, which is gone). The branch's only CHANGELOG change is that single added line.

One deliberate divergence from the recommendation: the suggestion was to remove the entry and let the release process place it when shipped, but main's own 0.49.7 — Unreleased carries contributor-credited entries (Thanks @Carl723000!, Thanks @gnattu!), so the convention here looks like contributor-added notes under the unreleased heading. I moved it rather than removing it. Happy to drop it entirely if the release tooling would rather own that line.

Re-verified on the new base: ProviderArchitectureGatekeeperTests 38/38 (the re-anchored entries survived this rebase unchanged), make check 0 violations across 1,879 files, and the suites this PR touches at 194 tests / 15 suites. I did not re-run the full sharded suite for this change, since the only branch-side edit is a changelog line.

Still outstanding and unchanged: the current-head offline text↔bar proof, which is mine to capture, and the scope sign-off called out under "Decision needed".

KSEGIT added a commit to KSEGIT/CodexBar that referenced this pull request Aug 15, 2026
@KSEGIT

KSEGIT commented Aug 15, 2026

Copy link
Copy Markdown
Author

Current-head live capture, taken from a build of this branch running against my Copilot Business seat:

Copilot credit bar at current head — Credits used 2455 / 3000

What this shows: a real token-billed Business seat rendering Credits used as a 2455 / 3000 bar in the menu card, with the reset date resolving to 1 Sep at 02:00, on a build of the current head rather than the earlier b7406fb8 one.

Being straight about its limits: this is the end state, not the transition. It does not by itself distinguish the cached text→bar repair from an ordinary successful refresh — the card reads "Updated just now", so a fetch had just landed. The offline sequence that isolates the cached rewrite (clear the entitlement with the network down, watch the row collapse to plain text and come back as a bar while the last-updated timestamp never advances) is still to come, and I'll post it as a follow-up.

Posting this now because it does close one gap the last review named: the live bar existing at the current head rather than only at b7406fb8.

KSEGIT added a commit to KSEGIT/CodexBar that referenced this pull request Aug 15, 2026
KSEGIT added a commit to KSEGIT/CodexBar that referenced this pull request Aug 15, 2026
KSEGIT added a commit to KSEGIT/CodexBar that referenced this pull request Aug 15, 2026
@KSEGIT

KSEGIT commented Aug 15, 2026

Copy link
Copy Markdown
Author

Current-head behavior evidence, all four frames captured from a build of this branch against my live Copilot Business seat (KSEGIT (Business), token-based billing).

1 — Bar renders at current head, online. Credits used as 2455 / 3000, reset date resolving.

online, 2455 / 3000

2 — The cached row survives a failed refresh. Network off; the card shows "The Internet connection appears to be offline" and the credit row is still rendered as a bar at 2455 / 3000 from cache, rather than disappearing or losing its denominator.

offline, 2455 / 3000

3 — A changed entitlement re-renders the row. Allowance changed 30005000; the row becomes 2455 / 5000 with a correspondingly shorter fill. Same numerator, new denominator, no re-fetch of usage data involved in producing the ratio.

online, 2455 / 5000

4 — And that survives offline too. Network off again; offline banner present, row still 2455 / 5000 from cache at the new entitlement.

offline, 2455 / 5000

What these do and don't establish

They establish, on the current head: the bar renders on a real token-billed Business seat; the cached row survives a failed refresh with its denominator intact at two different entitlements; and changing the entitlement re-renders the ratio.

They do not isolate the in-place cached rewrite as tightly as I'd like. Frames 2 and 4 are both offline but sit either side of a brief reconnect, so there is no single unbroken offline stretch spanning an entitlement change. I'd rather say that plainly than let the sequence imply more than it shows.

Worth noting why the literal "clear it and watch it return to text" capture is awkward on my setup specifically: the settings field resolves as account override ?? global fallback, and I have a global fallback of 3000 stored from before per-account entitlements existed. Clearing the field removes the override, the getter immediately falls back to 3000, and the box repopulates — so an empty field is unreachable without also removing the global value. That is the documented "Account field semantics" limitation, not the cached-row path, and it is the reason the evidence above changes the entitlement rather than clearing it. If that fallback-shadowing is itself worth changing, I'm happy to make clearing write an explicit empty override in a follow-up — it's a small change but it widens this diff.

Unit coverage for the exact text↔bar transition is in UsageStoreCoverageTests: text→bar on entering an entitlement, clear-restores-text-only, no-numeric-usage no-op, and the stale-reset-baseline path.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Aug 15, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Aug 15, 2026
KSEGIT and others added 13 commits August 15, 2026 16:36
Remove the opt-in organization billing lane pending an approved
authorization model for org-wide billing reads with device-flow tokens.

Rewrite the cached seat credit row immediately when the entitlement
changes so a stale denominator or bar never survives a failed refresh.
Text-only credit rows carried no structured usage, so entering an
entitlement during an offline or failed refresh left the cached row
without its bar. Stamp usageValue on the row at fetch time and use it
as the repair numerator.
Rebases landed the entry under 0.49.6, which shipped on 2026-08-14, so
merging would advertise this feature as part of that release. Move it to
0.49.7 Unreleased; 0.49.6 is now byte-identical to main.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016puVootfTssyPQe2TzmK1M
The credit-entitlement additions to MenuCardView, SettingsStore, and
UsageFetcher shift 13 allowlisted provider constructs past the
gatekeeper's two-line anchor tolerance. Point each entry at its current
line; the anchors, provider sets, and fingerprints are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016puVootfTssyPQe2TzmK1M
@KSEGIT
KSEGIT force-pushed the copilot-ai-credits branch from 2d25ce6 to 31281db Compare August 15, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

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