← All articles Agent infrastructure

It worked flawlessly in Cursor. Then you shipped it.

OBTO Team · Insights from the Glass Box

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.

  1. Name your jobs. Run the demo once more and write down every intervention you make, however small. That list is your real spec.
  2. Shrink the tools. List every verb the agent can call. Delete any it doesn't strictly need. The worst case is the surface area.
  3. Decide per tool: allow, refuse, or require-a-human. Put that decision on the server, not in the prompt.
  4. Kill a run mid-write and retry it. Count what landed. If the answer is ever "two," you're not ready.
  5. 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.

Frequently asked questions

Why does my AI agent work locally but fail in production?

Locally you approve each step, run one task at a time, and use your own credentials, so you silently catch and correct the agent's mistakes. Production removes you: it runs unattended, concurrently, against real systems. The model is identical; the conditions are not.

Is a better prompt enough to make an AI agent production-ready?

No. A better prompt improves reliability at the margin, but it can't enforce anything. The safety you provided in the demo has to be replaced structurally: scoped tools, a server-side policy that can refuse an action, approval gates on irreversible steps, and idempotent, receipted writes.

What changes when an AI agent runs unattended?

There is no human to catch a wrong action before it lands. A mistake you'd have vetoed in Cursor just completes. Unattended runs also face concurrency, retries, and at-least-once delivery that a single-user demo never exercises.

How do you safely give an AI agent write access to production?

Give it narrowly scoped tools instead of a god-mode key, enforce an allow/refuse/require-a-human policy on the server between the agent and the action, gate irreversible operations on human approval, make writes idempotent, and record every attempt in an auditable receipt.

Ship the agent, not the anxiety

Scoped tools, a server-side policy, and a per-run Glass Receipt — the safety layer that replaces you, by default.

Get started

More from the OBTO blog