Contact
READING PROGRESS:0%
PRIMITIVE

My AI agents run a real business's back office. The only way anything reaches a customer is a token you can't type by accident.

The most important security control in my AI ops system is a rule that a plain “yes” doesn’t count.

Some background: I run the back office tooling for a small dance studio in Seattle. A crew of AI agents does real work there every day — they draft email campaigns, build website pages, analyze search traffic, research competitors, process photos, and file it all on a Trello board. When an agent finishes an email campaign and asks to send it, I can reply “send it,” “go,” “looks great,” or 👍 — and nothing sends. Those replies keep refining the draft. The only token that fires a live send is the 🔥 emoji itself.

That sounds like a gimmick. It’s actually the same reasoning behind two-person launch keys and molly-guards on server power switches: make the dangerous action impossible to trigger by habit, autocomplete, or misread intent. I say “yes” to an AI assistant forty times a day. I have never once typed 🔥 without meaning this specific thing goes to real customers, now. The approval is unusual on purpose — the weirdness is the feature.

The question nobody asks precisely

The debate about agent safety mostly happens in the abstract: are they aligned, are they trustworthy, how much should a human be in the loop. Running agents for a real business makes the question concrete, and concrete questions have architecture answers. Mine came down to one rule: nothing customer-facing goes out without a human seeing it first — and then a stack of boring, testable code that makes the rule structural instead of aspirational.

The stack has four layers, from the outside in.

Layer one: the chat front door can only propose. The agents live behind a Discord bot, and the model inside a chat turn can’t execute anything. It emits structured proposals; ordinary host code validates each one against a fixed action registry and executes it — or refuses. The registry has read actions and reversible internal writes, and that’s it. There is no send action, no publish action, no delete action to propose. You can’t talk the model into an action that doesn’t exist.

There’s a subtle rule in this layer I’ve come to love: once a turn ingests untrusted content — an email body, a fetched web page — the one action that could turn text into standing instructions is refused for the rest of that turn. A prompt injection buried in a Reddit post can be read, but it can’t become installed behavior. Provenance tracking, three lines of code.

Layer two: scheduled jobs run deny-by-default. The morning scanners that look for opportunities run under a hook that permits a short allowlist — read tools, writing one brief into one folder, filing suggestion cards through a pinned wrapper — and refuses everything else. Not “blocked from dangerous things.” Blocked from everything, then allowed a few.

Layer three: the tools themselves refuse. This is the layer I’d tattoo on every agent architecture: the guarantee is the code, not the prompt. The Mailchimp CLI refuses to send when the unattended environment flag is set, demands you pass back the exact recipient count as a confirmation, and will test-send only to a hardcoded allowlist of two people. The website CLI publishes to a staging subdomain by default; the live domain is a separate flag behind the same refusals. The Trello CLI simply has no delete command. An agent that goes fully off the rails hits tools that shrug.

Layer four: the 🔥. When work is genuinely ready — drafted, reviewed by an automated brand judge, staged — the agent posts a pre-send summary: exactly what goes out, to whom, how many recipients, what pressing the button changes. Then it waits for the one token. Attended only, no standing pre-approval, every single time.

Blast radius is a small-business concept too

Last week the system grew a fifth check, and the reason is instructive. Our website platform has no per-page publish — one publish ships everything staged, including work other sessions staged for other reasons. So now there’s a pre-publish check that refuses to proceed while any staged item lacks a recorded verdict from the review step. The first time it ran, it flagged five items that predated the convention. Five things that would have ridden along silently with the next publish, now named in the summary I approve.

That’s the pattern worth stealing: the check didn’t make the agent smarter. It made the button honest about what it does.

Honest caveats. This is one small business, not a benchmark. The system does have exactly one autonomous publish path — factual listings on family-activity directories, rendered only from pre-approved copy, with five hard stops that route anything surprising back to a human — so the dial isn’t binary, and deciding where to set it was itself the work. And yes, I’m the throughput bottleneck for everything customer-facing. That’s not a limitation of the design. That is the design.

The point was never distrust of the model. It’s that “human in the loop” only means something if the loop can’t be fat-fingered. Mine is a fire emoji, four layers down.

STAY IN THE LOOP