Skip to content

fix(usage): only publish settled /usage reads — kill phantom quota alerts#139

Merged
Time4Mind merged 1 commit into
mainfrom
fix/usage-stale-read
Jun 20, 2026
Merged

fix(usage): only publish settled /usage reads — kill phantom quota alerts#139
Time4Mind merged 1 commit into
mainfrom
fix/usage-stale-read

Conversation

@Time4Mind

Copy link
Copy Markdown
Owner

Problem

@Alexes_claude_bot pushed three quota alerts in one minute — 🟡 week: 68%, 🟡 week: 68%, 🟠 week: 78% — while the live weekly usage was 2% (confirmed via /usage: session 7%, week 2%, week-Sonnet 0%).

Proof the 78% was never real: the week resets Jun 21; usage only climbs within a week, so it can't read 78% at 14:31 and 2% an hour later. The 78% was a stale pre-reset value (last week's high), surfaced by the usage poller.

Root cause

_poll_usage_modal waits for two consecutive agreeing captures (resolved), but the fallback returned an unsettled frame anyway:

return info if (resolved or info is not None) else None

And _capture_with_scrollback reads ~100 lines of scrollback, which can still contain a previous /usage render. After the weekly reset, the parser walks that stale render and extract_usage_breakdown can surface last week's high pct. quota_alerts (process-local baseline, reset by the bot's frequent restarts) then reads 0 → 78 and fires phantom 50%/75% crossings. Network flaps (the same instability behind the bot's restarts) bunched/duplicated the delivery into one minute.

Fix

  • Don't publish unsettled reads_poll_usage_modal returns None unless two consecutive captures agree. A missed poll is harmless; the loop retries on the next 10-min cadence.
  • Drop stale scrollback before reading — new _clear_pane_history clears the pane buffer, and an Escape dismisses any leftover modal/feedback-prompt, so a pre-reset render can't be parsed as the current week.

Tests

tests/ccbot/bot/test_usage_window.py:

  • a settled read (two agreeing captures) is published;
  • an oscillating, never-settling read returns None instead of the last frame (the old bug).

Full suite green (742 passed), pyright (strict, src/ccbot) + ruff clean.

🤖 Generated with Claude Code

…erts

The quota-alerts loop pushed "week: 78%" while the live week was 2%. Root
cause is in _poll_usage_modal: it returned even an UNSETTLED read
(`return info if (resolved or info is not None)`), and _capture_with_scrollback
reads ~100 lines back — which can still hold a PREVIOUS /usage render. After
the weekly reset that stale render shows last week's high pct; the parser
surfaces it and quota_alerts treats it as a fresh 50%/75% crossing.

Two changes:
- _poll_usage_modal returns None unless two consecutive captures agree
  (drop the stale-frame fallback). A missed poll is harmless; the loop
  retries on the next cadence.
- clear the pane scrollback (new _clear_pane_history) and Escape any
  leftover modal before /usage, so a pre-reset render can't be parsed as
  the current week.

Tests: a settled read is published; an oscillating (never-settling) read
returns None instead of the last frame.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Time4Mind Time4Mind merged commit 71db233 into main Jun 20, 2026
4 checks passed
@Time4Mind Time4Mind deleted the fix/usage-stale-read branch June 20, 2026 11:54
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