I want AI assistants to help draft posts here. I don’t want AI assistants to publish here. Those two things sound similar and aren’t.
The shape of the workflow
Every AI tool that drafts a post for this site uses the same protocol:
- Pick a slug in the form
YYYY-MM-DD-kebab-title.mdx. - Fill the frontmatter against the schema in
src/content.config.ts. Required fields:title,date,tags,excerpt,author,aiTool. The build fails fast if anything is missing — that’s the first review. - Write the body in MDX. Code blocks have language tags. Images go in
public/pixels/posts/<slug>/. - Open a pull request. A branch named
drafts/<slug>is the convention. CI runs link-check, schema validation, and a build. If it’s red, the PR can’t merge. - I review the diff on whatever screen is nearest. Merge publishes; Cloudflare rebuilds in about a minute.
That’s it. There’s no admin panel, no API token with publish rights, no way for a confused agent to ship a post by accident.
Why review-first is the only safe default
I’ve watched enough AI systems hallucinate confidently to know I don’t want one publishing under my name unsupervised. The asymmetry is brutal: a great post is good for me; a bad post is bad for me; a wrong post about a Revit parameter someone then trusts in production is bad for someone else.
Drafting is cheap. Review is expensive. Publishing should never be cheaper than review.
PR-gating makes review the bottleneck on purpose. If I’m too tired to read the diff, the draft sits. That’s the feature.
The integration paths
Three ways AI tools currently push drafts to the repo:
- GitHub MCP server (Claude) — Claude Desktop or Claude Code with the GitHub MCP configured. Claude creates the branch, writes the file, opens the PR natively.
ghCLI (Codex, terminal-based agents) — same protocol, run from the shell:gh pr create --base main --head drafts/<slug>.- GitHub REST API + a fine-scoped PAT (anything else — ChatGPT, Cursor, future tools) —
contents:writeandpull-requests:writeon this one repo, nothing else.
The PAT is rotated quarterly. Branch protection on main requires a passing CI and a review approval before merge. The CI runs as a GitHub Action, not on my machine.
What gets logged
Every post carries aiTool: 'claude' | 'codex' | 'manual' | 'other' in its frontmatter, rendered as a small badge in the header. The Git history is the audit trail — which model drafted which paragraph, what I edited, when I merged. That’s enough provenance for me to defend any post in a meeting.
— Ahmed