motir.co renders the public reading surface — the shared chrome, /explore, /docs and /legal in motir-marketing over the public API
motir.co is one origin, served by motir-marketing, and this story is where its reading surface actually gets built. /explore, /docs and /legal move out of motir-core and are rendered here — the first two against motir-core's public read API, the third from files in this repository.
⚠️ AMENDED 2026-08-30 — this story took over motir.co/legal
MOTIR-4005 (the /legal design) and MOTIR-4009 (the /legal render) were authored under MOTIR-3909 and re-filed here. Both ship in motir-marketing, which is this story's entire repository set, and both compose MOTIR-3880's chrome — which is this story's child.
Their old home wired three cross-parent leaf edges under plan-rules/fixtures/phase-skeleton.md's no-legal-lift limb, on the grounds that MOTIR-4009 sat upstream of MOTIR-3884 while MOTIR-4013 sat downstream of it, making a story-level edge a cycle in either direction. The exemption did not apply — phase-skeleton.md offers RE-FILE as remedy (b) and it was never tested. With those two cards here the interleave is gone, every cross-story edge runs one way, and MOTIR-3909 blocked_by this story carries the whole ordering.
MOTIR-3909 keeps the motir-core side — the configured manifest, the three link surfaces, the deletion, the re-consent gate — and the marketing-side Vitest gate MOTIR-4011, which stays there because its other blocker is a motir-core card.
Why this is buildable — the re-measurement that settles it
MOTIR-3877 recorded that a second renderer cannot use app/api/public/*. Re-measured on origin/main 2026-08-29, that objection is materially weaker than written, and the full correction is on that card:
/api/public/exploreand/api/public/categoriesmake ZEROgetSession()calls.explore/route.ts's own comment: "NOT session-gated: a logged-out visitor / crawler reads it … deliberately nogetSession()call." They are exactly the contract this story consumes.- Of the ten routes, one is session-gated —
follow, a POST/DELETE account relationship — and it is not one this story needs. - The earlier "six routes call
getSession()" and a later "eight" were both artefacts of counting comment MENTIONS rather than calls.
So /explore is a fetch away. /docs is different and is discussed below.
Scope
- The shared public chrome, built. MOTIR-3880 draws one header, nav and footer for every surface on the brand host; this story renders it here, over
@motir/design-system. With every public surface on one origin the chrome is finally one implementation rather than two, which is the arrangement that design was drawn for. /exploreand/explore/topic/*, against/api/public/explore+/api/public/categories— the square, its ranked tabs, the topic landing pages, the empty and error states, and the crawl surface (robots.txt,sitemap.xml, canonicals, the entity JSON-LD whose SearchAction finally targets a search this host serves)./docs. ⚠️ This is the card that needs a decision before it is built, and the story must not paper over it: the eight doc pages are GENERATED frommotir-core's own registries —lib/apiDocs/referencereads the OpenAPI spec file,lib/apiDocs/mcpreads the tool catalogue. Rendering them here means consuming those registries across a repository boundary, which is the mirror-rottests/navigation/proxy-matcher.test.tsexists to catch. Decide HOW before writing the pages — a published artifactmotir-coreemits and this repo installs is the shape that does not rot; a copied spec is the shape that does./legaland/legal/<slug>— the seven documents, ported byte-for-byte frommotir-coreand rendered under the same chrome. Unlike 2 and 3 these are files on disk in this repository, not an API read: no database, no network hop, nomotir-corecall at run time. MOTIR-4005 draws them; MOTIR-4009 builds them, along with the Markdown renderer and front-matter parser this repository does not yet have.
Boundary — where this story ENDS
- It does not touch
/p/*. Public project pages are MOTIR-3877's; they land on the same host, under the same chrome, and that story isblocked_bythis one for the chrome and by MOTIR-3876 for the contract. - It does not touch the
motir-coreside of the legal move. The configured legal-document manifest, the three link surfaces onapp.motir.co, the re-consent gate over the manifest and the deletion ofcontent/legal/are all MOTIR-3909's, and that story isblocked_bythis one. - It does not delete anything from
motir-core. Retiring the abandoned pages and issuing the 301s offapp.motir.cohappens only once this story serves the replacements. MOTIR-3951 removesapp/(public)barlegal/; MOTIR-4013, over in MOTIR-3909, removeslegal/. - It does not repoint DNS.
motir.coalready resolves to this application. - It does not make anything cloud-only.
motir-marketingis moooon's own site; it is not shipped to self-hosters, so the gate has nothing to do here.
Verification recipe
https://motir.co— the landing, unchanged.- Explore from the header —
/explorerenders on the same host, under the same chrome, with real project cards. - A ranked tab, then a topic page from the footer's crawl links; back.
- Docs — renders on this host, and its content matches what
motir-corecurrently publishes. - Legal from the footer —
/legallists all seven documents; open one and it renders under the same chrome, with its version line; an unknown slug is a 404. robots.txtandsitemap.xmlon this host list these surfaces and nothing that lives elsewhere./explorewith no public projects — the empty state, not a crash; and with the API unreachable — the error state, not a blank page./legalis unaffected by an API outage, because it reads no API.
Acceptance criteria
- Every child is
done; the repository set ismotir-marketingalone. /explore,/explore/topic/*,/docs,/legaland/legal/<slug>render onmotir.counder one chrome, matching MOTIR-3880's asset — layout, states and the primitives it names — and/legaladditionally matching MOTIR-4005's.- Every read of PROJECT DATA goes through
motir-core's public API. No database client, no Prisma import and no direct connection string appears in this repository — asserted by a test, because the cheap wrong answer here is a second database reader. The legal documents are the one surface that reads neither: they are files in this repository. - The
/docsmechanism is decided and recorded before the pages are written, and whatever is chosen carries a guard that fails when this repository's copy drifts frommotir-core's registries. A copied spec with no guard is not an acceptable outcome. - The seven legal documents are byte-identical to
motir-core's, with per-file checksums from both sides, and every.mdincontent/legal/is reachable as a route. - The empty, loading and error states are implemented for every API-backed surface, and the error state is asserted — the API is now a network hop, so failure is a real state rather than a theoretical one.
- No link in the chrome resolves to a 404; the entity JSON-LD's SearchAction targets a search THIS host serves.
- AA contrast holds in both themes for everything the chrome introduces.
Context refs
motir-core/app/api/public/explore/route.ts·categories/route.ts— the anonymous contract this story consumesmotir-core/app/(public)/explore/— the surface being reproduced; read it for behaviour, do not port its data layermotir-core/app/(public)/legal/·content/legal/·lib/legal/documents.ts— the legal surface being reproduced, and the parser that ports acrossmotir-core/lib/apiDocs/{reference,mcp,surfaces}.ts— what/docsis generated from, and the reason its mechanism is a decisionmotir-marketing/app/_components/—SiteHeader/SiteFooter, the chrome this replaces- MOTIR-3876 — the versioned contract this story consumes;
blocked_by - MOTIR-3880 — the chrome asset this builds to
- MOTIR-3909 — the
motir-coreside of the legal move;blocked_bythis story
Advisory dispositions
likely-missing-edge→ MOTIR-3880 (the chrome design, named in this story's acceptance criteria): genuinely consumed, and NO edge is owed — it is this story's own child. The note that used to stand here recorded 3880 as mis-filed under MOTIR-3876 and owed a re-file; that re-file has happened and 3880 is a subtask of this story. A story→own-childblocked_bywould be cross-level, which gate 7 forbids; the parent relationship is what carries it.reference→ MOTIR-3877 (advisory): not consumed. It is named in this story's BOUNDARY, which exists to say/p/*is somebody else's.reference→ MOTIR-3909 (advisory): the direction is OUT, not in. That story isblocked_bythis one; this story names it to say themotir-corehalf of the legal move is not here.