# Requirement change — count with a sliding window

We shipped, and the first thing a large customer noticed is the thing we already knew about and
wrote down: with a fixed window of 100 a minute, they can send 100 at 10:00:59 and 100 more at
10:01:00 — two hundred requests inside two seconds, and every one of them inside the rules. That is
the fixed window's known behaviour and we are not calling it a bug, but "100 a minute" is going into
a contract next quarter and it has to mean **100 in any minute**, not 100 per minute as our clock
happens to slice them up.

So there is a third way to count, and some rules will use it while others carry on using the two
that exist. Nobody's existing rules change.

## What the new algorithm means

`Algorithm.SLIDING_WINDOW` — `limit` requests in **any** `window`-long stretch of time, measured
backwards from right now.

- The window is the **trailing** interval `(now - window, now]`. A request counts against the rule
  for exactly `window` after it was allowed, and then stops counting. A request made **exactly**
  `window` ago has already fallen out and does not count — the same start-inclusive, end-exclusive
  convention `FIXED_WINDOW` already uses, applied to a window that moves instead of one that sits
  still.
- **Nothing is aligned to anything and nothing resets.** There is no boundary, so there is no
  `2×limit` burst across one. A caller who spends their whole allowance at 10:00:05 gets nothing
  back at 10:00:10; they get one unit back at 10:00:15 for each request they made at 10:00:05.
- **Only allowed requests are recorded.** A denial consumes nothing — that rule has not changed —
  so it leaves nothing behind to expire later.
- `retryAfter` on a denial is the time until the **oldest still-counting request falls out of the
  window**, which is the first instant at which one unit is genuinely free. It is strictly positive,
  as always.
- Everything else is exactly as it was: a rule still has a name a denial can quote, several rules on
  one key still all have to pass, evaluation is still all-or-nothing, and the clock is still the
  injected one.

The revised `Algorithm` enum in `contract-delta/` states all of it on the new constant, including a
side-by-side table of what all three algorithms answer for the same history — read that before
writing assertions, because the three genuinely disagree and all three are right.

## What we are not asking for

- Not a replacement. `FIXED_WINDOW` and `TOKEN_BUCKET` stay, unchanged, and a client may hold rules
  of all three kinds at once.
- Not an approximation. We know a sliding-window *counter* over sub-buckets is cheaper and close
  enough for most people; we want the exact one here, and we want to hear you say what it costs.
- Not a change to any existing convention — not the boundary rule for fixed windows, not the token
  bucket's refill, not what a denial consumes, not `retryAfter` for the rules that already exist.

## The question behind it

You were told when you started that a third algorithm was coming. This is it. The interesting
number is not whether it works — it is how many lines of the code you had already written you have
to go back and change to let it in.
