Most teams write their charter on day one, from best practices, in one sitting. That charter is a list of guesses: nobody can say what a line cost, so someone argues it away the first time it is inconvenient. So nobody authors a good charter. Instead it accretes, one incident at a time, and every rule carries the date and the damage that put it there. Here is one, in full, annotated line by line.
CLAUDE.md, a rules directory, or a system promptThe best practices you would put in it on day one are the ones the model already follows. But the rules that matter are the ones your codebase taught you, and you cannot know those yet.
A day-one charter says "write clean, maintainable code" and "follow existing patterns" and "add tests". But every line is true and none of it changes any behaviour, because the model was already close to that. Then a real problem arrives and somebody adds a paragraph about it. Now the charter is nine tenths ornament and one tenth load-bearing, and nobody inside can tell which is which.
The alternative is uncomfortable and works: start with nothing, and add a rule only after something has gone wrong. So the charter comes out shorter, and every line has a story. Then someone proposes dropping a rule under deadline pressure, and the answer is a cost, not an opinion.
AUTHORED
"Prefer clear names." ← already true. costs nothing, does nothing
"Handle errors appropriately." ← unfalsifiable
"Write maintainable code." ← nobody has ever done otherwise on purpose
ACCRETED
"Money is integer cents." ← #412, silent rounding loss on refunds
"Payment calls fail fast." ← INC-88, 1,140 double charges
"Only paths named in the ticket."← #388, unreviewed rename broke two teams
Can you name the specific thing that went wrong without it? If not, you are writing a wish. Such wishes are free to add and impossible to remove, which is the exact combination that produces a charter nobody reads.
A billing team, three years in, after roughly a dozen incidents. The deletions took everything else.
# Billing · agent charter
#
# Every rule names the incident that caused it. No incident, no rule.
# Adding one requires removing or merging one. Reviewed each January.
## Money
Money is integer cents. Percentages resolve through Decimal, then
round half-up to cents. Never float on a money path.
gate money.integer-cents
2026-03-04 · #412 · float refund rounding, ~$40/day lost silently
## Failure
Payment calls fail fast. No retries on any non-idempotent path.
gate payments.no-retry
2025-11-22 · INC-88 · retry storm double-charged 1,140 customers
## Scope
A change touches only paths named in the ticket. Renames, reformats
and refactors are separate changes with their own review.
check scope.allowed-paths
2025-08-09 · #388 · a rename rode along and broke two other teams
## Tests
Every behaviour change ships a test that goes red when the change is
reverted. A test that cannot fail is not a test.
check test.must-fail-when-reverted
2026-01-15 · #401 · 300 lines of coverage asserting against its own mock
## Secrets
Never read .env, credentials, or key material. Never write a literal
token, even in a fixture.
gate secrets.no-read · secrets.no-literals
2025-06-30 · INC-71 · test fixture key leaked to a public fork
## Comprehension
Don't merge a change you can't explain. Four claims, in the PR.
human see the Comprehension Standard
2025-04-02 · standing · adopted team-wide, not incident-driven
## Refusal
When the ticket and the code disagree about what the rule is, stop
and ask. Do not pick the one that is easier to implement.
none unenforceable, and the most valuable line here
2026-02-11 · INC-94 · agent implemented the ticket; the ticket was wrong
Six of the seven sections trace to a specific bad day, and the seventh is a standing policy. Nothing here covers style, naming, or architecture, and nothing tells the agent to be careful or thorough. So §5 explains why.
The rule text is the part everyone writes and the least important of the four.
Money is integer cents. Percentages resolve through Decimal,
then round half-up to cents. Never float on a money path.
└─ 1. THE RULE — behavioural, testable, and about this codebase
gate money.integer-cents
└─ 2. THE ALTITUDE — where it is enforced, and its identifier
2026-03-04 · #412
└─ 3. THE DATE AND THE LINK — so it can be looked up
float refund rounding, ~$40/day lost silently
└─ 4. THE DAMAGE — in the units the business uses
gate, check, human, or none. Writing it down forces the question "does anything enforce this rule", which is uncomfortable roughly a third of the time.
Six months from now somebody will propose relaxing this rule because it is inconvenient in one place. The weak answer, "it caused a bug," loses that argument. So give the strong one instead: "it cost forty dollars a day for three weeks before anyone noticed." That number also lets you lose the argument on the days when forty dollars a day is genuinely the right price.
The same charter carries three enforcement strengths, and the shape of the failure picks each one, not the strength of the team's feelings.
| Rule | Altitude | Why not lower | Why not higher |
|---|---|---|---|
| Money is integer cents | gate | Reached production once and cost money. Prose already failed here. | Nothing higher exists. |
| Only paths in the ticket | check | Happens constantly, and the team ignored prose alone under deadline. | Legitimate exceptions exist, so people would override a hard refusal. A gate everyone overrides is worse than a check. |
| Stop and ask when the ticket is wrong | none | Nothing lower than prose. | Nothing mechanical can detect it. A gate here would be a lie. §4. |
A gate whose rule has become second nature can go back down to a check, or out of the charter. Almost nobody demotes a rule, which is why charters only grow. See the demotion section for the version of this argument that applies to harness code.
"When the ticket and the code disagree about the rule, stop and ask." Yet no gate can detect that disagreement. It stays anyway, because it has prevented the most expensive failures.
INC-94 is worth telling in full, because it is the failure mode that survives every mechanical control on this page. In it, a ticket asked for discount codes to apply to shipping. But the code had a three-year-old undocumented rule: the system never discounts shipping, because of how the carrier contract settles. The agent implemented the ticket anyway. Every test passed, every gate passed, and review approved it. Then the company spent five weeks under-billing carriers before anyone connected the ticket and the rule.
No gate catches that failure. So the change matched the ticket, the tests, and the codebase style. But only a person catches it, by noticing that two sources of truth disagree and asking which one is wrong. That is the whole skill, and the exercises in this catalog exist to build it.
A tidy-minded instinct says to cut every rule no gate enforces, because those lines look
like decoration. Resist that instinct anyway. The unenforceable lines are where the
expensive failures live, because no gate is watching them. So marking them
none is honest, and removing them is not.
Train for it. A rule marked none is a standing instruction to the humans. So
you install it with reverse-review and
the workshop, not with a hook. And every charter rule at
that altitude should point at how the team practises it.
The omissions define a charter. So argue the omissions as hard as the inclusions: every line you add dilutes the ones that matter.
The model follows instructions near the top of a long document better than instructions near the bottom. That leaves two responses: order a long charter with care, or keep it short enough that order does not matter. The second holds up better, because the first depends on the current model. Then you would have to re-verify it every time a model changes.
Nothing in the ordinary operation of a team ever removes a charter rule. So without a hard constraint the charter only grows, until it stops working on a day nobody can name.
The mechanism is one line at the top of the file: adding a rule requires removing or merging one. It is crude and annoying, and nothing else reliably works, because it turns every addition into a comparison. The question stops being "is this rule good". It becomes "is this rule better than the weakest rule in the charter", which is a question with an answer.
A rule that becomes a check or a gate stops needing to be prose at all. So the agent does not have to remember it, the charter gets a line shorter, and the constraint gets stronger. Promotion to the harness is the same idea worked through five different mount points.
One afternoon a year, with the audit trail open. Then, for each rule, ask when it last did anything, rather than whether it is still a good idea.
Deletion day is why the trail records passes as well as blocks. So a rule that has never blocked anything is either second nature or dead. Both look identical from the outside until you have the numbers.
| Rule | Fired | Blocked | Last block | Verdict |
|---|---|---|---|---|
| money.integer-cents | 1,204 | 31 | 6 days ago | keep · load-bearing |
| payments.no-retry | 890 | 4 | 3 months ago | keep · rare and catastrophic |
| scope.allowed-paths | 1,204 | 147 | today | keep · but 147 is a smell, investigate |
| test.must-fail-when-reverted | 1,204 | 0 | never | demote to check-only, revisit next year |
| logging.structured-only | 1,204 | 0 | never | delete · superseded by the linter in March |
Two judgement calls in that table are worth naming. First, a rule with four blocks in a year stays, because the metric is expected damage, not frequency. So one double-charge event dwarfs a hundred scope warnings. Second, a rule with a hundred and forty-seven blocks is not a success story: something upstream keeps producing changes that violate it. So the rule absorbs a problem instead of solving it.
Additions happen on their own, because adding a rule is how a team shows the incident mattered. But deletion has no such motivation behind it, so it never happens on its own. So put a name and a date on it, or accept that the charter grows forever.
Nothing but the header that says how the file works.
# Charter
#
# Every rule names the incident that caused it. No incident, no rule.
# Adding one requires removing or merging one. Reviewed each January.
#
# Format:
# the rule, in behavioural terms
# altitude: gate | check | human | none
# date · link · what it cost
(no rules yet)
It will not stay empty for long, and every line that arrives will have earned its place. Two exceptions are reasonable on day one. One is a comprehension bar, because that rule is a policy decision rather than a lesson. The other is anything you carry over from a previous team, with the original incident still attached.
Do not rewrite it. Instead, go through it once and put a date and a cause on every line you can. The lines where you cannot are your answer. Some are load-bearing and undocumented, and the rest are the ones to delete first. That exercise takes an hour, and it is often the most informative hour anyone has spent on the charter.