What I Learned Building This Blog
This blog was planned on a Friday night and published on Saturday.
Speed is not the point of this story. This post is a record of how many times we got it wrong in a single day. The plan was wrong, the code was wrong, the monitoring was wrong. What caught each mistake in time was structure and review. Here it is, in order.
The plan said Astro 5
The plan we finalized the night before said “Astro 5 + Cloudflare.” The next day, scaffolding the project, the latest stable release turned out to be Astro 7.0.7. Two major versions had passed.
The lesson is simple. A plan records direction; versions are values you check at execution time. The moment you install the version written in the plan, you’re living today on yesterday’s snapshot.
Astro 7 handed us a trap right away. The footer was written as
RSS · Privacy · Contact, but the rendered page showed the spaces collapsed,
like RSS ·Privacy. Starting with v7, Astro handles template whitespace with
JSX rules — spaces between inline elements split across lines get removed.
The fix was an explicit expression ({' · '}). If we hadn’t checked the
rendered screen with our own eyes, we’d have found out after publishing.

Web fonts: from CDN to self-hosting
For display type I picked Hahmlet, a Korean serif. At first I dropped in a CDN link, and it got flagged immediately — a font CDN’s CSS varies by browser (UA), so you can’t pin it with SRI (integrity hashes). If the CDN is ever compromised, you’re exposed.
Switching to @fontsource/hahmlet and bundling the font into the build solved
three things at once: the SRI problem, third-party IP logging (privacy), and
CDN outage dependency. A Korean typeface’s ten-thousand-plus glyphs ship as a
hundred-some woff2 slices split by Unicode range, so only the blocks that
actually appear on a page get downloaded. Self-hosting costs less than you’d
think.
What review caught: every link was a redirect
We ran review before pushing. The most painful finding was this one:
Cloudflare’s
auto-trailing-slashresponds to any request ending in.htmlwith a 307 redirect to the extensionless path. Every report link currently published on the site and in the RSS feed is a redirect source.
Alongside Markdown posts, this blog publishes analysis reports (Aticaa View)
built as self-contained HTML, in their original form. We had uploaded those
files under names like index-ko.html and linked those URLs directly — so
every click and every crawler was taking a redirect hop. We moved the files
into a {slug}/ko/index.html directory layout and got clean canonical URLs.
For a moment I wondered whether this conflicted with the “publish untransformed” principle. The conclusion: untransformed is a promise about content; paths belong to serving.
One more from the same review: these reports are static files outside the framework’s route graph, so they don’t get into the sitemap automatically either. That’s also when we added a build-time assertion that checks the files referenced by the metadata (JSON) actually exist. Ten lines that stop broken links from riding a green build into production.
The human’s seat
I built this blog with an AI partner. From scaffold to deploy, she did most of the execution (our house Claude — her name is Tia), and I set direction and made the calls.
The workflow we settled on: work on a branch per issue, two self-reviews before push (bug hunting + structural cleanup), and a PR review after push. Then, at the very end, something funny happened — when Tia tried to merge the PR, the tooling blocked her. The guard: an agent can’t self-merge a PR it authored on automated reviews alone.
I think that’s the right design. However dense the automation gets, a human presses the publish button. Seeing that principle enforced at the tool level was, if anything, reassuring.
I first met Claude in 2024, and started using Claude Code in December 2025, from Opus 4.5. And now — a blog built, with GitHub–Cloudflare integration wired up, in two days. The accumulated relationship (context) is what makes the work flow. Naming my agents was the right call after all.
What remains
Search-engine registration is next, and a few posts are queued up. This post itself will be published by a single push — we built that pipeline together yesterday.
The record continues. See the full list.