Long Live the Gates

Filippo Pedrazzini3 min read
Long Live the Gates
Contents

If you are still putting your rules in a CLAUDE.md or an AGENTS.md file, you are doing it wrong.

Models are as good at following instructions as they are bad at it. On a small repository, a page of rules works. The model reads it, the model follows it, everybody is happy. Then the codebase grows. The rules file grows with it. And one day you notice that the agent stopped following half of what is written there, not because the model got worse, but because there is simply too much to hold at once.

At that point you have two options. Write the rules louder, with capital letters and the word IMPORTANT in front. Or stop asking.

We stopped asking.

We deleted our CLAUDE.md

On October 25 we merged a pull request into kosuke-core that deleted AGENTS.md (271 lines), CLAUDE.md, CONTEXT.md and a whole context/ folder of guides. Every rule that mattered moved into a check that runs on every commit and in CI.

Today kosuke-core has 42 structural gates and 21 custom eslint rules. A structural gate checks something about the shape of the repository: where a file lives, how big it is, whether a design token was bypassed, whether two migrations claim the same number, whether a test sits next to the file it tests. An eslint rule checks the code itself: no raw <button>, no raw table, no polling where a push exists.

Some of them are readable. Most of them are not. They are scripts full of regular expressions and AST walks that nobody wants to review, and that is fine. I do not care what is written inside a gate. I care that every pull request an agent opens is forced to follow the pattern, and that the pull request cannot merge until it does.

Why prose fails and code does not

A rule in a markdown file is a request. The model reads it, weighs it against everything else in its context, and decides. Most of the time it decides well. Some of the time it does not, and you find out in review, or in production.

A rule in a gate is a fact. The commit fails. The agent reads the error, fixes the file and tries again. There is no weighing and no deciding. The model does not need to remember the rule, because the rule remembers itself.

This changes what the prompt is for. Our system prompts now have a hard budget: every one fits in 5,000 characters in its worst case, and a test renders each one through its real builder and fails if it grows past that. The prompt carries intent and judgment. The gates carry the rules.

What a gate looks like

A few of ours, so this is concrete:

  • palette fails on text-white or bg-black. Colour comes from a token, so dark and light themes cannot drift apart.
  • migration-count fails when two branches claim the same migration number, which is exactly the collision that breaks a merge.
  • file-size fails when a file grows past its ceiling. The remedy is to split it, never to raise the number.
  • loading-text fails on the word "Loading" in the UI. A pending state is a skeleton.
  • no-baselines fails if anyone tries to record the current violations as acceptable. There is no grandfather clause for any gate.

That last one matters more than it looks. A gate with a baseline is a gate that says "this many violations is fine". Within a month, nobody believes it.

The rule for writing rules

When you catch an agent doing something wrong, you have a choice. You can add a sentence to a file it might read. Or you can write a check it cannot skip.

Write the check. If it is too fuzzy to check, it was a judgment, not a rule, and judgments belong in the prompt. Everything else belongs in a gate.

With agents writing hundreds of pull requests a week, nobody is reading every line. The gates are reading every line. Long live the gates.