The assistant somebody actually uses, and the interlock I built before I let it press send.
What these are
FRIDAY is a Windows desktop assistant. It reads and sends mail, moves calendar events, keeps a task list, answers out loud, and can text or call when something needs attention. I built it for one person, my dad, and gave it to him, and he uses it. He cannot read a stack trace, so it had to work, and it had to ask before doing anything I would regret.
VERONICA is what I built before I was comfortable with that. She sits between an AI agent and every external system it can reach, holds each outgoing call until a human approves that exact call, and refuses it if anything changed between the approval and the wire.
The problem, stated once: everyone approves a description of what an agent is about to do, then lets the model perform the action. That gap is where prompt injection, scope drift and silent failure live. An agent's report and an agent's effect are different objects, and almost every guardrail in production governs the first one. I did not start with that thesis. I arrived at it by shipping something.
FRIDAY: build the assistant first
Gmail, Calendar and Tasks behind OAuth 2.0 with scoped consent and refresh-token handling. WebAuthn passkeys, so there is no password to lose. An interface assembled from Pydantic structured outputs rather than a model's prose, and speech-to-text on the machine rather than in someone else's datacentre. 57,723 lines and 579 tests, in five weeks.
Building for someone who depends on the thing surfaces problems a personal project never will. Every convenience I wanted to add hit the same wall:
To be useful, she needs to send the email, move the meeting, write the file. To be safe, every one of those has to be something I approved. And I cannot approve a sentence. I have to approve the actual call.
FRIDAY is frozen at v1.0-dad, deliberately. Not abandoned. It works, it is in use,
and the safety layer it surfaced was worth more than the next feature.
VERONICA: the interlock
A local MCP gateway where the call an agent executes is the object a human approved. Five capabilities, in dependency order. Three run, two are drawings, and the difference is marked because it is the distinction the project exists to enforce:
- Credential custody (designed, not built). Every credential-shaped value moves out of the agent's config into an OS-protected vault, in one atomic operation or none.
- Transparent proxy interposition (runs, stdio only). Protocol, not connectors, so every MCP server works with no per-server integration. HTTP and SSE are untested and nothing is claimed about them.
- Gated external effects (runs, one grant at a time). The design calls for standing scoped grants, because approve-every-call gets disabled inside a week and a disabled control protects nothing.
- A durable ledger (runs). Approved-versus-executed, with the reversal class
disclosed before the call:
reversible,compensable,contained,irreversible. Every call currently disclosesirreversiblebecause nothing can classify one yet. The disclosure works; there is nothing to disclose from. - Verification against the canonical mirror (designed, not built). A provider
returning "accepted" is not the same as the effect having happened. Where no mirror
exists, the result is
unverifiableand says so.
Why this holds as models improve
Most guardrails are cognitive: a system prompt, a refusal, a classifier, a policy in natural language. Those are arguments, and arguments can be out-argued. They decay as models get better at arguing.
The load-bearing controls here are non-cognitive. An agent with no token cannot authenticate, not because it failed to think of a way, but because there is nothing to reason toward. A vault cannot be persuaded. That property is flat with respect to model intelligence, which is the only kind of guarantee worth building on.
Three controls in the design are cognitive: structural drift detection, result scrubbing, credential-shape detection at import. They will erode, and the docs say so. An honest architecture labels the parts of itself that are in an arms race.
What broke, and what it taught me
The vertical slice met a real Claude Code session for the first time and failed four ways in two minutes. Ten defects, repaired in a single commit: five on the approval surface, five in the relay and ledger.
Fifty unit tests were green through all of it.
That is the finding, and it is not a story about bad tests. The suite drove the gate correctly and decided one perfect approval card immediately, so nothing ever used the surface. No redirect followed, no page left refreshing, no error recovered from, no second call held open while the first was pending.
A test suite that only exercises the happy path proves the happy path exists. It says nothing about the lane a real user actually walks.
The defect that ended the session was the smallest: localhost:9219 was an accepted
Origin and a refused Host, so every approval made by the name a browser autocompletes
to was rejected.
The tests written afterward target the class of defect the suite structurally could not reach, not the ten instances. Two design decisions came out of the same session:
- The decision timeout is gone. An agent that hits an approval at 2am must still be answerable at 8am. Abandonment is detected by reacting to the event rather than guessing a number.
- A killed client leaves a
claimedrow until the next startup. Claude Code takes its MCP servers down with it, so nofinallyin that process ever runs. Startup recovery closes the row truthfully, verified live, recorded as a limitation rather than filed as passed.
Two minutes with a real client bought more than the fifty tests did. That is now how the gate gets exercised.
What it is not
A tool that governs an agent's authority is worth nothing if it is oversold, because someone will rely on a boundary that is not there. So, plainly:
- It does not sandbox the agent's process. It governs the effects that pass through it.
- It does not enforce network egress. A shell can
curl. It stops authenticated calls, because the credential is not there to use. - The agent's own file and shell tools are not MCP tools and never traverse the gateway. MCP is where external systems live, which is the right surface to govern, and it is a minority of what an agent actually does.
- The ledger is durable, not tamper-evident. Calling it immutable would be false.
- It cannot verify it is the only path. It can prove the configuration it imported is clean. It cannot prove the machine is.
Every line of that list is a boundary somebody could otherwise trust by mistake. Knowing where a control stops is what makes the part inside it worth anything, and inside it, 67 tests green, the approved call is the executed call against a live client today.