Every change to my code goes through the same gates, including Claude's
Plan, review and verify gates on every change, enforced by hooks and applied to Claude the same as to me.
Driving Claude Code by hand is bad practice for changing code. A manual session follows whatever process the developer feels like that day. Sometimes there's a plan and someone reviews it. Other times the build starts in the same cluttered conversation that argued about the design for an hour, and nobody can say afterwards which version of the plan the code came from.
If you're the one responsible for delivery, that's the real cost. You can't see how much design and checking happened on a change. You can't tell which plan the code came from. You find out the work is finished when the developer says so. You're carrying risk you can't measure, and AI widens the gap between a careful session and a careless one from a few lines of code to a few hundred.
Why writing the process into CLAUDE.md wasn't enough
The obvious fix is to write the process into CLAUDE.md and ask the model to follow it. That doesn't hold, because the model treats it as guidance. Anthropic's own best-practice guide draws the same line: CLAUDE.md, skills and prompts are non-deterministic, hooks are deterministic, and a rule with zero exceptions belongs in a hook.
My first version was a set of hooks that blocked every edit until an approval marker existed. It worked, but it had to be set up repo by repo, and an approval file sitting in the repo is a file Claude can write.
Every change goes through the same stages, each in a fresh session
cf (changeflow) is the plugin that replaced those hooks. Each stage runs in its own conversation and reads its inputs from files in .work/<change>/ rather than from a long chat history.
/cf:shapeis a conversation with me that ends inbrief.md: goal, scope, out of scope and acceptance criteria./cf:planreads the brief, the code and the CLAUDE.md standards, checks current docs for the library versions in use, and writesplan.mdwith the tests planned first./cf:review-planhands the plan to a reviewer agent that never saw the planning. It returns ship, fix or rework./cf:approveis mine. It refuses unless the latest review said ship on the plan as it stands now./cf:buildstarts a fresh builder fromprompt.mdandcontext.md, on the model picked at approval, and commits on the change's branch./cf:verifyis a second isolated agent. It runs the checks and looks for bugs, security and performance problems, with evidence for every finding./cf:shippushes and opens a draft PR.
While a change is open, a guard checks every tool call Claude makes. Before approval it can edit only the change folder. At push time the guard checks that the commit at HEAD is the exact code that passed verify and refuses anything else. The approval records live outside the repo, where Claude's edit tools can't reach them.
Small changes get a light lane. Docs, content, copy and config that don't change behaviour get a shorter plan and a cheaper reviewer, and every gate still applies. A small bug fix doesn't qualify, because it changes behaviour, so it goes through the full lane with its tests written first. Birgitta Böckeler argued last year that spec tools are too heavy for small work, after Kiro turned a small bug into four user stories and 16 acceptance criteria. For anything that changes behaviour I accept that weight. A bug fix should start with a test that fails.
Where this sits against Spec Kit and Kiro
The shape isn't mine. It's spec-driven development, and every major vendor shipped a version of it over the last year. GitHub's Spec Kit installs slash commands that take a change through constitution, specify, plan, tasks and implement, writing a markdown file at each step for someone to review before the next. AWS built Kiro around the same idea: requirements, design and tasks committed to the repo and approved before any code exists, and its newer workflows start each step with a fresh context. If your team already uses one of those, the brief and plan stages of cf will look familiar.
cf adds three things those tools leave to discipline.
The gates are enforced, not suggested. Spec Kit's commands are prompt templates the agent reads, so skipping a stage is a matter of the model deciding to. In cf the guard blocks the tool call.
The reviewer and the verifier are cold. Anthropic's engineering team wrote in March that agents grading their own work reliably skew positive, and that a standalone evaluator is far easier to tune than a generator asked to critique itself. The spec tools put a human between stages. cf puts a separate agent there as well, one that never shared a conversation with the planner or the builder and has to show evidence for every finding.
What ships is what passed. The push gate checks that the commit at HEAD is the code verify signed off on, and every stage writes to a log, so a change carries its brief, plan, review rounds, verify result and guard interventions with it.
What we're noticing already
cf logs every stage to a local events file that exports to CSV. This is what the log says so far, across three of my own repos, one of them ColchesterPets.
Six changes have reached plan review. Ten review rounds ran, and four of them sent the plan back with a blocker. Half the plans needed a second or third round before I could approve them. Those were problems in the plan, found before any code existed, and a round costs between two and seven minutes.
Verify sent three of the six built changes back with blockers. The one worth describing is a data exposure audit that touches row-level security, grants and auth triggers. Its plan went through review clean. Verify sent the build back with three blockers and five security findings. Plan review catches scope, missing tests and standards. Some problems only exist once the code does, and a cold verifier is the stage that finds them.
Twice the guard refused a push because the commit at HEAD wasn't the code that had passed verify. Four changes went out as draft PRs and one has merged. Three of those four shipped inside a working day, from brief to PR.
The handoff also logs which model builds each change and why. Two small changes went to Sonnet. The four that touch auth, schema or production data went to Opus, with the reason recorded next to the choice.
A few days is a small sample. What it does show is where the problems get caught, and plan review is doing a good share of the work before anyone has to read a diff. That matters because diff review is where AI-assisted teams are losing time. Faros's telemetry from over 10,000 developers found PR review time up 91% on teams with high AI adoption, with average PR size up 154%. If a reviewer sees the plan before the code exists, the diff they get later is smaller and already matches something they agreed to.
What this gives a team
On my own repos, cf stops me cutting corners. On a team it does more.
Every developer's changes go through the same gates, whatever their experience and whether or not they'd have written a plan on their own. Review effort moves to the plan, where a round takes minutes, instead of the pull request, where it takes an afternoon. The lead gets an answer to how a change got made that doesn't depend on asking the developer: which plans bounced and why, how many rounds each took, which builds verify sent back, and where the guard had to step in. That's what the CSV export feeds into a delivery dashboard. It's also how you'd notice the developer whose plans keep bouncing on the same blocker before it shows up in production.
What it doesn't solve yet
cf isn't a security boundary. A model set on getting round it can run a script that rewrites the approval records or removes the hook, and a GitHub token allows pull requests without a git push. cf stops drift and shortcuts. It doesn't stop a determined agent, though attempts the guard catches show up in the data.
The guard is also strict enough to get in my own way. It has already stopped another of my Claude plugins from saving its data, because that folder sits inside the Claude settings the guard protects.
My position is that this should be the only way code gets changed, by me or by anyone working with me. Today the gates only bind sessions with cf loaded, and pushes from a terminal or git client aren't checked. The next change planned for cf moves part of the rule onto the server. With GitHub branch protection, nothing reaches main without a reviewed pull request, wherever the push came from.
Sources
Best practices for Claude Code, Anthropic, https://code.claude.com/docs/en/best-practices
Hooks reference, Anthropic, https://code.claude.com/docs/en/hooks
Harness design for long-running application development, Anthropic Engineering, https://www.anthropic.com/engineering/harness-design-long-running-apps
AI coding assistants increase developer output, but not company productivity, Faros AI, https://www.faros.ai/blog/ai-software-engineering
Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl, martinfowler.com, https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html
Spec Kit, GitHub, https://github.com/github/spec-kit
Kiro, AWS, https://kiro.dev
Comments
Sign in or create an account to join the conversation.
Be the first to comment.
Enjoyed this? Get new articles by email.
Subscribe