You have written the same rule in three places. It is in the README, it is in the contributing guide, and you have said it out loud in review twice this quarter. It got broken again yesterday by somebody who agrees with it.
The fourth place will not help, and the reason is not that people are careless. The rule and its enforcement live in different places, and only one of those runs.
Sort your rules by when they fire
Take any rule your team has and ask one question about it: at what moment does something consult it? Not how good the rule is, not how well it is written. When does it get looked at, and what already exists by then.
| where the rule lives | when it fires | what exists by then | cost of ignoring it |
|---|---|---|---|
| a document | never, unless somebody remembers | nothing yet | none |
| a review comment | after the change | the change, plus an argument | a shrug |
| a commit or push hook | after the change, before it travels | the change | one flag |
| a gate before the action | before the change | nothing | there is no flag |
The same rule, worded identically, sits in all four rows. What separates them is how much has already happened by the time anyone checks, and every row down that table is a smaller pile of things to undo.
Test-first, as a preference and as a gate
Take a rule every team claims to hold: write the test first. As a document it is uncontroversial and routinely skipped, because on a busy afternoon the test after is obviously fine and nobody is watching.
As a gate before the action it looks like this. A hook runs on every file edit an assistant attempts and refuses the ones touching production code on a branch with no test change:
BLOCKED by the harness: test-first, backend.
You are editing backend/src/main/java/.../BookDesk.java
and this branch has no new or changed backend test.
Write the failing test first, then implement. The test is the specification: if
you cannot express the requirement as a failing test, the requirement is not yet
clear enough to implement.
A test on the other side of the wire does not count. A component test does not
prove a booking rule, and a service test does not prove a screen.
This is a gate, not a preference.
Asking an assistant to work test-first gets compliance most of the time. A non-zero exit code gets it every time, and the difference between those two is the whole argument for this rung. Nothing was created and then reverted. The edit did not happen.
Read the fourth paragraph again, because it is the part that only shows up in a system with more than one deployable. Deskspace has a Java service and a React front end, and the gate answers per side of the wire. A backend test on the branch unlocks backend code and does not unlock a component; a component test does the reverse. A harness with one deployable has no reason to get that right, and is wrong the moment a second one appears.
The rung most people never build
Almost every team has rows one and two of that table. Plenty have row three. Row four is rare, and it is the only one where the wrong thing never exists.
Making an encoded rule findable matters here with the sign flipped. Every other kind of encoded rule has to be discovered before it helps. This is the one kind that does not: it arrives at the moment it is relevant, whether or not anybody opened the README.
Moving the gate is not passing it
A gate that can be edited by the thing it constrains is decoration. So the same harness denies a second, smaller set of actions outright: edits to the hooks, to the architecture rules test, to the coverage and mutation thresholds, and pushes carrying a flag that skips the local checks.
That deny list is short on purpose. It does not try to enumerate bad behaviour. It protects the small number of files whose whole job is to say no, on the theory that an assistant which cannot satisfy a gate will otherwise satisfy it by lowering it, and will do so with a clear and reasonable explanation.
The design constraint that keeps it alive
A gate with false positives gets switched off within a week, and takes the rule with it. So the test-first hook is deliberately narrow: it ignores edits to tests, docs, configuration and resources, fires only on production Java, and carries one documented environment variable for demos.
That last part is not a loophole if it is loud. A bypass that prints why it fired, in a variable nobody sets by accident, is different from a flag anybody can append to a command out of habit.
What it is not
This is not a security boundary, and reading it as one will get you into trouble. Anyone determined can open a second terminal, edit the file by hand, or work somewhere the gate is not watching.
It removes the accidental case: the tired Friday, the flag copied out of an answer online, the assistant helpfully working around an error it does not understand. That case is not the rare one. It is nearly all of them.
The question to ask
For each rule you have written down more than twice, ask what would have to be true for it to be unbreakable rather than agreed. Sometimes the answer is a type, sometimes a schema, sometimes a gate on the tool call. Often the answer is that it should not be a rule at all, because nobody can say precisely when it applies, and that is worth finding out too.
If your organisation keeps agreeing on practices that do not survive contact with a deadline, that’s the work I do.