QuotaWindow
@forge/monorepo / backend/src / QuotaWindow
Type Alias: QuotaWindow
QuotaWindow = {
kind:"calendar";period:RollupPeriod; } | {kind:"rolling";minutes:number; }
Defined in: backend/src/persistence/index.ts:474
The span a limit is measured over — #181.
A union, not an optional field beside period, because the two kinds are read differently and confusing them
produces a wrong number rather than an error. A calendar window is a bucket the rollups already hold; a
rolling window has no bucket and has to be summed from the ledger. An optional minutes next to a required
period would let a caller set both, and something would have to decide which one meant it.
A rolling window slides; it does not reset. "No more than X in any five hours" — the window is always the
last minutes up to now. That is a deliberate choice over the other reading, an anchored session that
starts on first use and hard-resets after five hours, and the reason is that an anchored window's boundary is
state: to know which session a spend belongs to you must know when the current one began, which cannot be
derived from the records without walking history forward from the first one ever. Storing the anchor would
make the boundary a row that can be wrong, stale or missing, and the refusal message quotes it to people.
The sliding reading needs no state, cannot drift, and is strictly harder to game — you cannot wait out a
boundary and spend twice the allowance across it. What it gives up is a single clean "resets at": headroom
returns gradually as records age out, so the honest statement is when the oldest one leaves, which is what
earliestAt is for.
Union Members
Type Literal
{ kind: "calendar"; period: RollupPeriod; }
Type Literal
{ kind: "rolling"; minutes: number; }
minutes so an admin can express 5 hours, 90 minutes or 2 days without a new type.