Skip to content

quota: provider-agnostic window model with provenance, merge rule, and linear pace (#725 Part 1)#740

Open
avs-io wants to merge 1 commit into
getagentseal:mainfrom
avs-io:feat/725-quota-model
Open

quota: provider-agnostic window model with provenance, merge rule, and linear pace (#725 Part 1)#740
avs-io wants to merge 1 commit into
getagentseal:mainfrom
avs-io:feat/725-quota-model

Conversation

@avs-io

@avs-io avs-io commented Jul 18, 2026

Copy link
Copy Markdown
Member

Part 1 of #725, matching the model shape proposed in the epic thread (design comment posted there yesterday).

What changed

src/quota.ts — pure, side-effect-free, no UI, no network. Depends on nothing, per the epic's Part 1 scoping:

  • Types: QuotaWindow (kind, label, usedFraction, resetsAt, windowSeconds) with a per-window source: 'live' | 'derived' — provenance is load-bearing: a derived window is an estimate and surfaces must be able to render it as one, same discipline as costIsEstimated.
  • mergeQuotaWindows(live, derived): live wins per window kind (quotaWindowKey keeps named and custom kinds distinct); derived fills the gaps; provenance survives on each window.
  • computePace(window, now): whole-window linear pace — expected fraction, delta (deficit/reserve), projected-at-reset, and an exhaustion ETA only when the projection overflows and the window is longer than 6h. Guards: nothing until 3% of the window has elapsed, nothing on reset skew (past, or beyond one window length), nothing on an exhausted window, inputs clamped. Same semantics as the menubar's QuotaPace in menubar: linear pace on Codex quota windows — deficit/reserve, projection, run-out ETA #728 so surfaces can never disagree about pace.
  • buildPlanQuota(...): assembles the merged, pace-annotated view. A provider with no readings yields an empty windows list — absent data is absent, never zeros.

On the open question from the design comment (where live snapshots enter on the CLI side): this lands option (a) — the model takes optional live windows as an input and works offline-first from derived data alone; no new network surface, per the epic's non-goals.

How tested

  • npx tsc --noEmit clean; new tests/quota.test.ts: 16 tests, table-driven over the merge rule (live/derived collisions, custom-kind separation, empty sources) and every pace guard and branch (on-pace, deficit ETA landing strictly before reset, reserve, short-window ETA suppression, early-window/skew/exhausted silence, the 6h boundary against the exported constant), injected clock throughout.
  • Full suite: 2161 passed; the only failures are app/electron/cli.test.ts on a fresh clone — the known app/electron/cli.test.ts fails on a fresh clone: dev-resolution tests require a built dist/cli.js #681 issue, and they pass once npm run build produces dist/cli.js.

@avs-io
avs-io marked this pull request as ready for review July 18, 2026 03:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant