The public origin motir-core EMITS — the follow-digest links, and what app/sitemap.ts contains once nothing public is served
Type: code · Executor: coding_agent · Repo: motir-core · ONE PR.
⚠️ RE-SCOPED 2026-08-30 — the premise was measured and most of it is about to be deleted
This card was authored as "publicSiteOrigin() stops delegating to MOTIR_BASE_URL", on the reasoning that canonicals, og:url, JSON-LD @id and sitemap entries would need a second origin. Nearly all of those live in app/(public), which leaves this repository entirely. Measured on the branch, every reader of publicSiteOrigin() / publicProjectUrl():
| reader | after app/(public) is deleted |
|---|---|
app/(public)/p/[identifier]/{layout,changelog/page,changelog.xml/route}, explore/(square)/page, explore/topic/[slug]/page, _components/PublicProjectJsonLd, explore/_components/ExploreJsonLd | gone — 7 files, the whole canonical/JSON-LD population |
app/sitemap.ts | survives, with nothing public left to list |
lib/rootMetadata.ts | survives, and is not this card's — metadataBase answers where is the application, which stays MOTIR_BASE_URL |
lib/services/publicFollowDigestService.ts | survives, and is the real subject of this card |
So the split is still owed and its purpose has changed. It is not about the pages this application renders — it will render none. It is about the absolute links this application EMITS into the world for a host it does not serve: the follow-digest email points a reader at a public project page on motir.co.
⚠️ And the second stale framing: there is no inert window to protect
The earlier body said the fallback arm was load-bearing because the public origin would be unconfigured until a cutover. motir.co already resolves to the motir-marketing Fly app — the apex does not move (docs/decisions/public-surface-hosts.md §7, MOTIR-3910). The public origin is knowable and configurable today, so the interesting case is a MISSING configuration in a self-hosted or local build, not a window in production.
What this card decides and ships
publicSiteOrigin()resolves its own configured variable, no longer delegating toresolveBaseUrlTrimmed().lib/baseUrl.tskeeps the application-origin question and its comment names which consumers moved.- The follow digest emits links to the public site.
publicFollowDigestServiceis the one shipped consumer that outlives the page deletion, and its links must resolve for a reader who clicks them from an inbox. - ⚠️
app/sitemap.ts— DECIDE, do not leave it. It currently emits the square, its rank variants, every topic page and every public project against one origin. When this application serves none of them, a sitemap that still lists them is a crawl directive pointing at another host's URLs from this host's file. Say what it contains: the application host's own indexable surface, or nothing at all — and if nothing, whether the route is deleted or kept empty, consistently withapp/robots.ts, which names it (MOTIR-3726). - ⚠️
/follow/confirmand/follow/unsubscribeare NOT the reading surface and have no owner. Two pages underapp/(public)reached from transactional email, not from the public site. They are the deletion card's to dispose of; this card must not assume they leave, because the digest it configures links to them.
Boundary
- It deletes no page and issues no redirect. Both belong with the story that lands the replacements.
- It does not touch
lib/rootMetadata.ts.metadataBaseis the application's own origin and is unaffected. - It does not decide where the public site is hosted — the ADR did.
Acceptance criteria
publicSiteOrigin()resolves from its own configured variable;lib/baseUrl.tsno longer serves the public question, and its precedence comment names the split.- Exactly ONE module reads each variable, asserted by a tree grep in the shape
tests/hosting/appUrlSeam.test.tsalready uses. - The follow digest's links resolve to the public site, asserted on the rendered email — the one consumer that outlives the page deletion, so it is the one the test must actually exercise.
- An unset public-site origin has a stated, tested behaviour for a local or self-hosted build. Whatever it is, it may not silently emit
localhostinto an email that leaves the building. app/sitemap.ts's contents are decided and recorded on this card, and a test asserts it lists nothing served by another host./follow/*is named on this card as the deletion card's to dispose of, with the digest's dependency on it stated.- ≥90% coverage on the files this PR touches.
Context refs
motir-core/lib/publicProjects/urls.ts—publicSiteOrigin(),publicProjectUrl()motir-core/lib/baseUrl.ts— the precedence contract and its single-reader rulemotir-core/lib/services/publicFollowDigestService.ts— the surviving consumermotir-core/app/sitemap.ts·app/robots.ts— the crawl pair, and MOTIR-3726's decision that robots names the sitemapmotir-core/docs/decisions/public-surface-hosts.md§7 — the apex does not movemotir-core/tests/hosting/appUrlSeam.test.ts— the single-reader idiom