Standards the agent cannot talk past
Convert conventions into checks. A rule in a file depends on attention; a rule in the build does not.
Every convention you can move from prose into a check gets three upgrades at once: it stops costing context, it stops depending on attention, and it starts failing inside the agent's own correction loop — so it self-heals without you.
The ladder
| Level | Mechanism | Reliability |
|---|---|---|
| 1 | You say it in the session | This session, while the context is fresh |
| 2 | A line in the brief | Most sessions, degrading with length |
| 3 | A lint rule or type | Every time, deterministically |
| 4 | Impossible by construction | Cannot be violated at all |
Push everything as far down this ladder as it will go.
Level 3 in practice
{
files: ['src/domain/**'],
rules: {
'no-restricted-imports': ['error', {
patterns: [
{ group: ['react', 'express', 'next/*'],
message: 'src/domain must stay framework-free. Move this to src/server.' },
{ group: ['../server/*', '@/server/*'],
message: 'Domain cannot depend on the server layer.' }
]
}]
}
}The message is written for whoever reads it next — agent or human — and it says what to do, not just what is wrong. That turns a failing check into a usable instruction.
Level 4: make it unrepresentable
The strongest move is structural. If a mistake cannot be expressed, no amount of context degradation can produce it.
- Branded types so a
UserIdcannot be passed where anOrgIdis expected. - Parse at the boundary so internal code only ever holds validated values.
- Exhaustive switches with a
nevercheck, so adding a variant breaks the build everywhere it must be handled. - Private-by-default modules with a single public entry point, so there is nothing to import incorrectly.
Each of these was already good engineering. What is new is the return on it: an agent generating code at volume will find every gap your type system leaves open, far faster than a team of humans would.
Custom checks are cheap now
A project-specific rule used to be too much work to justify. It is now a fifteen-minute session:
Write a custom ESLint rule that fails when a file under src/domain imports anything from src/server. Include tests for the rule (valid and invalid cases) and wire it into eslint.config.js. Then run 'npm run check'.
Every such rule is a brief line you get to delete.
Watch out
A check the agent can silence is not a check. Ban blanket suppressions in review — eslint-disable, @ts-ignore, # type: ignore, skipped tests — and put every one of them in the danger-grep from Lesson 3.3. The rule and the ability to bypass it must not both be available.
Try it
Pick the brief line you have violated most often. Have the agent write the check that enforces it, with tests. Then delete the line from your brief.
Takeaways
- Push every convention down the ladder: session, brief, check, impossible.
- Write check messages that say what to do, not just what is wrong.
- Custom rules are now cheap enough to write for a single project convention.
- Ban suppressions, or your checks are advisory.
Why does a check outperform a brief line specifically in long sessions?
Because a brief line competes for attention budget, and attention degrades as the window fills — so the rule is weakest exactly when the session is most likely to break it. A check runs deterministically at the same strength at turn 40 as at turn 1.
A course by Pieter Zandbergen