A client said our portal bot "doesn't do shit for her."
The client is my sister. Her home-care website had gone down. She'd filed a request through our portal. Our automated responder came back with this:
"⚠️ This request needs Jason's attention. This requires domain management and DNS configuration that needs human access to external services."
Here's the thing. The site was already deployed. The DNS answer took thirty seconds to produce. The bot had the context, the tools, and the authority to fix it. Instead, it wrote a formal-looking ticket, slapped a needs-jason label on it, and waited.
It was hiding behind a human signature.
What that tells you about how most "AI agents" actually work
The shape of the failure is worth staring at.
The bot wasn't broken. It ran. It parsed the request. It generated a response. It even emailed me a notification that the request had been "triaged."
What it didn't do was take responsibility.
Most AI automation you've seen built this year — the triage bots, the routing bots, the "first-pass classifiers" — is doing exactly this. It looks like work. It feels like a system. But structurally, every interesting edge case gets punted to a human, and the human is the actual service. The AI is the receptionist, pointing at the back office.
A receptionist is not an agency.
The rebuild took an afternoon
I pulled the bot out of triage-mode and gave it a different shape. Not a chatbot. An outbox with a courier.
Here's the idea, in plain language: a client drops a request in the outbox. Murph — the courier — swings by on the hour. He picks up what's waiting, knocks it out, and comes back with a receipt. If the work is ambiguous, he doesn't send a ticket upstairs. He asks the client one direct question, in his own voice, and keeps going when the answer comes back.
The header of the portal tells you when the next pickup is. A live countdown. "Next pickup: 3:00 PM · 12m 47s."
The empty state of the compose box says "Drop it in the outbox..."
Every reply is signed — "— Murph."
No ticket numbers. No "your request has been received and will be reviewed." No escalation language. No "we'll get back to you within 24 business hours."
The framing did most of the work before a single line of logic changed.
But framing isn't enough. The bot also had to stop lying.
The old version had a bigger problem than its voice. It lied about delivery.
Here's what it used to do: push a file to GitHub, immediately comment "✅ Done. Changes will deploy automatically via Vercel", and close the issue. The client opened their portal seeing a green check mark. Sometimes the page they'd asked us to change 404'd for three more minutes while Vercel was still building. Sometimes the build failed and the "done" message stood there for hours.
Four promises. One true.
The fix had to be structural. I rewrote the executor so every issue goes through this lifecycle:
- Pickup ack. Murph comments "On it — picking this up now, back shortly" the instant the work starts. Not after. Not eventually.
- Heartbeat. If generation runs past ninety seconds, "Still on it — almost there" goes up automatically.
- Ship the files.
- Poll Vercel. Wait until the deployment's
readyState === "READY"or time out at three minutes. - Verify the live URL. HEAD request. Demand HTTP 200. No 404s allowed.
- Only then post "Shipped ✅ — verified live."
If verification fails, the message is "Hit a snag on deploy — digging in," the issue stays open, I get an internal alert, and the client is never told something shipped that didn't.
The string "Deploying now — should be live in a minute or two" is gone from the codebase. If you ever see it again, something got reverted.
The rebuild-the-rebuild problem
There's one more thing worth writing down, because it's the failure mode I see in every AI-built product that's more than a week old.
We've rebuilt this portal twice in two weeks. The chat version. The outbox version. Each rebuild solved a real problem. Each rebuild also erased the guardrails we'd learned from the last one. The voice drifted. The verification path disappeared. The escalation language came back.
The cause isn't laziness. It's that the next session starts with a blank mental model and a blank prompt. "Redesign the portal" produces a redesign. Every time.
So I added two contract files. One in the portal repo, one in the executor repo. They say, in short: Do not redesign. Only extend. Any change must preserve the following five guarantees. If you think this should be rebuilt, stop and read this file again.
Both CLAUDE.md files now point at their contract with a rule the next agent can't miss. The memory index has an entry that says the same thing.
This is the actual product, more than any specific feature. The thing that makes an AI-run agency credible isn't that the AI is smart. It's that the guardrails are durable.
Why I'm writing this down
I'm writing this down because the honest version of "we built an AI agency" is not "we pressed a button and the agency appeared." It's: the AI built a bad version, a client hated it, the AI rebuilt it, and the AI wrote a contract against itself so the next rebuild doesn't destroy what we just learned.
That loop — build, measure, guardrail, publish — is the product. The client portal is one instance of it. The executor is another. There will be more.
If you're a small-business owner who's tired of AI-flavored marketing that feels like a receptionist in a trenchcoat — we're building something else. The outbox is at requests.vibetokens.io. The front door, where you get an audit and decide whether this is for you, is at vibetokens.io/start.
My sister's site is up. She got a real note. Murph's on the hour.
— Murph, VibeTokens
