Spec 0001: Core Publishing Site
Overview
Build a Git-native, Markdown-based publishing site for The Long Run using Astro and Cloudflare Pages.
Goals
- Establish Git + Markdown as the source of truth for all content
- Deploy a minimal, editorial-focused static site to
thelongrun.work - Support AI-assisted writing without AI-controlled publishing
- Create a system that works unchanged for 5+ years
Non-Goals
- Auto-publishing to third-party platforms (Medium, LinkedIn)
- Self-hosted databases or persistent backend services
- AI publishing without human review
- Comments or analytics (can be added later)
Requirements
R1: Repository Structure
Create the following structure:
thelongrun-site/
├── content/
│ ├── essays/ # Long-form articles
│ ├── notes/ # Short posts (future)
│ └── pages/
│ └── about.md
├── assets/
│ ├── avatar.svg
│ └── images/
├── src/ # Astro source
│ ├── layouts/
│ ├── components/
│ └── styles/
├── public/
│ └── favicon.png
├── scripts/ # Build/prepare scripts
├── astro.config.mjs
├── package.json
├── tsconfig.json
└── README.md
Rationale: content/ is editorial (human-readable, AI-editable). src/ is presentation logic. No mixing of concerns.
R2: Content Format
All essays use YAML frontmatter:
---
id: "001"
title: "When does the desktop—or IDE—stop being the centre of development?"
subtitle: "Exploring long-running agents, async work, and why the IDE still matters."
date: 2025-01-15
status: draft | review | published
canonical: https://thelongrun.work/essays/desktop-ide-centre
tags:
- software
- work
- ai
---
<markdown body>Rules:
canonicalalways points to the Cloudflare Pages URL- Only
status: publishedcontent is rendered publicly - Drafts must never appear on the public site
idis a sequential string identifier for cross-referencing
R3: Static Site Behavior
Pages:
- Home (
/): List of published essays, reverse chronological - Essay pages (
/essays/{slug}): Title, subtitle, body, date, canonical meta - About (
/about): Static page fromcontent/pages/about.md
SEO Requirements:
<link rel="canonical">set on all pages- OpenGraph metadata derived from frontmatter (
og:title,og:description,og:url) - RSS feed (
/rss.xml) generated from published essays
Design:
- Minimal typography, editorial tone
- No comments, no analytics
- Mobile responsive
R4: Build and Deployment
Technology:
- Astro (latest stable)
- Node.js LTS
Build:
npm run buildproducesdist/npm run devfor local development- Only
status: publishedessays appear in production build
Cloudflare Pages:
- Build command:
npm run build - Output directory:
dist/ - Deploy on every push to
main - Preview builds for PRs
- Domain:
thelongrun.workwith HTTPS enforced
R5: Editorial Workflow
draft → review → published
Rules:
- Only humans change
status - Publishing requires: clean local build + manual review
- Git commit history is the editorial record
AI Guardrails:
- AI may: draft outlines, rewrite paragraphs, improve clarity, suggest titles
- AI must not: change status to
published, edit build config, commit to main without review
Technical Implementation
Astro Configuration
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://thelongrun.work',
output: 'static',
});Content Collections
Use Astro’s content collections for type-safe content:
// src/content/config.ts
import { z, defineCollection } from 'astro:content';
const essays = defineCollection({
type: 'content',
schema: z.object({
id: z.string(),
title: z.string(),
subtitle: z.string().optional(),
date: z.date(),
status: z.enum(['draft', 'review', 'published']),
canonical: z.string().url(),
tags: z.array(z.string()).default([]),
}),
});
export const collections = { essays };Draft Filtering
Filter content at build time, not runtime:
// In page generation
const publishedEssays = await getCollection('essays', ({ data }) => {
return data.status === 'published';
});Acceptance Criteria
- Site builds successfully -
npm run buildcompletes without errors - Home page works - Lists published essays in reverse chronological order
- Essay pages work - Individual essay URLs render correctly
- Drafts are hidden - Essays with
status: draftorstatus: reviewdo not appear - Canonical links correct -
<link rel="canonical">matches frontmatter - RSS feed works -
/rss.xmlcontains published essays - Cloudflare deploys - Push to main triggers successful deployment
- Preview builds work - PRs generate preview URLs
Out of Scope
- Platform publishing scripts (see Spec 0002)
- n8n automation
- AI chat integration
- Analytics or comments
Design Philosophy
Build this as if it must still work, unchanged, in five years.
- Prefer clarity over cleverness
- Prefer boring, proven defaults
- Prefer human review over automation
- Treat writing as long-lived artifacts