Skip to content

feat(claude): show the Fable weekly limit and fix /usage parsing for tall screens#217

Merged
hanrw merged 1 commit into
tddworks:mainfrom
billyjack2:feat/claude-fable-limit
Jul 2, 2026
Merged

feat(claude): show the Fable weekly limit and fix /usage parsing for tall screens#217
hanrw merged 1 commit into
tddworks:mainfrom
billyjack2:feat/claude-fable-limit

Conversation

@billyjack2

@billyjack2 billyjack2 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

The Claude CLI and OAuth usage API now report a Fable 5 weekly limit that ClaudeBar ignored. This PR parses it from both probes so it appears as a quota card in the window and as a selectable menu-bar metric. It also fixes a bug where the CLI probe failed on every tick with recent Claude CLI versions.

Fable weekly limit

  • CLI probe: parses the Current week (Fable) section (paren-open anchor so a future (Fable 5) label still matches), with its own reset time and fallback to the all-models weekly reset.
  • API probe: the OAuth usage endpoint now reports model limits via a generic limits array (kind: "weekly_scoped" + scope.model.display_name) instead of the legacy seven_day_<model> fields, which are now null (verified against the live endpoint). Parsing is generic — any future model-scoped limit appears automatically — keyed on the first word of the display name to stay in sync with the CLI probe's key, deduped against legacy fields, and preserving negative percentRemaining as the over-quota signal per the UsageQuota contract.
  • No UI changes needed: everything downstream (QuotaType.modelSpecific, quota cards, the menu-bar quota picker, quota keys like model:fable) is already generic.

Parse-failure fix (blocks Fable in CLI mode)

Recent Claude CLI versions render a /usage screen ~81 lines tall (new usage-contribution report). On the probe's 50-row PTY the quota sections scroll into scrollback, which TerminalRenderer.extractScreenText never read — so parsing failed with "could not find 'Current session'" on every tick. The renderer now reads the whole buffer (visible rows + scrollback, capacity raised to 2000 lines).

Verification

  • 8 new tests (CLI fixtures incl. tall-screen and fallback-reset cases, API limits array incl. dedupe/malformed/over-quota cases, QuotaType round-trip); full Domain + Infrastructure suites pass.
  • Verified against a real /usage PTY capture (160×50 raw bytes) and a live app run: probe succeeds with quotas=3 — Session 35% / Weekly 82% / Fable 67% remaining — matching the CLI exactly.

Follow-up (not in this PR)

  • Label scanners take the first occurrence of a section label in the buffer; with in-place TUI repaints a stale intermediate frame could in theory win. Benign today (repainted frames carry identical values); switching to last-occurrence with a two-frame fixture is a good follow-up.

Summary by CodeRabbit

  • New Features

    • Added support for showing the Fable weekly limit as a selectable quota metric/card.
    • Expanded usage detection so newer Claude usage responses are parsed correctly, including model-specific limits.
  • Bug Fixes

    • Fixed usage parsing when CLI output is taller than the visible terminal area, so quota sections are no longer missed.
    • Improved fallback handling for Fable reset times when a dedicated reset value isn’t available.

…tall screens

The Claude CLI and OAuth usage API now report a Fable 5 weekly limit that
ClaudeBar ignored. Parse it from both probes so it appears as a quota card
in the window and as a selectable menu-bar metric (everything downstream of
parsing is already generic over QuotaType.modelSpecific):

- CLI probe: parse the "Current week (Fable)" section (paren-open anchor so a
  future "(Fable 5)" label still matches), with its own reset time falling
  back to the all-models weekly reset.
- API probe: the endpoint reports model limits via a generic `limits` array
  (kind "weekly_scoped" + scope.model.display_name) instead of the legacy
  seven_day_<model> fields, which are now null. Parse it generically — any
  future model-scoped limit appears automatically — keyed on the first word
  of the display name to stay in sync with the CLI probe's key, deduped
  against legacy fields, and without clamping over-quota negative remaining.

Also fix the CLI probe failing on every tick with recent CLI versions: the
/usage screen grew past the probe's 50-row terminal (usage-contribution
report), scrolling the quota sections into scrollback, which the terminal
renderer never read. Render the whole buffer (visible rows + scrollback)
instead of just the visible screen.

Verified against a real /usage PTY capture and a live probe: session,
weekly, and Fable quotas all parse (quotas=3, "Fable: 67% remaining").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds parsing of a Claude Fable 5 weekly quota from both the CLI /usage output and the OAuth API's limits array, extends TerminalRenderer to capture scrollback so quota sections rendered off-screen are still parsed, and adds corresponding tests and CHANGELOG entries.

Changes

Fable weekly quota parsing and scrollback support

Layer / File(s) Summary
API usage probe: limits array parsing
Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift, Tests/InfrastructureTests/Claude/ClaudeAPIUsageProbeTests.swift
UsageResponse gains a limits field and LimitEntry/LimitScope/LimitScopeModel decoding types; parseUsageResponse derives model-specific quotas from weekly_scoped limit entries, avoiding duplicates with legacy fields, with tests for normal, malformed, and duplicate scenarios.
CLI usage probe: Fable label extraction and scrollback
Sources/Infrastructure/Claude/ClaudeUsageProbe.swift, Sources/Infrastructure/Shared/TerminalRenderer.swift, Tests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift
parseClaudeOutput extracts a fablePct from the "Current week (Fable" label and appends a modelSpecific("fable") quota with resolved reset time (falling back to the weekly reset); TerminalRenderer now captures scrollback lines beyond visible rows via getScrollInvariantLine, with tests covering both scrollback retention and off-screen quota parsing.
QuotaType fable key round-trip and CHANGELOG entries
Tests/DomainTests/Provider/QuotaTypeTests.swift, CHANGELOG.md
Test verifies displayName, shortLabel, quotaKey, and round-trip init for the fable QuotaType; CHANGELOG documents the new Fable quota parsing feature and the scrollback fix.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant API as OAuth Usage API
  participant CLI as Claude CLI /usage
  participant Renderer as TerminalRenderer
  participant Probe as Claude Usage Probes
  API->>Probe: usage JSON with limits array
  Probe->>Probe: filter weekly_scoped, derive model quota
  CLI->>Renderer: render output taller than visible rows
  Renderer->>Renderer: capture rows + scrollback lines
  Renderer-->>Probe: full text incl. off-screen quota sections
  Probe->>Probe: extract fablePct, resolve reset time
  Probe-->>Probe: append modelSpecific("fable") quota
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: Fable weekly limit support and the /usage parsing fix for tall terminal screens.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift (1)

474-505: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cross-file "fable" key derivation is duplicated and only comment-linked.

The model key here is derived by splitting/lowercasing displayName, while ClaudeUsageProbe.swift hardcodes the literal "fable" string for the CLI path. The only thing keeping them in sync is a comment (// Key on the first word... must stay in sync with the key the CLI probe hardcodes). If Anthropic changes the API's display_name format (e.g., to "Claude Fable 5"), this loop would silently derive "claude" instead of "fable", breaking the menu-bar persistence contract without any compiler or test signal outside the two now-passing but independently-maintained fixtures.

Consider extracting the key-derivation into a single shared helper (e.g. a QuotaType static function) that both probes call, so any future model-name format change is caught in one place.

🤖 Prompt for 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.

In `@Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift` around lines 474 -
505, The model key derivation for the Claude Fable quota is duplicated between
this loop and ClaudeUsageProbe.swift, relying only on a comment to stay in sync.
Extract the display-name-to-model-key logic into a shared helper or QuotaType
utility and have both the weekly_scoped parsing path and the CLI probe use it,
so the persisted model:<name> selection remains consistent if the API
displayName format changes.
🤖 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.

Nitpick comments:
In `@Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift`:
- Around line 474-505: The model key derivation for the Claude Fable quota is
duplicated between this loop and ClaudeUsageProbe.swift, relying only on a
comment to stay in sync. Extract the display-name-to-model-key logic into a
shared helper or QuotaType utility and have both the weekly_scoped parsing path
and the CLI probe use it, so the persisted model:<name> selection remains
consistent if the API displayName format changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57a2ef99-7ca3-4947-9aff-446a944bb0a5

📥 Commits

Reviewing files that changed from the base of the PR and between 93e57ae and 7b93470.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift
  • Sources/Infrastructure/Claude/ClaudeUsageProbe.swift
  • Sources/Infrastructure/Shared/TerminalRenderer.swift
  • Tests/DomainTests/Provider/QuotaTypeTests.swift
  • Tests/InfrastructureTests/Claude/ClaudeAPIUsageProbeTests.swift
  • Tests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.83%. Comparing base (ed3154f) to head (7b93470).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #217      +/-   ##
==========================================
+ Coverage   81.65%   81.83%   +0.17%     
==========================================
  Files         116      116              
  Lines        8713     8758      +45     
==========================================
+ Hits         7115     7167      +52     
+ Misses       1598     1591       -7     
Files with missing lines Coverage Δ
...es/Infrastructure/Claude/ClaudeAPIUsageProbe.swift 93.33% <100.00%> (+1.89%) ⬆️
...urces/Infrastructure/Claude/ClaudeUsageProbe.swift 83.14% <100.00%> (+0.41%) ⬆️
...urces/Infrastructure/Shared/TerminalRenderer.swift 94.59% <100.00%> (+6.02%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hanrw hanrw merged commit 0f76c2e into tddworks:main Jul 2, 2026
5 checks passed
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.

2 participants