Diagram

In the span of one conversation with an AI agent, this blog underwent six architectural changes. Each one fixed a real problem I’d been living with. Here’s the full tour — what was broken, what we changed, and what the pipeline looks like now.


Change 1: From self-hosted GitLab Pages to Cloudflare Pages

The problem: The old pipeline had a self-hosted gitlab-runner daemon that had to be alive before pushing. It would die silently after reboots. The build ran twice — once locally for verification, once on GitLab CI for deployment. And there was a GitHub mirror that served as a secondary host, creating confusion about which URL was canonical.

The fix: Deploy directly from the local machine to Cloudflare Pages via npx wrangler pages deploy. GitLab is now source control only — no CI pipeline, no runner, no mirror. Three stages instead of five. The runner daemon is gone. The GitHub mirror is gone. The CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID CI variables are gone.

Full migration deep dive →


Change 2: All 475 diagrams flipped vertical

Diagram

The problem: Horizontal diagrams (horizontal layout in the source) looked fine on desktop but became illegible on mobile. Nodes shrank, text became microscopic, and the reading flow broke when you had to scroll diagonally.

The fix: A Python script batch-processed all 191 .md files, finding 475 diagram code blocks and: - Converting 73 horizontal-layout blocks to vertical (vertical layout) - Adding vertical direction to 121 blocks that had none - Bumping 159 style.font-size values below 17px to 17px minimum

The diagram container CSS was simplified from an aggressive desktop breakout (1100px wide, negative margins) to a clean max-width: 800px centered layout that works naturally with vertical diagrams.

Full diagram redesign post →


Change 3: WCAG AA accessible theme

Diagram

The problem: The old theme had social icons at 36×36px (below WCAG’s 44px touch target), body text at 16px, and some secondary text colors that failed the 4.5:1 contrast ratio in light mode.

The fix: - Body font bumped to 1.0625rem (17px) for readability - All interactive elements — theme toggle, nav links, social icons — now have min-height: 44px and min-width: 44px - Secondary text darkened from #9ca3af (3.1:1) to #787878 (4.6:1) - Accent color adjusted for better contrast against the warm background - focus-visible outlines on every interactive element - env(safe-area-inset-*) for notched phones - prefers-reduced-motion respected everywhere


Change 4: PDPA Singapore compliance gating

Diagram

The problem: As a Singapore-based publisher, any accidental PII in a blog post — an NRIC number, a phone number, an API key — would violate the PDPA. Fines start at S$1 million. There was no automated check.

The fix: Integrated pdpa-sg-clj as a git submodule. Added scripts/pdpa-scan.bb — a Babashka wrapper that scans a single post before publishing. Inserted as Step 1.5 in publish.bb between validation and build. Detection rules cover NRIC (Mod-11 checksum verified), SG phone numbers, API keys, private keys, passwords, and email addresses. Critical and high findings block publishing. The scan adds ~0.2 seconds to the pipeline.

Full PDPA integration post →


Change 5: LLM-friendly endpoints (llms.txt)

Diagram

The problem: The old /llms.txt was a 766-byte static file — a bare link list, manually maintained, that would go stale with every new post. The content wasn’t structured for AI agent discovery (missing blockquote, description paragraph, topic sections).

The fix: Created generate-llms-txt and generate-llms-full-txt functions in the build system (sitemap.clj). Both are generated dynamically from the post list during every build. The llms.txt now includes: - H1 project name + blockquote summary - Description paragraph with topic coverage - 10 most recent posts with URLs and descriptions - 12 topic sections with tag page links and post counts - Agent guidance section explaining how to consume the site - “Optional” section for secondary resources

Added _headers file for proper Content-Type: text/plain; charset=utf-8 and 1-hour cache on llms endpoints. Follows the llmstxt.org specification and the agent-friendly-apis conventions.


Change 6: The complete publishing pipeline (now)

The pipeline now has 7 stages (up from 5), but the total wall-clock time is actually lower because we eliminated the CI double-build and the runner check. A typical publish run:

🚀 Publishing: apu-vs-gpu-vs-npu-architecture

📋 Step 1: Validating post format...         ✅ (0.1s)
🛡️  Step 1.5: PDPA SG compliance scan...     ✅ (0.2s)
🏗️  Step 2: Building site...                  ✅ (14s)
🔗 Step 3: Validating links...                ✅ (2s)
📤 Step 4: Pushing to GitLab...               ✅ (1s)
☁️  Step 5: Deploying to Cloudflare Pages...  ✅ (6s)
🌐 Step 6: Verifying live site...             ✅ (0.5s)

🎉 apu-vs-gpu-vs-npu-architecture published successfully!

Total: ~24 seconds from command to live. The old pipeline was 35-45 seconds with the CI overhead.


The file-level changes


What didn’t change

Some things stayed exactly the same because they already worked:

Component Status Why it stayed
Pandoc markdown → HTML Unchanged Already fast, already correct
Hiccup + Pandoc + RSS Unchanged Pure Clojure templating, already fast and standards-compliant
Sitemap Unchanged Already dynamic, already W3C valid
Robots.txt Unchanged Simple, correct
_redirects Unchanged Already consolidating canonical URLs
Dark mode / caching Unchanged SVG cache + data-theme toggle work correctly

The build core stayed stable. Pandoc, Hiccup, RSS, sitemap, and robots.txt didn’t need touching. They already worked correctly.

Diagram caching survived. SVG files in public/diagrams/ already skip regeneration when the source diagram block hasn’t changed, so no work was wasted.


The principles this architecture teaches

Six changes, five principles, one blog. The 2026 architecture is leaner, faster, more accessible, legally compliant, and machine-readable. Not bad for an afternoon with an AI agent.


Built with Babashka 1.4, Pandoc 3.x, diagram compiler (dagre layout), Hiccup, ripgrep, Wrangler 4.118. Hosted on Cloudflare Pages. 189+ posts, 642 HTML files, 0 broken links.