Instructing the agents
One rulebook, a planning workflow that comes before any code, and guardrails that stop an agent "fixing" the things it should report.
Most of the code in my projects is now written by LLM agents. Agents are fast, tireless and keen, and left alone they will copy a function instead of sharing it, swallow an error to make a test pass, and quietly add one more entry to the list of things the checks ignore.
This site explains every technique I use to stop that: the written rules, the machines that enforce them, and the tests and proofs that catch what slips past both.
Every rule I care about lives in one place, AGENTS.md. Every rule that a machine can check has a checker. Every checker that starts with existing debt holds that debt in a list that is only allowed to shrink. The tests have to cover every line, and then a mutation tester proves that the tests would notice if a line changed. The parts that move money are drawn as state machines and checked by a model checker.
None of this is exotic. It is a pile of small, boring, mechanical checks, each of which closes one door that an eager agent likes to walk through.
Each area has its own section, with worked examples from real code.
One rulebook, a planning workflow that comes before any code, and guardrails that stop an agent "fixing" the things it should report.
Linting, copy-paste detection, dead-code hunts, purity checks and baselines that can only ever get smaller.
Full coverage, weak-assertion gates, mutation testing, Cucumber stories and nightly runs against real payment and email sandboxes.
Lifecycles declared as data, swept against the real code, and checked for every possible run by the Apalache model checker.
The precommit pipeline, CI that mirrors it, stacked pull requests, review rules and an LLM grader that knows the house style.
Nix and devenv pin every tool, so a check that passes on my machine passes in CI for the same reason.
One rulebook, a plan before code, and guardrails for autonomous work
Lifecycles declared as data and checked for every possible run
Lint, duplication, dead code, purity and lists that only shrink
Coverage, assertion quality, mutation testing, stories and real sandboxes
Precommit, CI, stacked pull requests, reviews and a pinned toolchain
What a rule looks like
The rules in AGENTS.md are written to end discussions before they start. An agent that has to ask "should I delete this unused export?" or "should I merge these two near-identical helpers?" will often ask, or worse, guess. The rulebook answers in the heading.
Each preference is one bold line and a short reason, so an agent can apply it and a person can argue with it.
- **Remove dead code โ always the answer**: When code
has no production caller, delete it. Never keep it
"for symmetry" or "for future use".
- **Unify systems โ the answer is yes**: When two
systems do almost the same job, find the single
abstraction that does both.
- **Never loosen a check to close a finding**: Fix
the code, or argue on the thread that the finding
is wrong.
Almost every example on this site comes from chobbledotcom/tickets, an open-source ticket booking system that runs on Bunny Edge Scripting and takes real payments through Stripe, Square and SumUp. A few come from chobbledotcom/chobble-template, the Eleventy template this site is built on. Both repositories are public, so every file named here is a link you can follow and read in full.