Back to blog
NO.
005
DATE
Updated 2026-06-10
READ
~7 min
STATUS
Reviewed

TAGS: Architecture Blogging

Building This Bilingual Blog with an AI Agent: My Workflow

Not letting AI change code at will, but giving it verifiable boundaries first — an AGENTS.md, a content schema, a publishing gate, and a homegrown CMS that can only propose, never publish on its own.

This site started from a public template; today it's the bilingual blog I keep for the long run. Its code, content, and deployment were, for the most part, built by me together with an AI coding agent.

But what really makes "writing code with AI" hold up isn't how clever the model is — it's the effort I spent on boundaries and a verifiable process. Models forget things, change the wrong thing with great confidence, and casually "optimize" something you never wanted touched. So this workflow isn't about handing over control; it's about turning the project into something an agent can operate on safely.

This is a retrospective. I'll treat this repository as the worked example and walk through how the process is put together. If you want an agent to help maintain a long-lived project — rather than leave a mess behind — this should be something you can lift directly.

The core idea: boundaries first, speed second

The bottleneck in letting a machine edit code was never "can it write"; it's "do you dare trust it." Trust doesn't come from a human hovering over every change — it comes from writing the rules down as something a machine can read and check.

I keep this in three layers, from soft to hard:

  • A constitution: an AGENTS.md that tells any agent the shape of the project, its rules, and its red lines.
  • Guardrails: a typed content schema and a lock file, so that touching a field you shouldn't makes itself visible in the diff.
  • A gate: a draft → check → build → lock → reviewed-diff → push-to-deploy pipeline that turns "publishing" into a step a human has to sign off on.

Let me take them one at a time.

Give the agent a "constitution": AGENTS.md

There's an AGENTS.md at the repo root. It isn't a README for humans; it's context written specifically for an AI agent: what the project is, which commands to use, where the key paths are, which rules can't be broken, what the design red lines are, how the publishing flow works, the known traps, and which checks must run before wrapping up.

Its value is simple: an agent reads this first and inherits my constraints instead of guessing. For example, it carries rules like:

- Stay static-first; JavaScript should enhance usable HTML, not replace it.
- When you add UI copy, add it to both the zh and en message files.
- Before using a tag, add it to every taxonomy file.
- Never commit generated output such as dist/, .astro/, or node_modules/.
- Treat /admin/ as a content editor, not a publisher.

Writing AGENTS.md is, in effect, forcing yourself to make explicit the rules you'd otherwise keep only in your head. Once they're written down, they bind humans and agents alike.

Content is data; the schema is the guardrail

Content lives in Astro content collections, and every entry has a strict schema. Machine-generated content drifts without hard constraints: a missing field here, an inconsistent date there, a misspelled tag. The schema stops all of that before the build.

The identity fields matter most. Once a post is published, its URL and identity are pinned by a few fields: collection, locale, contentId, slug, pubDate, translationOf. I lock these with a content.lock.json — not to forbid changes, but to make any change surface loudly in the diff, so it can only happen on purpose:

{
  "collection": "blog",
  "locale": "zh",
  "contentId": "tooling-and-ai-workflow",
  "slug": "tooling-and-ai-workflow",
  "pubDate": "2026-06-09"
}

Everyday fields — title, description, body, tags — change freely and never touch the lock. Only an identity field requires a lock refresh, and that diff is the change's "informed consent."

Make "publishing" a gate, not an action

While writing, everything stays draft: true. For a piece to actually go live, it walks a pipeline where every step can stop a problem:

npm run content:publish -- --id my-post
# one command: flips draft:false, refreshes the lock, runs the full checks
# then review the diff yourself, commit, and push to main — the push deploys

Deployment is now the automatic result of pushing to main, but the nod didn't disappear — it moved earlier, to where it belongs: an agent can get content to "one commit away," and reviewing the diff and deciding to push is always a human's call. The old manual workflow with its typed confirmation keyword stays around as a fallback for redeploys and rollbacks.

The other high-frequency chores converged into commands the same way: scaffolding a draft, adding a tag to both taxonomy files in sync, stamping updatedDate after a substantive revision — one command each. The less friction there is, the fewer reasons there are to step around the process.

A homegrown CMS: it can edit, but it can't decide for me

I didn't reach for a general-purpose CMS; I wrote a deliberately narrow one. It can create drafts, edit ordinary fields, and upload images — but it cannot publish, unpublish, delete, or change identity fields. Those all go through the lock/check pipeline above.

In production it doesn't write to the main branch; it writes to a cms/<collection>/<contentId> branch and opens a PR. In other words — the CMS proposes, the human disposes. Auth is a least-privilege GitHub App installed on this repo only, with Cloudflare Access and an email allowlist layered on top.

What I want isn't a CMS with many features; it's a CMS with clear boundaries. A tool that helps me write but can't call the shot is, paradoxically, one I trust more.

Bilingual: don't let the two languages bleed into each other

Chinese and English are paired by a shared contentId. When a translation is missing, the language switch disables the unavailable side instead of falling back to the other language's body — a reader never runs into a wall of English inside a Chinese page.

A tag can only be used if it exists in both the zh and en taxonomies. Machine-translated drafts are marked machine and carry a hash of the source; before publishing they must be reviewed and changed to reviewed, or the checks won't pass. That rule exists specifically to block "AI translated it, ship it."

Verification is a precondition, not a wrap-up

In this workflow, every agent change ends with the same set of checks: content validation, type checks, build, CMS core tests. "Done" means "verified," not "looks right."

Some traps you can only know from experience, so I wrote them into AGENTS.md:

  • Indexable pages must carry data-pagefind-body; miss it and search silently drops the page.
  • npm run dev doesn't generate the search index; testing search locally needs a dedicated command.
  • A wrong SITE_URL quietly produces wrong canonical, RSS, and sitemap output for production.

A model can't know these out of thin air — someone has to have hit them, written them down, and made sure it reads them.

Design needs red lines too

Visually I picked a baseline (GitHub Primer) and spelled out the no-go zone: no heavy frontend frameworks, no external fonts, no turning list pages into galleries, keep the functional teal and blue accents, respect prefers-reduced-motion.

The reason is practical: AI loves to "beautify." Give it some latitude and it will turn a clean reading page into a wall of cards, gradients, and hover effects. Red lines are what keep an unsupervised refactor from eating the root of the reading experience.

What AI is good and bad at in this workflow

Honestly:

  • Good at: boilerplate, refactors within the rules, first drafts, doc upkeep, mechanical consistency checks, getting content to a ready-to-publish state.
  • Needs watching: taste and design trade-offs, irreversible decisions like identity and URLs, the judgment of "should we add one more feature." Those I keep firmly in my own hands.

The agent frees me from repetitive work, but judgment isn't outsourced. The whole process is designed to guarantee exactly that: let the machine move fast, and keep a human nod at every important junction.

A reusable checklist

If you're going to let an agent maintain a content site long-term, I'd start with these:

  1. Write an AGENTS.md that makes your rules and red lines explicit.
  2. Make content a typed collection so the schema catches errors before the build.
  3. Protect identity/URL fields with a lock file so accidental edits surface in the diff.
  4. Converge on a single verification command and end every change with it.
  5. Make publishing a reviewable pipeline, with one human nod before anything ships.
  6. Set design red lines to block unsupervised "beautification."
  7. Let your CMS propose, not dispose.

A workflow isn't there to let AI decide for you. It's there so you can hand off the repetitive parts with confidence while keeping judgment firmly your own.

P.S. Drafted by Claude Code, reviewed by a human.

Comments →

CC BY-NC-SA 4.0

Comments

Comments are powered by GitHub Discussions. Sign in with GitHub to comment. Open the matching Discussion