# 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. ## Before any of this: the flags `/blog` and `/about` are behind feature flags (`blog` and `about_page`), and every flag in this system starts off. While `blog` is dark, `/blog`, every post page and the RSS feed return 404, and neither appears in the sitemap or the footer. Posts still save normally, because `/admin` is deliberately **not** flagged: you have to be able to write a post before there is anything worth switching on. So a new post published to a dark blog is invisible, and that is working as intended, not a bug. Flip the flag in Unleash when the content is ready. Staging and production hold their own values (`development` and `production` environments), so you can light it on staging first. The page follows a flip within five minutes; the footer link takes up to a week, because it renders in the root layout and is cached at the same weekly floor as the school corpus. Both flags are temporary scaffolding, like every flag here: a test starts failing once one is older than 90 days, at which point either the feature is permanent and the flag comes out, or it was never going to ship. ## 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: ```bash 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 `

` 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. - **No em dashes.** They are one of the clearest tells of machine-written prose, which is the whole problem this blog exists to fix. A full stop, a colon, a semicolon or a pair of commas does the job and reads as though a person chose it. - **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.