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

11.2.11 Story vitest gate — coverage floor, the write→read seams, and the contract guards the work-item endpoints owe

The story's per-repo coverage + integration gate, run AFTER every feature card of this story has merged — it measures their REAL merged surface, so it cannot run before they land. It is not a static list of cases: each card already ships its own units (the per-subtask floor), and this card does the three things those units structurally cannot.

1. Coverage floor

Run coverage over this story's changed surface — app/api/v1/** and lib/api/v1/workItems/**, plus the changed service/repository reads — and WRITE the missing unit/branch tests wherever it sits below the project's ≥90% per-file floor. "Already covered by the per-subtask floor" is the EXPECTED normal here, not a reason the card has nothing to do: what it tops up is the seams BETWEEN cards.

2. Integration seams — real Postgres, no mocks

The tests each endpoint's own suite cannot write, because they span two cards:

  • Write → read round trip: create through POST, then find that exact row through the LIST endpoint (the keyset read + presenter path) and through the DETAIL endpoint, and assert all three agree field-for-field. This is the seam where a mapper and a read drift and no unit notices.
  • Presenter × keyset read: a row created with every optional field null, and one with all set, both survive the list and detail schemas — the null-shape case unit fixtures usually miss.
  • Transition × workflow: GET …/transitions and the allowedTransitions on a refused POST are produced by different code paths and must return the same set for the same item.
  • Links × detail: an edge written through POST …/links appears in BOTH the links sub-resource and the detail resource's groups, in the same shape.
  • Archive × list: an archived item leaves the list page and returns on restore, with the cursor walk staying correct across the change (the collection mutating mid-scan is the normal case here, not an edge case).
  • ETag × PATCH: the validator the detail read issues is the one the write path accepts — a round trip proving one function owns both directions, and that a write invalidates the previous validator.
  • Filter parity, end to end: the same FilterAST through the v1 list endpoint and through the /items view's read yields the same key set.

3. Contract guards — the guarantees a coverage percentage cannot see

Extend tests/api/v1/story-gate.test.ts's shipped auditV1RouteSource sweep, and add the guards this story owes:

  • No /api/v1 route imports lib/mcp/tools/**. The two surfaces are aligned through SCHEMAS (11.6), never through imports; a route reaching into the tool layer couples a stable contract to a deliberately fluid one.
  • The cascade delete is unreachable: no /api/v1 route references deleteWorkItem, and no route declares work_items:delete. The omission is asserted so it cannot be undone by accident — the ADR's own condition for leaving it out.
  • Every route's declared scope matches the ADR §3 table, asserted table-driven over the whole tree (a GET declares read; a work-item mutation work_items:write; archive/restore work_items:archive), so a new endpoint cannot pick its own gate.
  • Every response body a v1 work-item route returns goes through the schema module — no route hand-shapes a response inline, which is what makes 11.4's "single schema source" true rather than aspirational.
  • Each guard fails when violated — add the deliberately-bad fixture for every new guard, as the shipped suite does for the Prisma / transaction / wrapper rules. A guard with no failing case proves nothing.

Scope BOUNDARY

Vitest only, motir-core only, one repo one suite one PR. It does not drive the API as an external HTTP client with a real PAT — that is 11.2.12 — and it does not add endpoints, fix product defects it finds (those are their own cards, per the drive-by rule) or touch 11.3's surface.

Acceptance criteria

  • Every file this story added or changed meets the ≥90% per-file branch/fn/line floor, with the top-up tests written where it did not.
  • Each integration seam above is covered against real Postgres, driving the REAL routes rather than calling services directly.
  • Each new contract guard is present AND has a deliberately-violating fixture proving it fails.
  • The guard sweep finds the whole route tree (a guard over zero files proves nothing) and names the new route files explicitly, so a deleted route is a failing test rather than a silently smaller sweep.
  • The suite runs in CI on the standard vitest lane and needs no new infrastructure.

Context refs

  • tests/api/v1/story-gate.test.ts — the shipped gate this extends, including auditV1RouteSource and its violation fixtures.
  • tests/api/v1/pagination.test.ts · tests/api/v1/wrapper.test.ts — the existing per-concern suites.
  • motir-core/CLAUDE.md — the coverage floor and the vitest/real-Postgres conventions.
  • docs/decisions/public-api-conventions.md — §3 (the scope table the guard asserts) and §9 (the 4-layer rule it enforces).
  • Subjects: 11.2.2 · 11.2.3 · 11.2.4 · 11.2.5 · 11.2.6 · 11.2.7 · 11.2.8 · 11.2.9 · 11.2.10. Parent story: 11.2.