Patrick Diaz

Vision · Part 10

Guardrails for the coding agent

Claude Code builds most of Vision on its own. Written rules hold most of the time; layered checks catch the rest, and every merge stays with me. The layers and reasons, not the rules.

As of 2026-11-12

You are hereVision · map

Measured · aggregates only

What it does

Most of Vision is written by Claude Code, working on its own inside plans I approve. It follows the written project rules most of the time, but not every time, and the one time it doesn’t is usually the expensive one. So the rules don’t rest on good behaviour alone: there are checks before a command runs, checks when work leaves for the shared repository, and a merge that only I perform.

This page describes those layers and why each exists. The exact rules stay private: a list of what is blocked, and how, would mostly help someone get around it.

SESSION OWN WORKING COPY OWN BRANCH CHECK BEFORE EVERY COMMAND DENY · ASK · ALLOW PUSH SECOND CHECK SECRET SCAN MERGE I REVIEW ONLY I MERGE denied: what can't be undone asked: what I should see anything live DEPLOY LANE ONE AT A TIME SESSION OWN COPY OWN BRANCH DEPLOY LANE ONE AT A TIME CHECK BEFORE EVERY COMMAND DENY · ASK · ALLOW PUSH SECOND CHECK · SECRET SCAN MERGE I REVIEW · ONLY I MERGE denied: what can't be undone asked: what I should see
Fig. 10.1 Where the checks sit. Each session works in its own copy and branch, a check answers deny, ask or allow before every command, a second check and a secret scan look at every push, and only I merge. Anything live goes through one deploy lane. Layers and reasons only; the rules stay private.

Why this way

Decision 01

A check before every command, and every rule says why

Before a command or a file access runs, a check looks at it and answers deny, ask or allow. Each hard rule in the project’s instructions also says what to do instead and which check stands behind it. A refusal names its rule, so a refused step is never a mystery, and when the step was right after all, I run it myself.

Rejected: rules in writing only

They work until the one time they don’t, and that one time tends to be the costly one.

Decision 02

Deny what can’t be undone, ask what I should see

Two kinds of stop, for two reasons. Whatever can’t be undone is denied outright: losing data for good, reading secrets, controlling my infrastructure, speaking as me in my chat. Whatever I should see before it happens is asked: privileged commands, changes to the production database, rewriting history, and changes to the guardrail itself. When I approve a plan, it names its risky steps up front, so I know what I’m agreeing to.

Rejected: asking about everything

Most stops after a plan was approved were routine steps the plan had already named, and each one sat waiting for me. Too many questions train you to stop reading them.

Decision 03

Pushes and merges stay with me

Claude may push only to the branch of the item it is working on. The check before the command looks at that, a second check looks again at what is actually being pushed, and a secret scanner runs on every commit and every push. Claude opens pull requests; I review them and merge them myself.

Rejected: the hosting side as the backstop

I couldn’t count on it for this repository, so the protection lives locally, and I wrote that down instead of assuming otherwise.

Decision 04

Each session in its own working copy, one lane for anything live

Every session works in its own working copy, on its own branch. Only one place, the deploy lane, may build and restart services, and only one session at a time can hold it.

Decision 05

Honest about the limits

The check is one layer among several, a seatbelt, not a wall. Known gaps are tracked as open items and closed one by one.

How it's tested

Every rule has tests, and a new one must fail first

The check had 106 unit tests at the start and has 1133 now, growing with each rule. A new deny counts only once its test fails against the previous version of the check, which proves the test catches what the rule is for.

The push-time check is tested on its own

The second check, the one that looks at what is actually being pushed, has its own test suite, and all 28 of its tests pass. No before-and-after number fits this page: each incident below became a written rule, most of them with a test.

What I learned

Case 01

Claude spoke as me

What happened

To try out a new skill, Claude once sent messages to my chat’s back end from inside the system, at my request but not from my phone. In the records they looked exactly like messages I had written. Nothing went wrong, but it showed that something inside the system could pass as me.

What I changed

Two layers came out of it: Claude never acts as my chat, and the check enforces that; and any test that depends on who I am is sent by me, from my phone, while Claude only reads the records afterwards.

Result

Since then every chat message is signed, and agent core refuses anything unsigned before a run starts.

Case 02

Two sessions in one folder

What happened

For a few days several Claude sessions shared one working copy. Unfinished files landed on the wrong branch, one session ran commands that had been approved for another item, three pull requests from different starting points couldn’t be merged one after another, and a session lost track of what it had already run once its memory was compacted.

What I changed

Now every session gets its own working copy and branch, made automatically; one locked lane holds anything live; a board shows what waits for me; work is brought up to date before it’s called ready; and each session keeps a short record it can recover from.

Result

My part of an item came down to approving the plan, doing my own steps, reviewing, and merging.

Case 03

A guardrail that was out of date

What happened

Merged guardrail changes turned out not to be in force in every session. My first explanation of where sessions read their guardrail from was an assumption, and a probe showed it was wrong: each session used its own copy, and a half-finished edit of that copy could lock a session out completely.

What I changed

Now every session runs the guardrail from one current copy that moves with each merge, and a merge that would block every session is refused.

Result

The lesson: measure where a control actually runs before trusting that it does.

Case 04

A history rewrite that never stopped

What happened

Twice, a session rewrote its own item branch’s history and pushed it without any stop. The rule allowed that for a session’s own branch, though I had meant it only for the last step before an item is ready, and the check couldn’t tell where in an item it was. Overwriting an existing branch wasn’t checked at all. In the same weeks, the rule that keeps the coding agent away from stored chat content blocked two harmless edits of my docs, because ordinary prose used the same words as the protected data.

What I changed

The fix stayed at the layer level: rewriting a pushed branch now asks me, overwriting an existing branch asks, and the content rule was made more precise.

Result

Ordinary text edits no longer trip the content rule. The lesson: a rule that can’t see the context should ask, not decide, and a content rule should key on what can touch the data, not on words.