Vitest gate (motir-marketing) — the seven routes, the front-matter contract, and the no-database assertion
Type: test · Executor: coding_agent · Repo: motir-marketing · ONE PR. The story's per-repo coverage + integration gate for the receiving side.
This story spans two repositories, so it carries one gate card per repo — one repo, one suite, one PR. This is motir-marketing's; the motir-core gate is the other.
⚠️ Why this card STAYS in MOTIR-3909 while its subject moved
The render card — this suite's subject — was re-filed to MOTIR-3932 on 2026-08-30, along with its design asset. This card did not follow it, and the reason is its OTHER blocker: the subprocessor guard card is a motir-core card in this story, and the subprocessor-seam guard below asserts against the manifest it emits. Moving this card to MOTIR-3932 would make that edge cross-parent in the opposite direction and close a cycle against MOTIR-3909 blocked_by MOTIR-3932.
So this card is blocked_by the guard card directly, and its dependency on the render card is carried by the story edge — the whole of MOTIR-3932 is done before anything here starts, so content/legal/ and the routes exist by the time this suite runs. It is also why this story's repository set stays motir-core + motir-marketing: this card is the motir-marketing half.
The lane it runs in — read before writing a line
motir-marketing runs Vitest with jsdom (vitest, @vitejs/plugin-react, @testing-library/react, tests/setup.ts), and it has no Playwright and no database. So every assertion below is a route-render or a file-level assertion, and none of them needs a server. The existing suites — tests/destinations.test.ts, tests/siteHeader.test.tsx, tests/aaMatrix.test.ts, tests/support/themeTokens.ts — are the house patterns to compose.
The browser-level walk of motir.co/legal is NOT this card's. MOTIR-3886 is the public site's Playwright journey and its step 4 already reads "Open Docs, then a legal document from the footer — each stays on the public host", under an acceptance criterion covering steps 1–8. That card runs after MOTIR-3884, which runs after the render card — all three now siblings under MOTIR-3932 — so the pages exist by the time it walks them. Introducing a second Playwright harness here to cover the same walk would be a duplicate.
What this gate does at run time — three jobs, not a fixed case list
- Coverage floor over the story's changed surface in this repository — the loader, the two route components and the copy — writing the missing unit and branch tests to reach the project's floor. Do not pre-subtract what the render card's own units already cover; that they cover it is the expected normal, and this gate tops up the seams between them.
- The integration seam, which here is the directory as the registry: drive the REAL
content/legal/directory through the real loader into the real route components, rather than a fixture of one hand-written document. That is the seam a unit test mocks away and the one that actually broke inmotir-core's history — a document added and a route table not updated. - The contract guards coverage cannot see, listed below.
The guards
- Every
.mdincontent/legal/is REACHABLE. Glob the directory, render each slug, assert a 200-shaped result — so a document that ships by existing cannot 404. This is the propertymotir-core's loader was deliberately designed for and the reason a hard-coded slug list is forbidden. - The literal
TBDnever reaches a rendered page, over the real directory. Today every document has no effective date, so this is the arm in force, not an edge case. - Front-matter parity: every document parses, every
versionmatches^\d+\.\d+\.\d+$, and every document has atitle.motir-core'stests/legal/legalVersionGuard.test.tsexists for exactly this and its intent moves here with the documents. - The SUBPROCESSOR seam — every vendor row on
content/legal/subprocessors.mdhas a matching entry in the egress manifest the guard card emits frommotir-core, and a row with no entry FAILS. This is the page-side half of that split; the evidence side stays inmotir-corebecause that is where the dependencies are. - No database reader: no Prisma import, no
pg, no connection string anywhere in this repository — asserted, because the cheap wrong answer to "how does the marketing site get data?" is a second database client. - AA contrast for anything the legal surfaces introduce, in both themes, composed from
tests/support/themeTokens.ts.
⚠️ What this card may NOT assert
- Nothing about
motir-core. The manifest's PRODUCTION — that it matchesmotir-core's dependency tree — is asserted there. Asserting it here would test a copied file, which is the harness, not the product. - Nothing about the redirect.
app.motir.co/legal/*is MOTIR-3884's and is not reachable from this repository's suite. - No case for a feature not yet built. Every behaviour asserted maps to a shipped symbol in this repository at the moment this card runs.
Acceptance criteria
- The suite is green in this repository's CI and runs in the existing Vitest lane with no new runner.
- The reachability guard globs the real
content/legal/directory and renders every slug it returns; adding a file to that directory with no other change keeps the suite green, and removing one from the sitemap makes it red. Both directions demonstrated in the PR. - The
TBDguard, the front-matter parity guard and the subprocessor-seam guard are each present and each shown going RED against a deliberately broken fixture in the PR body. - The no-database-reader assertion exists and covers imports as well as strings.
- Coverage over this story's changed surface in this repository meets the project floor; the PR body shows the run.
- Every guard names the exact mechanism it leans on — the real directory, the real loader, the manifest file's real path — and the PR body confirms each is reachable from this runner.
- The PR body records that the browser-level walk is MOTIR-3886's, quoting its step 4, so the deferral is attributable rather than assumed.
Context refs
motir-marketing/tests/—destinations.test.ts,siteHeader.test.tsx,aaMatrix.test.ts,support/themeTokens.ts,setup.ts; the lane's house patternsmotir-marketing/package.json— the runner set; no Playwright, no database clientmotir-marketing/content/legal/— the population the reachability guard globs, once the render card lands itmotir-core/tests/legal/legalVersionGuard.test.ts·legalPagesRender.test.tsx·legalDocuments.test.ts— the intents that move heremotir-core/tests/legal/subprocessor-list-guard.test.ts— the guard this card holds the page-side half of- the render card — the subject, in MOTIR-3932; ordered against by the story edge, not a leaf edge
- the guard card — emits the egress manifest this asserts against;
blocked_by, sibling - MOTIR-3886 — the public site's Playwright walk, which covers these pages in a browser
Advisory dispositions
likely-missing-edge→ MOTIR-3886: NOT REAL, and the edge would be backwards. This card names that one as the owner of the browser walk it deliberately does not duplicate — a hand-off OUT, not a dependency IN. MOTIR-3886 sits in MOTIR-3932, which this whole story isblocked_by— so an edge from here into it would close a cycle against that story edge.- The remaining
referenceadvisories name cards this one's Boundary excludes. A boundary exists to say a deliverable is somebody else's, and naming the owner is what makes the exclusion checkable — so the reference is deliberate and no edge is owed.