5.4 KiB
5.4 KiB
Claude Scoped OAuth Usage Limits
Problem
Anthropic's current OAuth usage response reports Fable in limits as a model-scoped weekly limit instead of one of the legacy top-level Fable fields. Orca ignores limits, maps fableWeekly to null, and then depends on a hidden Claude /usage PTY read that is disabled on Windows and can fail silently elsewhere.
src/main/rate-limits/claude-fetcher.ts:300models only top-level OAuth windows.src/main/rate-limits/claude-fetcher.ts:393maps only legacy Fable field names.src/main/rate-limits/service.ts:1203disables the PTY supplement on Windows.src/renderer/src/components/status-bar/tooltip.tsx:172renders Fable wheneverfableWeeklyis populated.
Root Cause
The OAuth response contract evolved from dedicated model fields to generic entries shaped like kind: "weekly_scoped", percent, resets_at, and scope.model.display_name. Orca's response type and mapper were not updated for that shape.
Non-goals
- Do not change polling, credentials, token refresh, account switching, renderer layout, or usage percentage semantics.
- Do not remove the existing PTY supplement or legacy field compatibility.
- Do not generalize shared renderer state to arbitrary model windows in this targeted bug fix.
Design
- Extend the private OAuth response type with an optional
limitsarray containing only the fields needed for safe parsing. - Select a Fable entry only when
kindisweekly_scoped, the model display name is Fable (case-insensitive), andpercentis finite. - Map the scoped entry to the existing seven-day
fableWeeklywindow, including its reset timestamp. - Prefer the current scoped entry, then retain the three legacy top-level fields as fallbacks.
- Keep malformed, unrelated, or absent entries non-fatal. Do NOT gate on
is_active: it marks which limit is currently binding, not whether the entry's data is valid, so anis_active: falseFable entry with a finitepercentmust still render (#8979). Accept a missing activity flag for compatibility.
Data Flow
- OAuth response
limits[].weekly_scopedFable ->fableWeekly- otherwise legacy explicit Fable field ->
fableWeekly - otherwise existing optional PTY supplement
- Existing provider state -> existing status-bar and details rendering
Edge Cases
limitsis missing, null, malformed, or contains null entries.- A scoped entry names another model.
- Fable percent is missing, non-numeric, or non-finite.
- Fable is inactive (
is_active: false) but still carries a finitepercent/reset, so it must render (#8979). is_activeis omitted by an older server response but the remaining scoped entry is valid.- Both current and legacy fields exist; the current scoped entry wins.
- Reset timestamps may be ISO strings, epoch seconds, epoch milliseconds, or absent.
- Windows, WSL, SSH, and remote runtimes use the same OAuth mapper and require no platform-specific execution.
Test Plan
- Unit: reproduce a current real-response shape and assert Fable maps without a PTY attempt.
- Unit: assert scoped data wins over a legacy field.
- Unit: assert malformed and unrelated scoped entries are ignored while legacy fallback remains available; assert an inactive-but-valid Fable entry still surfaces (#8979).
- Regression: retain existing legacy-field and bare-
fablebehavior tests. - Verification: focused Claude fetcher tests, typecheck, lint, and max-lines ratchet.
- Electron: refresh Claude usage and confirm Session, Weekly, and Fable remain visible in the existing status-bar details surface.
UI Quality Bar
No UI implementation changes. The existing Fable row must reappear with the same typography, spacing, progress bar, percentage semantics, and reset copy as adjacent Session and Weekly rows.
Review Screenshots
- Claude usage details showing Session, Weekly, and Fable from a live OAuth refresh.
- Adjacent status-bar context showing the Claude provider remains visually unchanged outside the restored row.
Rollout
- Add the scoped OAuth response types and mapper.
- Add focused current-schema and compatibility regression tests.
- Run focused and repository checks.
- Validate the restored row in Electron and capture review screenshots.
- Commit, push, and open an unmerged PR.
Lightweight Eng Review
- Scope: Kept to the private OAuth mapper and tests; no shared-state or renderer generalization is required to restore Fable.
- Architecture/data flow: OAuth remains authoritative, with structured scoped data preferred over legacy fields and PTY used only as the existing final supplement.
- Failure modes covered: malformed optional data, unrelated models, inactive-but-valid limits (still rendered, #8979), missing activity flags, duplicate old/new representations, missing reset metadata, and platform-neutral execution.
- Test coverage required: current-schema success without PTY, precedence, inactive-but-valid rendering, malformed/unrelated entries, and legacy fallback.
- Performance/blast radius: One bounded linear scan of the small response
limitsarray per existing OAuth refresh; no new requests, polling, subprocesses, IPC, storage, or renderer work. - UI quality bar: Existing status-bar visuals must remain unchanged except for the restored Fable row.
- Required review screenshots: Live Claude details with all three rows; surrounding status-bar context.
- Residual risks: Anthropic may rename the scoped model display label; legacy and PTY fallbacks remain available.