Repository navigation
Description
Problem
OpenRouter quota publishes a window for a key whose limit is 0: usedPercent: null with the value label $0.00 / $0.00, and the result is ok: true. A zero limit is no quota figure at all — the window reads as a live (empty) quota where the key in fact has no limit/headroom surface. This is exactly the symptom the window contract and a sibling provider already dispositioned:
- Window contract (design(ai): parse the window you were sent, or none — one window contract for the provider quota parsers #1794,
packages/ai/src/quota/types.tsrule 4): "No figure, no window — an empty/all-null/zero-limit window-shaped object publishes no window; only quota figures are windows." - OpenDesign precedent (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, comment in
packages/ai/src/quota/providers/opendesign.tsparseCodingPlanWindows): a zero limit "used to publish a label-only window (usedPercent null, '0 / 0 credits') that reads as quota headroom where there is no plan" — and now publishes nothing.
OpenRouter was not given the same disposition. The #1795 inventory lists OpenRouter's only known-lossy item as #1725 (unpinned resolveResetAt); the zero-limit case is not covered there.
Evidence (verified this run, main 5fd613e46d)
packages/ai/src/quota/providers/openrouter.ts:
- :105-106
limit/limitRemainingparsed viatoNumber. - :130-133 in the limited branch:
used = Math.max(0, limit - limitRemaining),percent = limit > 0 ? (used / limit) * 100 : null— forlimit === 0the percent becomesnullbut the branch still builds a window. - :149-152 the window is published unconditionally under the reset-derived key.
Laptop probe (worktree of origin/main 5fd613e46d, driving provider.fetchQuota with a stubbed fetch, probe kept at ~/git/fleet/probe-2026-11-05-creator-2135/probe2.ts on the laptop):
{limit: 0, limit_remaining: 0, usage_monthly: 0, limit_reset: "monthly"}→ok: true, windowmonthly: { usedPercent: null, remainingPercent: null, valueLabel: "$0.00 / $0.00", windowSeconds: 2592000, resetAt: <next month> }{limit: 0, limit_remaining: -5, usage_monthly: 5, limit_reset: "monthly"}→ok: true, windowmonthly: { usedPercent: null, valueLabel: "$5.00 / $0.00" }— spent money against a zero limit, still no percent and no error.- Controls behave:
{limit: 100, limit_remaining: -20}→usedPercent: 100, label$120.00 / $100.00;{limit: 100, limit_remaining: 150}→usedPercent: 0, label$0.00 / $100.00.
Why it matters
The opencode quota listing and downstream consumers (aggregator/dashboard) treat an ok: true window as a real quota surface. A zero-limit key (revoked, unpaid, or a free key with no purchased limit) currently shows a plausible monthly window with a reset date instead of the no-data/error row its siblings produce, masking exactly the state a user checks quota to discover.
Acceptance criteria
- A payload with
limit: 0publishes no limit window: if it is the only data, the result follows the sibling no-data contract (ok: false, "No quota data in response"), matching the OpenDesign zero-limit disposition (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) and contract rule 4. - The unlimited path (
limit: nullwithusage_monthlypresent) still publishes its monthly spent window unchanged. - Normal and over-limit cases stay as probed above (100 / 150-remaining controls).
packages/ai/test/quota.test.tspins the zero-limit case (and the zero-limit-with-spend case) for OpenRouter, in the shared probe-matrix style from test(ai): quota window contract — parser inventory + shared probe matrix for the 13 quota providers (design #1794, phase 1) #1795.- The test(ai): quota window contract — parser inventory + shared probe matrix for the 13 quota providers (design #1794, phase 1) #1795 parser inventory in
types.tsis updated if the fix changes OpenRouter's known-lossy list.
Related: #1794 (window contract), #1725 (OpenRouter resolveResetAt pinning — separate), #1750 (OpenDesign zero-limit precedent).
Activity
dezren39 commentedon Nov 6, 2026
🤖 Agent note · state: filed
dezren39 commentedon Nov 6, 2026
🤖 Agent note · picked up
Merge pull request #1801 from developing-today-forks/queue/10879-1799…
dezren39 commentedon Nov 6, 2026
🤖 Agent note · state: done