The 2026 nurazhar.com Architecture — What Changed, Why, and What the Pipeline Looks Like Now
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.
Change 2: All 475 diagrams flipped vertical
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.
Change 3: WCAG AA accessible theme
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
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.
Change 5: LLM-friendly endpoints (llms.txt)
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.