A serial longform publication where editorial discipline is the architecture — not a CMS afterthought.
Long-form publications break when the editorial layer is a UI on top of a database. Reader Publications inverts the stack: schema-validated frontmatter, a cross-entry invariant checker, and design tokens as code — so the manuscript and the build refuse to diverge. The live showcase is Outchurched, a 34-day greenfield build that hit production on day 28.
Generic CMSes leak. Hand-rolled publication sites rot.
Editorial integrity is an opt-in plugin, not a contract
The schema is whatever the editor happened to type. Cross-references break silently. Required fields aren't required. Translation, taxonomy, and version pinning are bolted-on add-ons whose contracts drift from the manuscript. A typo in one entry can publish a broken page hours later, and the build will not stop you.
Schema discipline starts strong and decays
Day-one frontmatter is clean. By chapter forty, optional fields have crept in, two contributors disagree about field names, the cross-reference convention shifted, and the design tokens are scattered across half a dozen CSS files. Nothing automated tells you the manuscript and the build have diverged — until a release breaks the reader experience.
The manuscript and the website are coupled by a checked contract. Frontmatter validates against generated Zod schemas, a cross-entry invariant checker runs before every build, and design tokens emit from a single source — so a broken manuscript cannot produce a deployed site, and the editorial system cannot quietly drift from the design system.
A two-repo split with a checked contract at the seam.
The website repo owns Astro, build, and tooling. The content submodule owns the manuscript, the JSON Schemas, and the editorial specs. The seam between them is enforced by codegen, schema validation, and a cross-entry invariant checker — every build, every PR, every release.
Two-repo split with submodule pinning
The website repo never duplicates manuscript content; the content repo never holds Astro tooling. They link via a Git submodule pinned to an explicit SHA per release — make sync-content advances the pin, releases bake the SHA into the deploy. The build is reproducible from git alone, with no --remote drift at deploy time.
JSON Schema → Zod codegen, validated at build
Four JSON Schema files (book, episode, chapter, threads) are the upstream source of truth for frontmatter shape. make gen-schemas emits src/generated/content-schemas.ts as named Zod exports; Astro's Content Layer validates every MDX file against them. A frontmatter typo is a build failure, not a 404 in production.
Cross-entry invariant checker as the final build gate
scripts/check-invariants.mjs runs as the last step of pnpm prebuild before every astro build — including inside the Docker builder stage. It enforces seven rules JSON Schema cannot express: unique IDs per collection, chapter.episode resolves to a real episode, every crossReferences[] entry resolves to a real chapter, outline coverage, and per-(episode, part) personal-voice presence. A broken content tree cannot produce a deployed site.
v0.3.0-launch tagged on 2026-05-16 at 575 commits[1]. Across the project: 122 merged PRs, 213 commits carrying explicit WBS task identifiers, a 6-minute median PR turnaround[2], and a single in-flight revert epic structured as four reviewable steps rather than one squashed commit[3]. The contract held: when CI gates finally turned on for master, the first failure was a 5-line absolute-path test bug — caught before it shipped[4]. astro build succeeds9 editorial design moves applied across the reader shell Editorial discipline is structural, not a layer you bolt on.
A competitor can spin up an MDX blog in an afternoon. Holding a long-form publication to a schema-validated frontmatter contract, a cross-entry invariant checker, and a token-driven editorial design language across a hundred-plus chapters is a different exercise — and it is the entire bet.
Schema-validated frontmatter, generated from JSON Schema
Four JSON Schema files compile to named Zod exports via <code>make gen-schemas</code>. Astro's Content Layer validates every MDX file at build. A frontmatter typo fails the build, not the reader.
Cross-entry invariants checked before every build
Seven rules JSON Schema cannot express — unique IDs, cross-reference resolution, outline coverage, per-(episode, part) personal-voice presence — enforced by a Node ESM checker that runs in the Docker builder stage.
Two-repo split, pinned per release
Manuscript and tooling live in different repositories joined by an explicit-SHA submodule pin. The website never copies manuscript content; the content repo never holds Astro tooling. Releases are reproducible from git alone.
Design tokens as code, not as CSS sprawl
An OKLCH token system in <code>src/lib/tokens.ts</code> emits the entire palette, surface, text, border, and spacing scale to <code>src/styles/tokens.css</code>. A nine-move editorial design language reuses these tokens across the reader shell — masthead, title-page hero, pull-quote, lede band, contents-as-frontmatter, descent spine — with WCAG AA contrast verified by unit test.
Six author-facing MDX components, contract-locked
<code>KeyPassage</code>, <code>WordStudy</code>, <code>DiscoveryMoment</code>, <code>Application</code>, <code>PersonalVoice</code>, <code>Callout</code> — importable from <code>@/components/book</code> with a documented prop surface. Authors compose with these; the editorial system stays narrow, the manuscript stays portable.
Plain-HTTP origin behind shared Traefik, no per-tenant TLS surface
Production is <code>nginx:alpine</code> serving static files on internal port 8080. A shared Traefik v3 edge terminates TLS, issues Let's Encrypt certs via TLS-ALPN-01, and forwards cleartext on the <code>web</code> Docker network. The container ships no certs, binds no host ports, and adds the publication as one more tenant — not one more operational footprint.
Four engagement tiers — from a one-week scope to an ongoing build.
Every engagement is fixed-price against a written scope. We start small on purpose: a Discovery week usually pays for itself in scope clarity before a line of editorial schema is written.
- 1–2 weeks of joint work with you and your editorial lead
- Frontmatter schema sketch (book / episode / chapter or equivalent)
- Invariant catalogue — the rules your manuscript must obey
- Written architecture brief and price quote for the build
- 6–10 week build against the Discovery spec
- Two-repo split with submodule pinning
- JSON Schema → Zod codegen + cross-entry invariant checker
- Reader shell with masthead, title-page hero, chapter reader, TOC
- OKLCH token system + four to six MDX editorial components
- Full nine-move editorial design language applied across the reader shell
- Six or more author-facing MDX components with documented prop surface
- WCAG AA contrast survey enforced by unit test
- Multi-stage Docker build, plain-HTTP origin behind shared Traefik
- Tag-triggered CI release pipeline with GHCR + SSH deploy + healthz smoke
- Ongoing schema and invariant evolution as the manuscript grows
- New MDX component development and editorial design moves
- Submodule-pin release cadence and CI maintenance
- Performance, accessibility, and SEO regression work
The canonical stack we ship — proven in the reference implementation.
max-width: 68ch, line-height 1.7, OKLCH-tuned text colours that pass WCAG 2.1 AA on every shipped surface. json-schema-to-zod and committed to src/generated/. Six author-facing components — KeyPassage, WordStudy, DiscoveryMoment, Application, PersonalVoice, Callout — importable from @/components/book. pnpm prebuild runs gen-schemas then check-invariants.mjs before every astro build. Seven cross-entry rules enforced; broken manuscript = failed build, not failed reader. node:24-alpine builder with BuildKit cache mount for the pnpm store; nginx:alpine runtime that discards the Node toolchain entirely. Image is the single deployable artefact; make test-e2e exercises the same image that ships. :8080; Traefik v3 on a shared VPS terminates TLS via Let's Encrypt (TLS-ALPN-01). HSTS, CSP, X-Content-Type-Options, and Referrer-Policy set by container nginx — Traefik is header-transparent. Three cache tiers: _astro/* immutable for a year, *.html always-revalidate, everything else one hour. deploy.yml: BuildKit build → GHCR push (semver + :latest) → SSH into the VPS → docker compose pull && up -d → curl /healthz smoke. The content submodule pins to an explicit SHA in the release commit; deploy is reproducible from git alone, with no --remote drift at build time. Single-host nginx:alpine origin on internal port :8080, fronted by shared Traefik v3 that terminates TLS and forwards cleartext on a shared Docker network. The container ships no certs, binds no host ports, and reads no runtime env vars — all configuration is baked in at build time via --build-arg. To change a value, rebuild the image and redeploy.
Three fixed phases from a scoping call to a production handoff.
Every build runs through the same gates. Earlier phases are de-risked by the Discovery week; the handoff is non-negotiable — you leave with both repositories, the schemas, the invariant checker, and the deploy pipeline.
Joint scoping with your editorial lead. We leave Discovery with a frontmatter schema sketch, a written invariant catalogue, a reader-shell wireframe, and a fixed quote for the build.
Two-repo split with submodule pinning, JSON Schema → Zod codegen, cross-entry invariant checker, reader shell, MDX editorial components, OKLCH token system, multi-stage Docker, tag-triggered deploy. The reference implementation reached production in 34 days through this loop.
Both repositories transferred, deployment runbook, schema/invariant evolution guide, design-token reference, and an architecture walkthrough with your engineers. Optional retainer for ongoing authoring support; the codebase runs without us.
Anywhere a serial publication needs to hold a contract over time.
Outchurched is one instance of a generalisable pattern. The same architecture — manuscript-as-MDX, schema-validated frontmatter, cross-entry invariants, design tokens as code — applies wherever editorial discipline has to outlast the team that started it.
Need a publication where the manuscript and the build cannot drift?
We build serial longform publications for authors, editors, and publishers who need editorial discipline as a checked contract — fixed price, fixed timeline, two-repo handoff. Start with a Discovery week; leave with a schema sketch, an invariant catalogue, and a quote. If you build, you own both repos.