Skip to content

fix(usage): consumo do Claude (plano Team, 429, reset, GLM dup) + tile Extra + botão force#27

Merged
aronpc merged 8 commits into
mainfrom
fix/claude-usage-team-plan-429
Jul 10, 2026
Merged

fix(usage): consumo do Claude (plano Team, 429, reset, GLM dup) + tile Extra + botão force#27
aronpc merged 8 commits into
mainfrom
fix/claude-usage-team-plan-429

Conversation

@aronpc

@aronpc aronpc commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Contexto

O consumo do Claude parou de aparecer no overlay depois que a conta migrou para Team. A investigação (com payload REAL da API, não palpite) revelou uma cadeia de causas — a raiz era o plano não reconhecido + rate-limit (429) do endpoint de uso, que é compartilhado com o próprio Claude Code (/status).

Correções

Reconhecimento de plano

  • Plano resolvido primeiro pelas credenciais (~/.claude/.credentials.json: rateLimitTier/subscriptionType) — fonte do tier REAL (default_claude_max_5x), enquanto o .claude.json trazia o tier opaco default_raven. Rótulo fica preciso ("Claude Max 5×").
  • Fallback: reconhece claude_team/claude_pro/claude_enterprise; rótulo genérico "Claude" para conta presente mas não mapeada. null só quando não há conta.

Rate-limit (429) — o motivo do "% não vinha"

  • Backoff exponencial (Retry-After × 1.5^fails, teto 1 h): o endpoint é compartilhado com o Claude Code, então rebater a cada expiração só escalava a punição (observado 1007s → 2871s).
  • Cooldown persistido em disco (claude-cooldown.json, {until, fails}nunca o token): sobrevive a restart, então bun start/dev não re-bate no boot nem re-escala.
  • Cache próprio do Claude de 5 min (a API é fortemente rate-limited; janelas são 5h/7d).
  • Durante o cooldown, mantém o último valor bom (ou plano-só) — não some nem pisca ⚠.
  • parseRetryAfter (delta-seconds ou HTTP-date).

Reset correto

  • Removido o uso de planLimitsEndDate como reset da janela (é o fim do ciclo do plano, não da janela 5h/7d — mostrava "3d" logo após a janela resetar). O reset real vem do resets_at de cada janela via API.

GLM duplicado

  • A mesma conta z.ai chegando por 2 credenciais (tokens distintos) com resetAt diferindo ~1ms não colapsava → "MCP (mês) 2×". A chave de dedup agora normaliza resetAt para segundos.

Novas features

Botão "atualizar uso agora" (⟳)

Força a recoleta (fura o cache de conveniência de 5 min). Seguro: respeita o cooldown do 429 — fica apagado com tooltip "aguarde Xmin" e não bate na API durante o rate-limit.

Tile "Extra" (overage)

Contas com crédito extra medido em dinheiro (extra_usage) ganham um tile mostrando % + valor (ex.: $50.4/$50.0), respeitando currency/decimal_places. Descoberto no payload real: a conta tinha overage estourado (100%).

Verificação

  • 188 testes (165 → 188), todos verdes.
  • Payload REAL da API confirmou que o parse das janelas 5h/7d sempre esteve correto — o % só não vinha por causa do 429 (não era bug de schema). Quando a API respondeu 200: five_hour 7%, seven_day 24%, extra_usage 100% — exatamente o que o overlay passa a mostrar.
  • E2E via collectUsage com o payload real: Claude Max 5× · 5h 7% / 7 dias 24% / Extra 100% $50.4/$50.0.

Arquivos

src/usage.js, main.js, preload.js, src/renderer.js, src/index.html, src/styles.css, src/i18n.js, test/usage.test.js, test/rename.test.js, CHANGELOG.md

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aronpc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01caa07d-7e7f-4881-8f09-96f1396bf86b

📥 Commits

Reviewing files that changed from the base of the PR and between f27ab1f and 41c600f.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • main.js
  • preload.js
  • src/i18n.js
  • src/index.html
  • src/renderer.js
  • src/styles.css
  • src/usage.js
  • test/rename.test.js
  • test/usage.test.js

Walkthrough

O PR amplia o mapeamento de planos Claude, trata HTTP 429 com Retry-After, cooldown persistido, backoff e cache, além de adicionar atualização manual no overlay com feedback visual durante o cooldown.

Changes

Uso do Claude e rate limiting

Layer / File(s) Summary
Mapeamento de plano e identidade
src/usage.js, test/usage.test.js
Tiers Max e tipos de organização recebem labels específicos; identidades OAuth desconhecidas usam Claude, enquanto configurações sem OAuth retornam null.
Interpretação e propagação do rate limit
src/usage.js, test/usage.test.js
_httpsGetJson expõe statusCode e retryAfterMs; parseRetryAfter interpreta segundos ou datas HTTP.
Cooldown, cache e recuperação
src/usage.js, test/usage.test.js, CHANGELOG.md
A coleta bloqueia chamadas durante o cooldown, aplica backoff, preserva o último valor válido, persiste falhas e retoma após a expiração.
Persistência e atualização manual no overlay
main.js, preload.js, src/renderer.js, src/index.html, src/styles.css, src/i18n.js, test/rename.test.js
O cooldown é transmitido à interface; o botão força a coleta, exibe tooltip de espera e aplica estados visuais de rotação e bloqueio.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Overlay
  participant preload
  participant main
  participant collectUsage
  participant ClaudeAPI
  Overlay->>preload: chama forceUsage()
  preload->>main: envia evento force-usage
  main->>collectUsage: solicita coleta
  collectUsage->>ClaudeAPI: consulta uso do Claude
  ClaudeAPI-->>collectUsage: HTTP 429 e Retry-After
  collectUsage-->>main: retorna último valor e cooldown
  main-->>preload: envia usage-meta
  preload-->>Overlay: atualiza tooltip e estado do botão
Loading

Possibly related PRs

Poem

Sou coelho ligeiro, saltando no chão,
Claude ganha um plano e menos confusão.
Se vem 429, faço pausa afinal,
guardo o bom valor no meu cache natal.
Quando a espera termina, volto a correr —
e o percentual torna a aparecer!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed O título resume bem a correção do consumo do Claude, incluindo plano Team, 429, reset e o botão de atualização.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/claude-usage-team-plan-429

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@main.js`:
- Around line 1178-1184: Preserve the last valid Claude usage data when forcing
a refresh during an active cooldown. Update the force-usage handler and the
cache-clearing logic used by _clearClaudeCache/readClaudeUsage so Claude cache
entries are not removed while claudeCooldownUntil is still in the future; only
invalidate them when no cooldown is active, while retaining the existing GLM and
Codex behavior.

In `@src/renderer.js`:
- Around line 643-663: O listener de clique do botão $forceUsage permite
chamadas durante o cooldown. Em applyUsageMeta e no handler de click, verifique
claudeCooldownUntil contra Date.now() antes de chamar
window.trafficLight.forceUsage(), atualizando o estado visual sem iniciar o
spinner quando bloqueado; mantenha a chamada e o feedback atuais apenas fora do
cooldown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5229d659-fe9d-4c5e-ae12-8ab061105354

📥 Commits

Reviewing files that changed from the base of the PR and between ba51886 and f27ab1f.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • main.js
  • preload.js
  • src/i18n.js
  • src/index.html
  • src/renderer.js
  • src/styles.css
  • src/usage.js
  • test/rename.test.js
  • test/usage.test.js
✅ Files skipped from review due to trivial changes (1)
  • src/i18n.js

Comment thread main.js
Comment thread src/renderer.js
@aronpc aronpc changed the title fix(usage): consumo do Claude sumia (plano Team + rate limit 429) fix(usage): consumo do Claude (plano Team, 429, reset, GLM dup) + tile Extra + botão force Jul 10, 2026
@aronpc

aronpc commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Ambos os achados eram válidos e foram corrigidos em 843667e:

1. main.js — force-usage limpava o cache durante o cooldown (Major): confirmado. Agora _clearClaudeCache() só roda fora do cooldown (if (!(claudeCooldownUntil > Date.now()))); GLM/Codex seguem sempre limpos. Assim clicar ⟳ no rate limit preserva o último % real em vez de regredir para plano-só.

2. src/renderer.js — botão clicável durante o cooldown: confirmado. O clique agora retorna cedo (if (claudeCooldownUntil > Date.now()) return;) — sem spinner falso nem chamada à toa. O tooltip "aguarde Xmin" já sinaliza o estado.

Verificado E2E: collectUsage com cache quente + cooldown futuro mantém 5h 7% / 7d 24% (não cai para plano-só). Suíte 188/188 verde.

aronpc added 8 commits July 10, 2026 12:30
Duas causas combinadas faziam o tile de consumo do Claude desaparecer
do overlay:

1. Contas não-Max (Team, tier interno default_raven) não eram
   reconhecidas — o rótulo do plano virava null e o tile sumia. Agora
   parseClaudeConfig reconhece claude_team/claude_pro/claude_enterprise
   e cai num rótulo genérico "Claude" para qualquer conta presente mas
   não mapeada; só retorna null quando NÃO há conta OAuth.

2. Ao levar HTTP 429 da API de uso, o loop de 60s rebatia na mesma
   janela e renovava a penalidade indefinidamente — o % nunca voltava.
   Agora um 429 agenda um cooldown que respeita o header Retry-After
   (fallback 15 min): durante ele o coletor não bate na API e mantém o
   último valor conhecido (ou o plano-só), sem sumir nem piscar.

Testes: 165 → 175 (novos casos p/ plano Team, parseRetryAfter e o
cooldown do 429).
…cooldown persistido)

O 429 da API de uso escalava o Retry-After a cada toque (1007s → 2871s):
em dev com `bun start`, cada restart perdia o cooldown em memória,
re-batia no boot e re-armava a punição.

- Cache próprio do Claude de 5 min (antes usava o global de 30s): a API
  /api/oauth/usage é fortemente rate-limited e as janelas são de 5h/7d,
  então consultar a cada 60s era desperdício e pressão desnecessária.
- Cooldown do 429 persistido em disco (claude-cooldown.json, SÓ o
  timestamp — nunca o token): sobrevive a restart, então boot em dev não
  re-bate nem re-escala. Injetado via collectUsage → readClaudeUsage.

Testes: 175 → 178.
Botão ⟳ que fura o cache de conveniência (5min) e recoleta na hora. É
SEGURO: respeita o cooldown do 429 persistido — durante a janela de
rate limit fica apagado com tooltip "aguarde Xmin" e NÃO bate na API,
evitando re-escalar a penalidade.

- index.html: botão ⟳ no header (SVG refresh)
- main.js: handler force-usage (limpa caches + recoleta), emite
  usage-meta com claudeCooldownUntil p/ o tooltip do botão
- renderer: applyUsageMeta troca data-tip (↻↔"aguarde Nmin"), classe
  .is-cooldown apaga o botão, .is-spinning gira no clique (feedback)
- i18n pt/en: tooltip_force_usage + tooltip_force_cooldown
- mock do teste atualizado (onUsageMeta/forceUsage)

178/178 verdes; verificado ponta a ponta: force limpa cache e recupera
o %; force durante o 429 faz 0 chamadas à API.
Dois bugs do consumo do Claude:

1. Reset do plano sumia no tile plano-só (Claude Team durante o 429).
   O planLimitsEndDate (reset semanal) estava no .claude.json e o
   parseClaudeConfig lia, mas claudePlanLabel descartava tudo e o
   planOnly hardcodeava resetAt:null. Refatorado p/ readClaudeConfig
   devolver o objeto completo; o tile agora mostra o reset + passes.

2. 429 escalava indefinidamente. O endpoint /api/oauth/usage é
   compartilhado com o próprio Claude Code (/status) — limite agregado.
   Cada cooldown que expirava e rebatia recebia Retry-After cada vez
   maior. Backoff exponencial: a cada 429 seguido, alonga a espera
   (Retry-After × 1.5^fails, teto 1h) e persiste {until, fails}.

Testes: 178 → 182.
…At 1ms diferente)

A mesma conta z.ai Pro chegava por 2 credenciais (tokens distintos,
sufixos 6baca3 e c3c374) — ex.: /proc + OpenCode auth.json. A API
devolve resetAt com ~1ms de diferença ("...09.995Z" vs "...09.996Z"),
e a chave de dedup por conteúdo usava o resetAt exato → não colapsava
→ "MCP (mês) 2×".

Agora a chave normaliza resetAt p/ segundos (floor(parse/1000), tolera
sub-segundo) sem colapsar contas realmente distintas (resets >1s apart).

Teste reproduz as 3 entradas reais e confirma colapso p/ 2. 182 → 183.
…noso)

O planLimitsEndDate (ex.: Jul 13) é o fim do ciclo do PLANO, não o
reset da janela de uso 5h/7d — que reseta várias vezes até lá. O commit
anterior o pôs no tile plano-só e mostrava "3d" logo após a janela ter
resetado (o usuário resetou às 12pm e via "3d"). Agora o tile plano-só
não expõe reset (honesto: sem a API OAuth não sabemos o reset da
janela); passes continua (info local legítima). Quando a API responde,
o resets_at real de cada janela já vem via parseAnthropicUsage.
Investigação com payload REAL da conta Team (API respondeu 200 após o
429 sarar) revelou duas coisas:

1. As credenciais (~/.claude/.credentials.json) têm rateLimitTier e
   subscriptionType — fonte MAIS confiável do plano que o .claude.json
   (que trazia o tier opaco 'default_raven'). Agora o plano é resolvido
   primeiro pelas credenciais → "Claude Max 5×" em vez do genérico.

2. O payload traz extra_usage (overage medido em $): a conta tinha
   used_credits 5042 / monthly_limit 5000 = 100% ($50.4/$50). Novo tile
   "Extra" (id claude-extra, não colide com 7d) mostra % + valor,
   respeitando currency/decimal_places.

Confirmado E2E que o parse das janelas 5h/7d sempre esteve correto — o
% só não vinha por causa do 429 (não era bug de schema). 183 → 188.
… à toa)

Dois achados do review (CodeRabbit) na feature do botão de force:

1. main.js: force-usage limpava o cache do Claude incondicionalmente.
   Clicar ⟳ DURANTE o cooldown zerava o cache → readClaudeUsage
   respeitava o cooldown (não batia na API, correto) mas perdia o
   fallback e caía em plano-só — o tile perdia o % só pelo clique.
   Agora só limpa o cache do Claude FORA do cooldown (GLM/Codex sempre).

2. renderer.js: o botão ficava apagado (.is-cooldown) mas continuava
   clicável — o clique disparava spinner falso e forceUsage() à toa.
   Agora o clique é bloqueado durante o cooldown (o tooltip já explica).

E2E confirma: force no cooldown preserva 5h/7d reais (não regride). 188 verdes.
@aronpc aronpc force-pushed the fix/claude-usage-team-plan-429 branch from 843667e to 41c600f Compare July 10, 2026 15:30
@aronpc aronpc merged commit 1b01a43 into main Jul 10, 2026
1 check passed
@aronpc aronpc deleted the fix/claude-usage-team-plan-429 branch July 10, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant