Repository navigation
Description
Summary
The Claude quota transform (packages/ai/src/quota/providers/claude.ts, toClaudeUsage) silently overwrites duplicate limits entries — last wins. addWindow assigns target[key] = ... unconditionally (claude.ts:152–164), and scoped model windows are stored by unconditional assignment models[modelName] = ... (claude.ts:185). That diverges from the keep-first convention the suite has now adopted three times for the identical shape: #1750 (opendesign coding-plan windows), #1769 (cline-pass limit entries, fixed in PR #1773), and #1762's collision handling for codex. Claude's transform was pinned in #1750 for the legacy-fallback behavior, but no test exercises duplicate limits entries.
Evidence (verified this run, main 32fc03b)
Probe on the laptop (~/git/fleet/probe-2026-11-05-claude-quota/probe.ts, kept) importing toClaudeUsage:
- CASE1 — limits
[session 20% (reset 2026-12-01), session 80% (reset 2026-12-02), weekly_all 30%, weekly_all 70%]: published5h= 80% with the second reset,7d= 70% — both first entries silently dropped. - CASE2 — two
weekly_scopedentries for the same model ("Claude Sonnet", 30% then 60%): published model window = 60% with the second reset; the 30% entry is gone. - CASE3 (control) — a duplicate
sessionentry withpercent: nulldoes not overwrite (20% survives), becauseaddWindowreturns early when percent is null and there is no valueLabel. The overwrite therefore happens exactly when the duplicate carries a real figure — the case that matters.
Why it matters
Claude is a plan pool the fork rotates across. If Anthropic's usage payload ever carries repeated limit entries (per-model splits, plan migrations, a kind repeated during a rollout), the published usage silently becomes whichever entry sorts last, with nothing recording that a choice was made — the exact failure #1750/#1769 treated as a defect in the sibling providers. The transform is otherwise well-pinned after #1750; this is the one input shape left unpinned and convention-divergent.
Acceptance criteria
- Duplicate
limitsentries keep the first entry per kind (and per model forweekly_scoped), per the test(ai): quota suite pins none of claude / meta-billing / opendesign's pure parsers — billing banner rejects numeric strings, claude transform drops legacy windows when limits is non-empty, coding-plan publishes a zero-limit window #1750 convention — or a different disposition is chosen and documented in the code + this issue's closing note. The legacy-fallback precedence from test(ai): quota suite pins none of claude / meta-billing / opendesign's pure parsers — billing banner rejects numeric strings, claude transform drops legacy windows when limits is non-empty, coding-plan publishes a zero-limit window #1750 (limits-produced windows win overfive_hour/seven_day) is unchanged. -
packages/ai/test/quota.test.tspins: duplicatesession, duplicateweekly_all, duplicateweekly_scopedfor one model, two different models' scoped entries both surviving, and the null-percent duplicate control (first entry survives). - Existing quota suite stays green; gate state not regressed (the three quota-era gates already red on main belong to fix(ci): three gates red on main — PR #1722's quota subcommand landed 16 undispositioned sites, and turso bypasses the PR's own bounded reader #1724 — do not conflate).
Activity
dezren39 commentedon Nov 5, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 6, 2026
🤖 Agent note · adopted as phase 2 of design #1794 (one window contract for the provider quota parsers; doc:
files/designs/opencode-quota-window-contract.md, keep-the-four-repos-healthy goal workspace). This issue is the contract's claude application: rule 2 (duplicates keep the first — the #1750/#1769 convention — per kind and per model scope). Linked as a sub-issue of #1794; its queue lifecycle is untouched and it is not re-enqueued. When it lands, design phase 1's inventory row for claude duplicates flips from known-lossy to conforming.dezren39 commentedon Nov 8, 2026
Partial verification — staying open (2026-11-07 done-task-only audit, tracking: developing-today/code#7016).
Queue task(s) [10788] were marked done, and some work did land:
No merged change closes this issue's full scope, so the done status is not treated as completion. Remainder re-queued as triage task 11507.
dezren39 commentedon Nov 8, 2026
🤖 Agent note · picked up
developing-today commentedon Nov 8, 2026
🤖 Agent note · triaged
Merge pull request #2043 from developing-today-forks/queue/12828-1781…