Skip to content

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

Closed

Description

@developing-today

test(ai): the quota suite still pins none of claude / meta-billing / opendesign's pure parsers — and three of them demonstrably drop or invent windows on plausible payload shapes

Sibling of #1725 (which covers opencode-go's three parsers and openrouter's reset math). The same gap remains for the other exported pure parsers in packages/ai/src/quota/providers/, re-exported from packages/ai/src/quota/index.ts:21-26:

  • toClaudeUsage — packages/ai/src/quota/providers/claude.ts:166
  • parseBillingBanner — packages/ai/src/quota/providers/meta.ts:105
  • parseCodingPlanWindows — packages/ai/src/quota/providers/opendesign.ts:78
  • normalizeOpenDesignCredential — packages/ai/src/quota/providers/opendesign.ts:49

grep across packages/ai/test/ and packages/core/test/ returns zero references to any of the four (verified this run on main 8185486080). packages/ai/test/quota.test.ts pins mistral, meta subscription, turso, groq, command-code and the toNumber blank-string cases — none of these four.

Verified fragile behaviors (laptop probe, main 8185486)

Probe: ~/git/fleet/probe-2026-11-05-creator-opencode-1535/probe-quota-parsers.ts on the laptop, importing the real modules.

  1. parseBillingBanner rejects numeric strings that every sibling parser accepts. parseBillingBanner({ remaining_minor_units: 12345, currency: "USD" }) → 123.45, but parseBillingBanner({ remaining_minor_units: "12345", currency: "USD" }) → null. The function hand-rolls a typeof minor !== "number" check (meta.ts:106-109) instead of the module's shared toNumber (utils.ts), which accepts numeric strings — the exact lesson of fix(ai): quota toNumber coerces empty strings to 0 — an empty upstream field reports a pool 100% used (or 0% used) #1741 (providers send string-typed numbers; the quota suite now pins that for command-code/opencode-go/meta-subscription). A string-typed banner silently nulls the Meta billing cross-check credits.
  2. toClaudeUsage discards the legacy windows whenever limits is non-empty. With limits: [{ kind: "weekly_scoped", … }] plus populated legacy five_hour/seven_day fields, the result is windows: {} — the session and weekly windows vanish, because the legacy branch is an else of limits.length > 0 (claude.ts:175-215). A payload whose limits list contains only scoped or unrecognized kinds loses data the payload itself carries.
  3. parseCodingPlanWindows publishes a window for a zero limit. { durationSeconds: 18000, limitCredits: 0, remainingCredits: 0 } yields a 5h window with usedPercent: null and valueLabel: "0 / 0 credits" (opendesign.ts:92-99 publishes whenever either percent or label exists via toUsageWindow). Compare command-code's pinned behavior: a zero monthly balance is a real 100%-used window (fix(ai): Command Code quota monthly window vanishes when remaining credits exceed the hard-coded 10 included — an over-limit payload reads as 'could not be parsed' #1744 tests), not a null-percent label-only window. Duplicate durationSeconds entries also silently overwrite (last wins, no note).

normalizeOpenDesignCredential showed no defect in the probe (bare token correctly prefixed with the session-cookie name; a full better-auth… cookie passed through) — it is included for pinning, not as a defect claim.

Why it matters

These parsers feed the same quota surfaces as the #1722/#1741/#1744/#1746 line of bugs: every one of those was a pure-parser shape assumption that only surfaced in production payloads. The four parsers above are the remaining unpinned surface of the same module, and two of them already demonstrably diverge from the module's own conventions on plausible payload shapes.

Acceptance criteria

  • packages/ai/test/quota.test.ts (or a sibling test file) pins all four parsers: happy path, missing/malformed fields, string-typed numbers, and the null/empty conventions the rest of the module follows.
  • parseBillingBanner accepts numeric-string remaining_minor_units via the shared toNumber (or the strictness is documented as deliberate, with a test pinning the rejection).
  • toClaudeUsage with a non-empty limits array that yields no session/weekly window still surfaces legacy five_hour/seven_day windows (or the exclusivity is documented and pinned by test).
  • parseCodingPlanWindows zero-limit behavior matches the module convention (a pinned test states which), and duplicate-duration handling is pinned.
  • Existing quota tests still pass (bun test test/quota.test.ts in packages/ai) and bunx tsgo --noEmit for packages/ai is clean.

Activity

  1. added
    kind/testTest coverage, test infrastructure, or test-only fixes
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    app/aiPackage: ai (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Nov 5, 2026
  2. developing-today commented on Nov 5, 2026

    @developing-today
    Author

    🤖 Agent note · filed

  3. added
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 5, 2026
  4. 17 remaining items

  5. added
    queue/claimedA queue worker has claimed the task for this item
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 5, 2026
  6. dezren39 commented on Nov 5, 2026

    @dezren39

    Implementation PR opened — task 10684 parked on CI.

    PR: #1766 (branch queue/10684-1750-quota-parsers, commit 278454a, laptop worktree ~/git/fleet/10684-1750-quota-parsers)

    What it does, per this issue's acceptance criteria:

    • parseBillingBanner (meta.ts) now uses the shared toNumber — numeric-string remaining_minor_units converts instead of silently nulling.
    • toClaudeUsage (claude.ts): legacy five_hour/seven_day windows fill any session/weekly window the limits list did not produce; scoped-only limits no longer discard them, and a limits-produced window wins its own slot.
    • parseCodingPlanWindows (opendesign.ts): zero/negative-limit entries publish no window (was the label-only "0 / 0 credits" window); duplicate durations keep the first entry instead of silently overwriting.
    • quota.test.ts pins all four parsers (including normalizeOpenDesignCredential, pin-only as probed): happy path, string-typed numbers, missing/malformed, null/empty conventions.

    Resume brief (task 10684, parked, not_before 2026-11-05T21:45:00Z): check PR #1766 CI (ai tests + typecheck) and Codex review; fix on red and re-push from the laptop worktree; disposition any Codex findings; merge when green per the standing directive — the PR closes this issue on merge — then complete task 10684. Note: the queue park reason was truncated to a placeholder by a flag probe; this comment is the authoritative resume brief.

    Parked by: lane opencode-a, task 10684 — PR #1766 awaiting CI

  7. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    and removed
    queue/claimedA queue worker has claimed the task for this item
    on Nov 5, 2026
  8. added a commit that references this issue on Nov 5, 2026
  9. dezren39 commented on Nov 5, 2026

    @dezren39

    Merged — PR #1766 landed as 4b9febf and this issue auto-closed.

    Verification actually run (laptop, worktree ~/git/fleet/10684-1750-quota-parsers, against the PR branch):

    Queue bookkeeping: task 10684 is parked (not_before 2026-11-05T21:45:00Z) from the PR-open step; the work is now done, so the next claim of task 10684 should simply complete it as done/merged — no further code work remains.

    Completed by: lane opencode-a, task 10684 — PR #1766 merged 4b9febf

  10. removed
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 5, 2026
  11. dezren39 commented on Nov 5, 2026

    @dezren39

    🤖 Agent note · done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/aiPackage: ai (pkgs/ or apps/ in this repo)area/typescriptTypeScript code areaeffort/sSmall: expected under 2 hours of workkind/testTest coverage, test infrastructure, or test-only fixespriority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions