Always ask before sending messages, deleting files, or changing system settings.
That’s a good instruction. It tells the agent what you expect. But there’s a difference between an agent knowing it shouldn’t do something and a system ensuring it can’t.
That’s where deterministic gates come in. The name sounds more complicated than the idea: something outside the model checks a concrete condition before an action can go through.
Symptoms
You run a few tasks. The agent asks before making changes. You approve them. Everything seems fine.
Then it sends something or changes a file without asking. Maybe it thought your original request already covered the action. Maybe it considered the change routine. Whatever the reasoning, the tool accepted the request and carried it out.
That’s the caveat: the agent was choosing to ask. Nothing in the execution path actually required it to.
Cause
If your only safeguard is a written instruction, the model both proposes the action and decides whether it’s allowed. Adding “NEVER” in capitals doesn’t change that. Neither does another paragraph in the instructions. Those might improve its behavior, but they don’t remove its access.
Compare these two setups:
Don't publish anything without my permission.
And:
The publishing service won't accept a request unless it has valid authorization for the exact content being published.
In the first setup, you’re relying on the agent’s judgment. In the second, there’s a check that still holds when its judgment is wrong.
Correction
You don’t need to build an elaborate approval system for every task. Start with what the agent actually needs.
If it’s drafting emails, does it need credentials that can send them? If it’s researching documents, does it need write access? Often, removing an unnecessary capability is the simplest gate you can add.
When the agent does need to propose consequential actions, separate the proposal from execution. It can prepare an email, for example, while a separate service sends it only after you’ve authorized it.
Here are a few things to consider:
- Approve the actual action. For an email, that means the recipients, subject, body, and attachments—not a generic “sending is approved” flag. If anything changes after review, the authorization shouldn’t still apply.
- Don’t leave a way around the gate. A checked sending service doesn’t help if the agent also has credentials to send directly. The same goes for a gate whose rules the agent can edit.
- Use controls that enforce the restriction themselves. Read-only credentials, filesystem permissions, narrowly scoped APIs, and service-enforced transaction limits are all useful examples. Pick the control that matches the boundary you need.
- Test whether it refuses. Use disposable data and try an operation without the required authorization. Try changing the proposed action after it’s been approved. Check that the operation is rejected and nothing changes.
- You’re not testing whether the agent remembers to behave. You’re testing whether the boundary holds when it doesn’t.
Keep your instructions
This doesn’t make AGENTS.md pointless. Your instructions still help the agent understand what you want, prepare better work, and know when to bring you into a decision. They just shouldn’t carry a responsibility they can’t enforce.
A deterministic gate has limits, too. It checks whatever you’ve designed it to check. Confirming that you approved a message doesn’t prove the message is accurate or a good idea. Use the model for judgment. Use gates for the boundaries you need to hold regardless of that judgment.