Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-3067Done

(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.000: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-2224MOTIR-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

  1. Every test that asserts a 429 / refusal / a degraded: false outcome against the real Postgres store pins a generous test-time deadline via createPostgresRateLimitStore({ timeoutMs }), following the shape tests/api/v1/shared-store.test.ts:460 already uses rather than introducing a third idiom. tests/rateLimit/guard.test.ts is the known instance; say per file what was found.
  2. 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.
  3. A test that deliberately EXERCISES the fail-open arm (sharedStore.test.ts:208's timeoutMs: 20) is untouched and is named as such — pinning a long deadline there would delete the coverage.
  4. 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 pins timeoutMs, 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).
  5. The PR body reports a measurement, not an assurance: run the failing case in a loop with an artificially tight timeoutMs and 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 into allowed: 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.