We let an AI write to production. Then a dropped reply wrote it twice.
There were two of them.
Same headline, same icon, same summary line, one on top of the other at the top of this blog. An article we published once had published itself twice. Nobody had touched the page. Nobody had clicked deploy, because on OBTO there is no deploy button to click. An agent had written the change, and it landed on the live site the way every change here lands: directly.
We let an AI agent write to production. Not to a branch. Not to a staging box a human later promotes. To the record that serves this page, to real visitors: no git push, no pull request, no CI run, no one reading the diff first. If that makes you wince, good. It made us wince too. Then we built the thing that lets us sleep.
The failure nobody warns you about
When people imagine an AI writing to production going wrong, they imagine the model doing something dumb: hallucinating a field, dropping a table, inventing a price. Real risks, and we guard them hard. But that is not what bit us.
The agent's content was correct. The card it wrote was exactly right. It just wrote it twice.
Here is what actually happened. The agent sent one write — an insert to add the new card to the blog index. It succeeded on the server. The reply confirming it got lost on the way back. From the agent's side the call looked like it failed, so it did what any client does with a failed network call: it retried. The second insert also succeeded. Now there were two.
Nothing here is exotic. It is the oldest law in distributed systems, with a boring name: at-least-once delivery. Any write that crosses a network can arrive while its acknowledgement vanishes. You don't notice for months, because the ack almost always comes back fine. Then one retry lands, and "we write it once" turns out to have been a story you told yourself.
What made it land in production, unreviewed, was our own design. We had removed the two things that normally catch this.
What we deleted when we deleted the humans
A normal deploy pipeline has two gates between a change and your users: a human reads the diff and asks "wait, why are there two cards?", and CI runs a check that would flag it. Both are slow. Both are exactly why nothing this dumb reaches most production sites.
We took both out. Not as a shortcut — it is the point of the platform: the agent describes the change and the change ships. (We wrote about why the runtime itself carries that weight in a piece on deleting the session.) Pulling the human out of the loop makes an agent useful at 3 a.m. It also removes the person who would have caught the duplicate.
So the real question is not "how do we get the human back." It is sharper: what did the human actually do at that gate, and can a machine do it every time without getting tired? Two things: check the change makes sense against what is really there, and that what got written was what was meant. Both can be made structural — a judgment call turned into a check the write itself must pass, one that never skips a Friday.
The turnstile, not the bouncer
Picture the old human reviewer as a bouncer: a bouncer decides, which is powerful and exactly what doesn't scale to a thousand small writes a day. So we replaced the bouncer with a turnstile. A turnstile decides nothing; it just won't turn unless your token matches. Two of them guard every write an agent makes on OBTO.
The first is on edits. When the agent patches a page it doesn't just say "change line 154." It says "change line 154, and the line there starts with <a class="post" href=." That expected text is an anchor. If the live line doesn't match it, the patch is refused before it touches anything — the state moved, the token no longer fits. Stale line numbers, the classic way an automated edit corrupts a file, simply cannot land.
The second is on new files. When the agent uploads a page it declares a sha256 — a fingerprint of the exact bytes it means to store. The server hashes what arrived and compares. If one byte differs, the write is rejected with sha256_mismatch. You cannot half-write a page: either the bytes are exactly what was promised, or nothing is stored.
Which is why an agent writing straight to production is less reckless than it sounds: the door is not open, and it only turns for a matching token.
The blind spot the duplicate found
So why did the duplicate get through two turnstiles?
Because a pure insert has a gap the anchor check can't see. When you insert a card above the top card, the anchor you match against is that card — and inserting above it doesn't change its text, only shifts it down. The anchor still matches on the retry, so the turnstile turns a second time, honestly: from its narrow view, nothing is wrong.
Anchor-matching protects you from writing against a changed world. It does nothing about writing the same thing twice into an unchanged one: necessary, but not sufficient for idempotency.
The fix is embarrassingly plain, which is usually the sign it's right: make the write check its own outcome. After every insert, the agent re-reads and counts — this slug must appear exactly twice, once as a card, once in the structured-data list.
// after every insert, verify the outcome
const n = countOccurrences(page, slug);
if (n !== 2) { // 1 card + 1 JSON-LD entry
// a retry doubled it: reconcile before build
removeExtras(slug, keep = 2);
}
If it appears three times, a retry doubled it; delete the extra before building. The check runs on every insert now — because "looked wrong" is precisely the judgment we cannot ask a machine to make.
That is idempotency the pragmatic way: not praying the network delivers exactly once, but making the write converge to the same end state however many times it runs. Read back, assert the count you expect, reconcile the difference. It is the instinct we lean on whenever an agent runs unattended on a schedule — assume the run will be retried, and design so a retry is safe.
Why this is the real work
We read it the other way. Where the page lives was never the interesting part — storing a page as data is decades old; every CMS does it. The work is letting a non-deterministic agent write to a live system with the humans removed, and making that safe with structure, not supervision: anchor-matched edits, hash-verified uploads, writes that check their own outcome, and guardrails in front of the action, not in the prompt.
None of that shows up in a demo. A demo is one happy write on a clean machine with someone watching. Production is the retry you didn't see, at the hour nobody is awake, against a page that already moved. The gap between those two is the whole job.
This article was published by an agent, with no git push and no human promoting a diff. The duplicate is why the last step of that sequence is now "read it back and count." We would rather tell you about the retry that bit us than pretend our writes were exactly-once. They never were. Yours aren't either. The only question is whether you designed for it.
If you are wiring an agent to something real, that is the part to get right first. See how we price it on our pricing page, or push a real write through the turnstiles yourself with the getting-started guide.