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-2367Done

Story test gate (Vitest) — the coverage floor, the route→service→gate seams, and the guard that stops a second policy path

The story's per-repo coverage and integration gate, run AFTER every motir-core code card has merged so it measures their real, merged coverage. Modelled on the administrative split's gate; this one has more to prove, because its story removes capability rather than preserving it.

The three jobs

  1. Coverage floor. Run coverage over the story's changed surface — the services that gained a gate, lib/permissions/*, and the routes that changed — and write the missing unit and branch tests wherever a file is below the project's ≥90% per-file floor. Each card's own tests are the floor; this tops up the seams between them.
  2. The route→service→gate seams the unit tests mock. Drive a REAL request through a route to a real service to a real assertPermission against a real Postgres, for at least one operation per key, and assert on the HTTP status the client will actually see — the refusal shape (404 for a non-browser, 403 for a browser without the key) is what every consumer branches on, and no unit test on a service method observes it.
  3. The architecture guards coverage cannot see.
    • No second policy path. Every gate in the story resolves through projectAccessService; a test asserts that no service under lib/services decides access by reading a membership row and branching on the role itself — the private-helper shape the name-whitelist bug found three of.
    • Every member-facing key has at least one production call site, asserted by grepping lib and app outside tests/ — the inverse of the orphan guard, at story scope.
    • The cross-repo parameter name is pinned. The core client's job-read query parameter and the name the motir-ai card requires are the same string, asserted from a shared constant rather than from two literals — this is the one seam in the story that no single repo's tests can cover, and a typo in it fails closed and silently.

Boundary

One gate card, one repo. motir-ai has a single card in this story and it carries its own tests; there is no second gate card for it, because a story-level gate exists to cover the seams BETWEEN sibling cards and that repo has no siblings here. The seam that does cross the boundary is covered by job 3's shared-constant assertion.

Acceptance criteria

  • Coverage over the story's changed files meets the project's per-file floor, and the PR body names any file that was already above it and needed nothing.
  • At least one end-to-end route→service→gate test exists per member-facing key, asserting the HTTP status and body shape of the refusal, against real Postgres.
  • The no-second-policy-path guard fails when a service is edited to branch on a role directly — proved by writing that violation, watching it go red, and reverting it.
  • The production-call-site assertion covers all eight keys and excludes tests/.
  • The shared-constant test for the job-read parameter name exists and fails if either side's literal drifts.
  • pnpm test is green for the touched suites, and the story's own tests/permissions/ suite is unchanged by this card except where it adds coverage.

Context refs

  • tests/permissions/ — the four shipped suites this card tops up rather than duplicates.
  • motir-core/CLAUDE.md § coverage — the per-file floor.
  • lib/services/projectAccessService.ts — the single policy path the guard asserts.
  • The administrative split's gate — the sibling card whose shape this follows.
  • The name-whitelist bug — why the second-policy-path guard exists.