It worked flawlessly in Cursor. Then you shipped it.
You built it in an afternoon. In Cursor, the agent read the ticket, looked up the account, updated the record, and posted the reply. You ran it a dozen times and it was clean every time. You demoed it. Everyone nodded. You shipped it.
Then Monday happened. The same agent, same prompt, same model, quietly did the wrong thing to a real customer at 2am — and nobody was watching.
If you have lived this, the first thing worth saying is: you are not crazy, and you did not build it wrong. The demo was real. It just wasn't telling you the truth about production.
The demo didn't lie about your model. It lied about the conditions.
Here is the uncomfortable part. When it worked in Cursor, you assumed the model was the thing making it work. It wasn't. You were.
Every time you ran that agent by hand, you were doing a stack of jobs you never named. You read each step before it executed and killed the run when a tool call looked wrong. You went one task at a time, so nothing ever collided. You used your own credentials on your own data, so the blast radius was you. And when something misfired, you noticed instantly, because you were sitting right there watching the trace scroll by.
You weren't the operator of the safety system. You were the safety system. You were the guardrail, the reviewer, the rate limiter, and the rollback — all at once, for free, without noticing you were doing any of it.
Production's entire job is to remove you from that seat. It runs unattended, so no one reads the step before it executes. It runs concurrently, so tasks collide. It uses real credentials against real systems, so the blast radius is everyone. And when it misfires at 2am, the trace scrolls by to an empty room.
"But it's the exact same model"
Right. That's the point. The model was never the variable. If the identical agent behaves well with you in the loop and badly without you, then the thing that was holding it together was the loop — you — not the weights. Swapping in a smarter model doesn't give you back the four jobs you were quietly doing. It just does the wrong thing more articulately.
This is the same demo-to-production gap every system has ever had. What makes agents worse is that they are non-deterministic and they take actions. A flaky script fails loudly and stops. A flaky agent improvises, and sometimes the improvisation writes to your database.
What has to replace you
So the real migration isn't "make the agent smarter." It's "replace the four things I was silently doing." And none of them are a prompt. Each one is structural.
1. You caught the wrong action → scoped tools
In the demo you could veto anything. In production the agent should not be able to attempt most things. Don't hand it a god-mode API key and hope; hand it a small set of named tools. The narrower the surface, the smaller the worst case. An agent that can only call update_ticket_status cannot drop a table, no matter what it talks itself into.
2. You approved the risky step → a server-side policy
Some actions are fine to run freely; some should never run without a human. That decision cannot live in the prompt, because a prompt is a request the model can talk itself past. It has to live on the server, between the agent and the action: allow, refuse, or require-a-human. The model's intent is an input to that decision, never the final word. That's the whole argument in our piece on AI agent guardrails that actually hold.
A refusal should also leave something behind. Here is what one looks like in our own logs — an agent tried to patch a live page against a line that had changed since it last read the file, and the server said no:
{
"run_id": "run_4c19",
"tool": "patch_artifact",
"target": "pltf_page/pricing",
"decision": "refused",
"reason": "anchor_mismatch: line 141 no longer matches the live artifact",
"human_needed": false,
"retry_safe": true
}
Nothing to clean up, and a one-line answer to "what happened at 2am?" That's a guardrail that isn't a sentence in a prompt.
3. You ran one thing at a time → writes that survive being retried
The instant you go unattended and concurrent, you inherit at-least-once delivery: a network reply gets lost, the call is retried, and the same write lands twice. In Cursor you'd have seen the duplicate and undone it. In production it just becomes two of something. Writes have to be idempotent, and where they can't be, they need to reconcile. This one is not hypothetical for us: a dropped network reply retried an insert and published the same blog card twice on our own site last week — we wrote up the incident, receipts included, in the retry that wrote twice.
4. You watched it happen → a receipt you can read the next morning
The cheapest thing you did in the demo was simply being there. Unattended, you need that back as an artifact: a per-run ledger of every tool call, decision, and refusal, so the 2am run is readable at 9am. Without it, an unattended agent is a black box that occasionally moves money. It's why we run agents on a schedule behind a Glass Receipt, not a cron job and a prayer.
The before-you-ship runbook
Five checks, in order. Each one replaces a job you were doing by hand.
- Name your jobs. Run the demo once more and write down every intervention you make, however small. That list is your real spec.
- Shrink the tools. List every verb the agent can call. Delete any it doesn't strictly need. The worst case is the surface area.
- Decide per tool: allow, refuse, or require-a-human. Put that decision on the server, not in the prompt.
- Kill a run mid-write and retry it. Count what landed. If the answer is ever "two," you're not ready.
- Read the receipt of a failed run. If you can't reconstruct what happened from the log alone, neither will 9am-you after a 2am incident.
A fair caveat, because we're not selling you a miracle
A better prompt genuinely helps — clear instructions, good tool descriptions, a few examples. Do all of it. It just can't enforce anything: prompts move the average; structure sets the worst case. Production is a game about the worst case. We catalogued the ways these runs fall over in why AI agents break in production, and the broader trap in a prototype that feels like a product still isn't one.
Where OBTO actually fits
OBTO's whole reason to exist is the part after the demo: an agent gets direct write access to a live production system — no git branch, no CI, no human in the deploy loop — and what stands in for that missing review step is structural, not hopeful. A change is refused if it doesn't match what's actually live; an upload is refused if its bytes don't hash to what was promised. Scoped tools, the server-side policy, and the receipt come with the platform instead of being something you remember to build. Describe it, ship it, own it — including the 2am runs.
So the question to ask before you ship isn't "is my agent smart enough for production?" The demo already answered that; it was smart enough in Cursor. The real question is quieter and more useful: what was I doing in that demo that production won't? Write those jobs down. Then go replace them, one structural piece at a time.