Files
school_compare/nextjs-app/docs/PUBLISHING.md
T
TudorandClaude Opus 5 07d586d0ad
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m15s
PR Checks / Backend Smoke (pull_request) Successful in 10s
PR Checks / Build Backend (no push) (pull_request) Successful in 36s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m11s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 1m18s
PR Checks / AI Code Review (Claude) (pull_request) Failing after 2m43s
docs(blog): how to publish, and why the app has two route groups
PUBLISHING.md carries the house style with the posts, so the standard
survives without the design doc to hand — including the rule that a post
states what a metric does not show, which is the strongest signal a
human wrote it.

CLAUDE.md gains the two constraints that are invisible from the code and
expensive to rediscover: metadata file conventions break if moved into a
route group, and the build must keep succeeding with DATABASE_URL unset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
2026-09-02 16:30:09 +01:00

3.4 KiB

Publishing to the blog

The blog is Payload CMS, running inside the Next.js app. There is no separate service and no second deploy — writing a post is done in the browser and takes effect on the live site within seconds.

Signing in

https://www.schoolcompare.co.uk/admin — one account, no registration. If you need the account seeded on a fresh environment, run against the container:

npx payload create-first-user

Staging has its own admin panel, its own database and its own credentials at https://stx.schoolcompare.co.uk/admin. Never reuse production's secret or password there.

Writing a post

Posts → Create New. The fields:

Field Notes
Title The <h1> and the browser tab.
Slug The URL segment, in the sidebar. Never change it after publishing — it is the canonical URL, and changing it breaks every existing link and discards the page's accumulated search signal.
Published at The date shown on the post and in the feed.
Excerpt Max 200 characters. Shown on the index and used as the meta description, so write it as a standalone sentence rather than a teaser.
Hero image Optional. Becomes the social share image; without one, the site's generated card is used.
Content Rich text. / inserts a block.

Save as draft while you're working — drafts are not public. Publish when it's ready.

The callout block

One custom block, Callout, with two tones:

  • Caveat — what a number does not show. This is the one that matters: it is how a post states a limitation in context rather than burying it in a closing paragraph.
  • Note — a useful aside.

Images

Every image requires alt text; the editor will not let you save without it. Uploads go to a Docker volume on the host, which is backed up separately from Postgres — an image is not reproducible from the pipeline the way school data is.

How publishing reaches the live site

  • /blog, /blog/rss.xml and /content-sitemap.xml are rendered per request, so a new post appears immediately.
  • /blog/[slug] is cached after its first request. Publishing or editing fires a revalidatePath from the collection's afterChange hook, which drops that cached copy — so edits appear immediately too.

If a change doesn't show, it is far more likely the post is still a draft than that the cache is stale.

House style

These rules are why the blog exists. A post that ignores them makes the site read more machine-generated, not less.

  • First person singular. "I built", "I found" — never "we provide".
  • Concrete over general. "When we were looking at schools in Wandsworth" beats any amount of stated warmth.
  • State limits before someone else finds them. Every post that presents a metric says what it does not show. This is the single strongest signal that a human wrote it: generated content does not volunteer its own weaknesses.
  • No mission statements, no "passionate about", no invented team. There is one person here.
  • Short sentences.
  • Never publish a surname, an employer, or a child's name. The site's author is "Tudor". See /about.
  • Never invent a figure, even illustratively. On a site whose whole proposition is official data, a made-up number attached to a real school is the one thing it cannot do — and no illustrative intent survives being screenshotted.