11.1.5 Story vitest gate — coverage floor + the wrapper↔pagination↔limiter integration seams + the architecture guards for `/api/v1`
Repo: motir-core. One PR. The story-level vitest gate — the mandatory per-repo coverage + integration + architecture-guard card that runs AFTER the feature subtasks merge, because it measures their real, merged result.
blocked_by 11.1.2 + 11.1.3 + 11.1.4 — every code subtask of this story in this repo.
This is not a static list of cases to write, and "already covered by the per-subtask floor" is the EXPECTED normal it resolves at run time, not a redundancy to pre-subtract. It does three things:
1. Coverage floor
Run coverage over the story's changed surface (lib/api/v1/**, app/api/v1/**) and, wherever it sits below the project's ≥90% per-file branch/fn/line gate, write the missing tests to reach it. Each code subtask ships its own units as the floor; this tops up the seams between them, which is exactly where no single subtask's tests reach.
2. Integration seams — drive one subtask's REAL output through the next's REAL consumer
The three code subtasks compose into ONE wrapper, and their units mock each other's edges. Cover the real compositions:
- Auth × pagination: a paginated request from an unauthenticated caller must 401 BEFORE any cursor parsing or DB read — assert ordering, not just the status, because a limiter or parser that runs first is an unauthenticated compute path.
- Auth × limiter: does an unauthenticated request consume rate budget? Decide and ASSERT the answer deliberately — if it does not, an unauthenticated flood is unmetered; if it does, an attacker can exhaust a known token's budget without holding it. This is a real design question the units cannot see, and shipping it undecided is the gap this card exists to catch.
- Limiter × pagination: a client paging a large collection must be able to complete a full scan within the budget, or the API is unusable for its most ordinary read. Assert a full paged scan of a realistic collection does not 429.
- Error mapping × every layer: a service error raised beneath pagination still emerges as
{ code, error }with the right status and the rate-limit headers intact — headers must not be dropped on an error path.
3. Architecture / contract guards — what coverage percentage cannot see
- No
/api/v1route calls Prisma or opens a transaction — the 4-layer contract, asserted mechanically over the route tree, so the guard holds for routes added by 11.2 / 11.3 later. - Every
/api/v1route goes through the wrapper — a route that hand-rolls auth, skips the scope gate or bypasses the limiter fails the build. This is the guard that keeps the foundation load-bearing as the surface grows; mirror the registry-driven totality patternlib/mcp/scopes.tsalready uses. - Every route declares a required scope, and the declared scope matches the ADR's operation→scope table.
- Cross-tenant isolation across the whole v1 tree: a workspace-A token reads nothing from workspace B, with 404-not-403.
Scope BOUNDARY
One repo, one suite, one PR — motir-core only (nothing in this story touches another repo). It asserts ONLY the assembled surface of its DONE blockers: it does NOT pull forward coverage for 11.2 / 11.3 resource endpoints that do not exist yet (those ship their own tests), though its architecture guards are written to hold for them automatically. It does NOT drive the API over real HTTP as an external client — that is 11.1.6. It does NOT change production code; a defect it finds is fixed here only if this story caused it, otherwise it is logged as a bug.
Acceptance criteria
lib/api/v1/**andapp/api/v1/**meet the ≥90% per-file branch/fn/line floor, with any file gated on functions+lines instead carrying a written reason beside it. No existing gate entry is weakened.- Each integration seam above has a test driving REAL components — no mock standing in for another subtask's output.
- The auth-before-anything ordering is asserted, not assumed.
- The "does an unauthenticated request consume budget?" question is answered explicitly in a test with the rationale in a comment.
- A full paged scan of a realistic collection completes without a 429.
- Rate-limit headers survive every error path.
- The architecture guards fail the build when: a v1 route calls Prisma; a v1 route bypasses the wrapper; a v1 route declares no scope. Each is asserted by deliberately introducing the violation in the test.
- Cross-tenant isolation is asserted across every v1 route present.
- Tests use real Postgres, no mocked DB.
Context refs
lib/api/v1/**·app/api/v1/**— the surface under test.packages/cli/vitest.config.ts+ the rootvitest.config.ts— the per-file coverage-gate pattern and its documented carve-out format.lib/mcp/scopes.ts+tests/mcp/scopes.test.ts— the totality-guard pattern (typed map + runtime assertion) the route↔scope guard should copy.tests/helpers/db.ts— the real-Postgres truncate harness.- Blockers: 11.1.2 · 11.1.3 · 11.1.4. Sibling: 11.1.6. Parent story: 11.1.