Skip to content

design(ai): parse the window you were sent, or none — one window contract for the provider quota parsers #1794

Closed
6 of 6 issues completed

Description

@dezren39

Design: Parse the window you were sent, or none — one window contract for the provider quota parsers

Design doc: files/designs/opencode-quota-window-contract.md in the goal workspace (keep-the-four-repos-healthy) — problem, evidence, alternatives, risks, and open questions in full. Grounded on main aa2fe24fe3981756d37452191175e0416d3853ae (files read via the GitHub API; the sandbox clone was missing at run start).

Problem

#1718 / PR #1722 (2026-11-05) landed thirteen per-provider quota functions in packages/ai/src/quota/providers/, each parsing a different wire format into one published shape — usage.windows, a Record<string, QuotaWindow> (src/quota/types.ts) — now the fleet's single quota source (developing-today/code#6125). Within a day the unstated window rules produced one defect per provider:

The tell: src/quota/utils.ts shares the scalar layer (toNumber, clampPercent, resolveWindowLabel, toUsageWindow) but there is no shared window layer — every provider inserts by direct assignment, so the landed rules exist only as comment-cited precedents inside one provider each (opendesign's keep-first, codex's -primary/-secondary collision qualifiers at codex.ts:137, meta's honest null).

The contract

Parse the window you were sent, or none.

  1. Identity is semantic; a derived label never erases a window — the key names the window's role/kind where the payload carries one; collisions disambiguate both keys or keep the semantic key; overwriting a published window is a violation in every spelling.
  2. Duplicates keep the first — "contradictory duplicates are a payload anomaly, not an update" (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/fix(ai): cline-pass quota silently overwrites duplicate limit entries (last wins) — diverges from the #1750 keep-first convention, and the mapping is unpinned #1769 wording).
  3. Missing is null, and nulls the field — not the window, and never a number — never 0, never 1-as-fully-remaining, no fabricated default label.
  4. No figure, no window — an empty/all-null/zero-limit window-shaped object publishes no window; only quota figures are windows (fix(ai): Command Code quota publishes purchasedCredits as a quota window — contradicting the module's own READ RULE that it is never a quota figure #1746's purchasedCredits READ RULE, generalised).
  5. A window's fields agree — derived fields are computed from the published (post-disposition) values; the out-of-range disposition (clamp vs publish-truthfully) is the provider's stated choice, applied to the whole window, never split; out-of-range never means the window vanishes.
  6. Unknown kinds are skipped, not renamed — no fabricated familiar label; groq's no-quota-surface is the same rule at result level.
  7. Every pure parser is pinned against this contract — the oracle is the contract itself; one shared probe matrix (duplicate, collision, unknown kind, missing figure, empty object, out-of-range) per parser, and a complete 13-provider inventory where a provider with no pure parser says so with its reason.

Scope boundary: #587 owns what consumers do with windows (core routing — this design is its producer-side sibling); #1729 owns whether a provider result is emitted (dispatch). This design owns only what a parser puts in windows, and under what key.

Phases

  1. The contract, the parser inventory, and the shared probe matrix — filed as a new sub-issue this run (contract stated in src/quota/, a 13-provider parser inventory, the matrix asserting the already-conforming parsers; known-lossy rows linked, not asserted; no production behaviour changes).
  2. The four open per-site conformances land — adopts existing open fix(ai): antigravity quota buckets keyed only by derived label — an unrecognized bucket overwrites the real 5h window, same-label buckets silently drop, missing remainingFraction counts as fully remaining #1768 + fix(ai): meta quota rolling window keyed only by derived label — a weekly-length rolling window is silently overwritten by the weekly window #1777 + fix(ai): claude quota transform silently overwrites duplicate limits entries (last wins) — diverges from the #1750 keep-first convention #1781 + fix(ai): mistral quota budget window disagrees with itself — usedPercent clamped, usedAmount derived from the raw percentage #1785 as sub-issues; all keep their own queue lifecycles (not re-enqueued); each landing flips its inventory row.
  3. The unpinned remainder is pinned, drop-vs-null decided — adopts existing open test(ai): PR #1722's quota suite pins 4 of its pure parsers — opencode-go's three parsers and openrouter's reset math have none, and opencode-go silently drops windows its siblings would keep #1725 (opencode-go's three parsers + openrouter's resolveResetAt) as a sub-issue, lifecycle untouched; the design records the rule-3 recommendation (meta precedent: keep the window, null the field) for its closing note to adopt or rebut.
  4. Not filed — a shared insertion helper in utils.ts with all providers migrated, a new-provider ratchet, and the downstream key-spelling compat question (code#6125 consumers). Open questions in full in the design doc.

No Drew decision is needed: the contract adopts conventions the repo's own landed fixes already stated (keep-first, collision qualifiers, honest nulls), and phase 1 changes no production behaviour.

🤖 Agent note · filed by repo-design-architect (opencode pass, 2026-11-05). Evidence verified singly via gh against main aa2fe24f this run; the code claims (antigravity.ts:314 last-wins assignment, claude.ts:158 unconditional addWindow, codex.ts:137 collision qualifiers, meta's object-literal + honest-null comment, utils.ts with no window layer) were read verbatim from current main, not from the citing issues alone.

Acceptance criteria

Activity

  1. added
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    app/aiPackage: ai (pkgs/ or apps/ in this repo)
    effort/mMedium: expected under 1 day of work
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    on Nov 6, 2026
  2. dezren39 commented on Nov 6, 2026

    @dezren39
    Author

    Handoff (task 11096, lane opencode-a): verified OPEN 2026-11-06, no hard-stop labels. Execution lane is opencode — checkpointed handed_off and parked for the opencode claimant to bind and work it. Attempts 4/6.

  3. 15 remaining items

  4. removed
    task/triageQueue packet kind: verify, reproduce, and route this item
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 8, 2026
  5. dezren39 commented on Nov 8, 2026

    @dezren39
    Author

    Partial verification — staying open (2026-11-07 done-task-only audit, tracking: developing-today/code#7016).

    Queue task(s) [11176] 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 enqueue deferred: this issue was active in tonight's lane shifts after the audit snapshot.

  6. dezren39 commented on Nov 8, 2026

    @dezren39
    Author

    Triage update (task 11855, lane opencode-a) — re-verified fresh 2026-11-07 against origin/main 24200c3 and live issue/queue states; supersedes the task-11176 triage where noted:

  7. developing-today commented on Nov 8, 2026

    @developing-today

    Triage update (task 12815, lane opencode-a) — re-verified fresh 2026-11-08 against live issue/queue states; supersedes the task-11855 triage where noted:

  8. dezren39 commented on Nov 8, 2026

    @dezren39
    Author

    🤖 Agent note · inventory flip (phase 3 landed)

  9. dezren39 commented on Nov 8, 2026

    @dezren39
    Author

    🤖 Agent note · inventory flip (phase 2 meta collision landed)

  10. added
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    and removed on Nov 10, 2026
  11. dezren39 commented on Nov 10, 2026

    @dezren39
    Author

    🤖 Agent note · triaged

    • Agent: triage-step
    • State: triaged
    • Verdict: work kind task/small-fix · priority/normal · effort/m · kind/design (as filed)
    • Priority why: priority/normal retained from filing; Drew priorities brief line 4 (keep the four repos healthy).
    • First step: Inventory the provider quota parsers and their window handling; state the one window contract where the parsers share code.
    • Acceptance: Every quota parser follows the single contract: a sent window is parsed; an absent window yields none, never an invented window Parser tests cover sent-window and no-window cases
    • Fix packet: repo-health:v1:fix:developing-today-forks/opencode:issue:1794 → queue repo-health-opencode-pilot (enqueue result: see triage log; raw: ": "2026-11-10T02:43:48Z",
      "updated_at": "2026-11-10T02:43:48Z",
      "completed_at": null
      }
      packet valid (repo-task/v1)
      )
  12. dezren39 commented on Nov 10, 2026

    @dezren39
    Author

    🤖 Agent note · completed (all phases landed)

  13. removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 10, 2026
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)effort/mMedium: expected under 1 day of workkind/designA design proposal (design-architect): multi-step, phased, decision-bearingpriority/normalOrdinary priority (the default)source/design-architectFiled by the repo design architect jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions