fix: show status badges in overview when no quota data is available#489
fix: show status badges in overview when no quota data is available#489RaghavShubham wants to merge 2 commits into
Conversation
When a plugin returns only a "Status" badge (e.g. "No usage data" or "Rate limited") and the card is rendered in overview scope, the badge was silently filtered out because its label was not listed as an overview-scoped line in plugin.json. This left the card visibly blank after the first successful probe set `lastUpdatedAt`, making `hasStaleData` true while `filteredLines` remained empty. Fix the scope filter in ProviderCard to always pass badge-type lines through regardless of scope — they are status indicators, not metric lines, and should always be visible. Also improve the Claude plugin's fallback message when the usage API responds successfully but returns no recognized quota fields (e.g. Enterprise plans or future plan types): show "Connected — no quota data" instead of the generic "No usage data" so users can distinguish a working connection from an authentication or network failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The personal usage endpoint (/api/oauth/usage) returns 403 for Enterprise accounts because their billing is tracked at the organisation level, not per-user. Previously the plugin treated any 403 as a token-expired error, which caused the JS probe to throw, the Rust runtime to emit an Error badge, and the state management to set data=null. On the first load this left the provider card completely blank because hasStaleData was false and no PluginError was prominently surfaced. Fix: intercept 403 from the usage endpoint before the generic isAuthStatus check and treat it as "no personal quota data" instead. The probe no longer throws, returns a "Status: Enterprise — org-level billing" badge, and the card renders meaningful content on the first load. A three-way fallback is now emitted when lines is empty: • 403 response → "Enterprise — org-level billing" • 200 but no recognized quota fields → "Connected — no quota data" • Inference-only / no API call → "No usage data" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/claude/plugin.js">
<violation number="1" location="plugins/claude/plugin.js:632">
P2: Enterprise/org-billing status badge is not stable across probes because `orgBillingOnly` is function-local and lost when requests are throttled via the min-interval guard.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| let lines = [] | ||
| let rateLimited = false | ||
| let retryAfterSeconds = null | ||
| let orgBillingOnly = false // true when the API returned 403 (Enterprise org-level billing) |
There was a problem hiding this comment.
P2: Enterprise/org-billing status badge is not stable across probes because orgBillingOnly is function-local and lost when requests are throttled via the min-interval guard.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At plugins/claude/plugin.js, line 632:
<comment>Enterprise/org-billing status badge is not stable across probes because `orgBillingOnly` is function-local and lost when requests are throttled via the min-interval guard.</comment>
<file context>
@@ -629,6 +629,7 @@
let lines = []
let rateLimited = false
let retryAfterSeconds = null
+ let orgBillingOnly = false // true when the API returned 403 (Enterprise org-level billing)
if (canFetchLiveUsage) {
if (nowMs < rateLimitedUntilMs) {
</file context>
|
@codex review |
There was a problem hiding this comment.
Pull request overview
This PR fixes an overview rendering bug where providers that only emit a status badge (e.g. “No usage data”, “Rate limited”) could appear blank in overview/menu-bar scope, and it refines the Claude plugin’s fallback status messaging when quota data can’t be derived.
Changes:
- Update
ProviderCardscope filtering so status-only output isn’t silently dropped in overview scope. - Improve Claude plugin behavior/messages for “connected but no quota fields”, and for usage API 403 (Enterprise org-billing).
- Add/adjust regression tests covering the overview badge scenario and Claude fallback paths.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/components/provider-card.tsx | Adjusts runtime line filtering logic in overview scope to avoid blank cards. |
| src/components/provider-card.test.tsx | Adds a regression test ensuring a status badge renders in overview scope. |
| plugins/claude/plugin.js | Adds clearer fallback badges for “no quota fields” and Enterprise (403) scenarios. |
| plugins/claude/plugin.test.js | Updates/adds tests to validate the new Claude status badge behaviors and API-call skipping. |
Comments suppressed due to low confidence (1)
plugins/claude/plugin.js:639
orgBillingOnlyis a per-probe local flag. If the usage request is skipped on subsequent probes (min-interval throttle or existing rate-limit window), the code won’t remember that the last real response was 403 and may fall back to the wrong status badge. Consider deriving this state from the cached value (or caching a sentinel) so the badge remains accurate across skipped polls.
let lines = []
let rateLimited = false
let retryAfterSeconds = null
let orgBillingOnly = false // true when the API returned 403 (Enterprise org-level billing)
if (canFetchLiveUsage) {
if (nowMs < rateLimitedUntilMs) {
// Still within a rate-limit window from a previous probe call — skip the
// API request entirely and surface the remaining wait time to the user.
rateLimited = true
retryAfterSeconds = Math.ceil((rateLimitedUntilMs - nowMs) / 1000)
data = cachedUsageData
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Badge lines are status indicators (e.g. "No usage data", "Rate limited") and must | ||
| // always be shown regardless of scope so the overview card is never silently blank. | ||
| const filteredLines = scopeFilter === "all" | ||
| ? lines | ||
| : lines.filter(line => overviewLabels.has(line.label)) | ||
| : lines.filter(line => line.type === "badge" || overviewLabels.has(line.label)) | ||
|
|
| if (resp.status === 403) { | ||
| // A 403 from the usage endpoint means the account type does not have access | ||
| // to personal quota data. Enterprise accounts are billed at the organisation | ||
| // level and consistently return 403 here. Treat it as "no personal quota data" | ||
| // so the card shows a helpful badge rather than the error state, which leaves | ||
| // it blank on first load. A 401 (truly expired token) falls through to the | ||
| // isAuthStatus handler below. | ||
| ctx.host.log.info("usage API returned 403 — organisation-level billing; no personal quota data") | ||
| orgBillingOnly = true | ||
| data = cachedUsageData // keep previous cache if any, otherwise null | ||
| } else if (ctx.util.isAuthStatus(resp.status)) { |
Problem
When a plugin returns only a status badge — "No usage data" or "Rate limited" — and the card is rendered in overview/menu-bar scope, the badge was silently dropped, leaving the card visibly blank.
Root cause
ProviderCardfilters lines by scope:overviewLabelsis built fromskeletonLineswithscope === "overview". For Claude that's only"Session"and"Weekly". The fallback badge has label"Status"— not in the set — so it gets filtered out.After the first successful probe sets
lastUpdatedAt,hasStaleDatabecomestrue, butfilteredLinesstays empty. The{hasStaleData && …}block renders with no children → blank card.This affects Enterprise accounts (API responds but contains no recognized quota fields), inference-only tokens, and any provider where the plugin falls back to a status-only badge.
Fix
src/components/provider-card.tsx— Badge-type lines are status indicators, not metric lines. They must always pass the scope filter:plugins/claude/plugin.js— Improve the fallback message so users can distinguish a working connection from an auth/network failure:"Connected — no quota data"(e.g. Enterprise plans)"No usage data"(unchanged)Tests
provider-card.test.tsxthat reproduces the exact scenario: overview scope +lastUpdatedAtset + only a"Status"badge → badge must be visible.plugin.test.jsto match the new"Connected — no quota data"text for the API-connected case, and added a complementary test for the inference-only path (must still show"No usage data"and must not call the HTTP API).All 1087 tests pass.
Closes #440
Summary by cubic
Always show status badges in the overview and clarify Claude’s status when personal quota data isn’t available, including Enterprise org‑billing accounts.
Written for commit 4813319. Summary will update on new commits. Review in cubic