What We Didn’t Build: The Deliberate Omissions Behind an AI Website Editor
By Zahi Raifer
Architecture write-ups usually describe what a system has. This one goes the other way. When I look at the AI website editor we run for small businesses, most of what keeps it stable is what we left out. Each omission removes a whole category of bug, and each one has a price.
Here are eleven of them, grouped by where they sit.
In the edit loop
1. The model doesn’t write files.
The planner (Gemini, in our own chat) never produces a file. It produces a list of operations from a fixed grammar of 14 verbs, such as set_text, add_class, move_after or remove. Each operation points at an element by a short id like [data-ai-id='a0f0']. Python applies the list with BeautifulSoup and is the only thing that touches disk.
What we gave up: the model can’t do anything outside the grammar in one step. What we got: a change that can be validated, logged and replayed before it lands, and edits that stay small. Across 24 benchmark runs, the planner’s output had a median of 141 tokens.
2. No regenerating the site on every request.
A new site is drafted once: the model returns a complete page of HTML and Tailwind in a single pass. After that it’s only edited. The first edit commits that original version as a baseline.
What we gave up: the “just make it again” button. What we got: the owner’s site doesn’t drift each time they ask for something.
3. No summary of the page by default.
It would be natural to hand the model a tidy JSON description of the page. We benchmarked that against raw HTML on real edit requests. Raw HTML gave the same understanding at about half the tokens, and the model sees every element. A compact summary is used only automatically, for very large pages.
4. No open-ended loop.
The planner has 11 tools: reading another page, looking at a screenshot, reading a page the owner links to (with their consent), and so on. All of it runs within a fixed budget of six model calls. An edit call ends the turn.
In how state is owned
5. No second copy of the conversation.
The web app sends the editing service only the latest message. The editing service keeps the one transcript. Nothing has to be synchronized because nothing is duplicated.
6. The web app never runs Git.
Each project’s history belongs to the editing service. When the web app needs to write files itself, it takes the project lock, writes, and asks the editing service to commit. That gives us one authoritative history per site.
7. No concurrent writers.
One lock in Redis covers a whole turn, whoever is writing: an edit, a manual commit, a site copy or a background job. Two changes can’t interleave on the same site. The cost is that a second writer waits. For one small-business website, that’s an easy trade.
At the edges
8. Our AI connector has no database write access and no session memory.
Owners can also edit their site from their own Claude through a remote MCP server. That server holds no write credentials. It calls the editing service and the web app over HTTP, and it reads one read-only view to identify the caller. It keeps no state between calls, so any instance can serve any request, and a restart costs at most the one request in flight. An outside model goes through the same 14 operations, the same checks and the same commit as ours.
9. Web requests never wait on a model.
Edit turns and other long work go through job queues backed by Redis. A slow model call can’t tie up the site.
In operations
10. No hand edits on production servers.
Code is pushed to the repository, pulled into a development environment that has its own site files, verified there, and promoted from an admin dashboard. The dashboard records every release and keeps a backup of what it replaced. Production runs on two identical servers with one active, and a release goes to the standby first. Rolling back means moving traffic back.
11. No trusting version labels.
A release is confirmed by hashing the files that are actually running. A label says what we meant to deploy. A hash says what’s there.
Is “omission” just another word for guardrail?
Partly. The system also has positive checks. Every save passes deterministic rules: a live page can’t be emptied, moved text keeps every sentence, the business logo and the contact form’s privacy notice are protected, and a claim of success needs a real change. Each rule is pinned by a replay test.
But I’ve come to think the omissions do more of the work. A check catches a bad outcome after something tried to cause it. An omission means nothing can try. You don’t need a guard against the web app corrupting Git history if the web app has no way to run Git. You don’t need conflict resolution if there’s only ever one writer.
How to find your own omissions
This is my own takeaway, and it isn’t specific to website editors. If you’re putting a language model in front of something customers care about, a short exercise helps:
- List every process that can change the thing you’re protecting.
- For each one, ask whether it really needs that ability, or whether it could hand a request to the one component that does.
- For each piece of state, name exactly one owner. If you can’t, that’s where your next incident will come from.
- Write the omissions down as rules (“the model never writes files”, “the web app never runs Git”) so a later change has to break them on purpose rather than by accident.
The list you end up with will look less impressive than a feature list. In my experience it’s the more useful one to keep.
If you want to see the editor these omissions sit behind, it’s at https://www.orivox.pro/
Disclosure: I’m the founder of Orivox, and this post describes our own system. AI tools helped me draft and edit it, and I checked every technical claim against our own architecture notes.