(motir-core) `rateLimit/guard.test.ts` takes the DEFAULT 250 ms store deadline, so a loaded runner fails it OPEN — the store-timeout arm MOTIR-2658 fixed in one file and left in the class
Repo: motir-core. One PR. Runtime tests only — no production code changes. Found by motir run MOTIR-3051 (PR #2130, run 32198903941, job 95908390604) on a diff touching no limiter, store, route or middleware file.
The failure
FAIL tests/rateLimit/guard.test.ts > the 429 is spec-correct > carries Retry-After AND the X-RateLimit triple
AssertionError: expected null not to be null
❯ tests/rateLimit/guard.test.ts:47:21
1 failed | 5135 passed, every other check green.
It is the STORE DEADLINE, not the window — and the discriminator says so directly
MOTIR-3016 closed the epoch-window arm for this file and stated in its own body that "if this assertion ever fires again it is NOT this class." Correct. The job log carries exactly one store unavailable line, and vitest attributes it to this very test:
stderr | tests/rateLimit/guard.test.ts > the 429 is spec-correct > carries Retry-After AND the X-RateLimit triple
[rateLimit] store unavailable; allowing the request RateLimitStoreTimeoutError: The rate-limit store did not answer within 250ms.
at Timeout.<anonymous> (lib/rateLimit/postgresStore.ts:60:41)
Mechanism, end to end: DEFAULT_RATE_LIMIT_STORE_TIMEOUT_MS = 250 puts a hard deadline on one counter increment; on timeout consumeSharedRateLimit fails open by design ({ allowed: true, degraded: true }, lib/rateLimit/limiter.ts:78); enforceRateLimit then returns response: null; :47 asserts not-null. The shard ran 5 136 tests in 753 s against a shared Postgres — 250 ms is a tight budget there.
The window arm is independently excluded by the timestamps: the pair ran at 00:05:12.0–00:05:12.6, crossing no minute boundary. (This PR's base predates MOTIR-3016's merge, so both arms were live on it; only one fired.)
Why this is a card and not another flake-log line
MOTIR-2658 diagnosed this exact mechanism and fixed its own file, tests/api/v1/shared-store.test.ts. MOTIR-3016 then named the store-timeout arm in Out of scope and moved on. So the class has been correctly understood twice and swept zero times — the same instance fixed, class left standing shape MOTIR-2224 → MOTIR-3016 went through for the window arm, which took four months and roughly six red PRs to close.
And the cure already exists in the repo. createPostgresRateLimitStore takes options.timeoutMs (lib/rateLimit/postgresStore.ts:80), and TWO sibling files already pin it — tests/rateLimit/sharedStore.test.ts:208,227 and tests/api/v1/shared-store.test.ts:460 (setRateLimitStore(createPostgresRateLimitStore({ timeoutMs: TIMEOUT_MS }))). guard.test.ts calls __resetSharedRateLimitStoreForTest() and inherits the production 250 ms.
Acceptance criteria
- Every test that asserts a 429 / refusal / a
degraded: falseoutcome against the real Postgres store pins a generous test-time deadline viacreatePostgresRateLimitStore({ timeoutMs }), following the shapetests/api/v1/shared-store.test.ts:460already uses rather than introducing a third idiom.tests/rateLimit/guard.test.tsis the known instance; say per file what was found. - The sweep is by the claim, not by a path: any test whose assertion is falsified by a fail-open decision. Report which files accumulate against the real store and which pin a deadline, including the ones already correct.
- A test that deliberately EXERCISES the fail-open arm (
sharedStore.test.ts:208'stimeoutMs: 20) is untouched and is named as such — pinning a long deadline there would delete the coverage. - A guard catches the ABSENT pin, not only a wrong one — this is what makes it the last instance rather than the next-to-last, and it is the lesson MOTIR-3016 AC 4 wrote for the window arm one file over. Assert over
tests/**that a file which asserts a refusal against the Postgres store pinstimeoutMs, with opt-out by a named reason, never by silence. Mutate the guard to prove it fails — a comment naming the helper must not satisfy it (MOTIR-3016 hit exactly that trap). - The PR body reports a measurement, not an assurance: run the failing case in a loop with an artificially tight
timeoutMsand report fail-open→failure counts before and after the pin.
Out of scope
lib/rateLimit/**— the limiter is correct and the fail-open arm is a deliberate, documented contract ("a brief over-serve beats an outage caused by the thing meant to prevent one"). The defect is that tests assert the non-degraded branch of a path whose deadline they do not control.- The epoch-window class — closed by MOTIR-3016.
Context refs
tests/rateLimit/guard.test.ts:33-66— the failing case;:26__resetSharedRateLimitStoreForTest(), which is where the default deadline enters.lib/rateLimit/postgresStore.ts:30(DEFAULT_RATE_LIMIT_STORE_TIMEOUT_MS),:60(the rejecting timer),:80(options.timeoutMs).lib/rateLimit/limiter.ts:72-80— the fail-open catch that turns the timeout intoallowed: true.tests/api/v1/shared-store.test.ts:460·tests/rateLimit/sharedStore.test.ts:208,227— the pin, twice, already in the repo.tests/api/v1/rate-limit-window-alignment.test.ts— the sibling guard whose shape AC 4 copies.