Skip to content

Commit 85e84f2

Browse files
committed
ai(rules[AGENTS]) Judge comments by three gates
why: Two rules in this file gave opposite answers on the same comment. The slop rubric said to keep the text and ask when unsure; the estate now prefers deletion in the borderline case. what: - Add "Comments earn their maintenance cost": the loss, elite, and upkeep gates, the one-to-two-line ceiling, and the keep and delete lists - Exempt doctests, usage examples, and param, return, and raises lines on public API from the loss gate, and from nothing else - Point "Preservation & Context" at the new rule and drop its keep-when-unsure default, keeping the invariant carve-out
1 parent 1c4372d commit 85e84f2

1 file changed

Lines changed: 86 additions & 2 deletions

File tree

AGENTS.md

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,89 @@ When stuck in debugging loops:
513513
3. **Document the issue** comprehensively for a fresh approach
514514
4. **Format for portability** (using quadruple backticks)
515515

516+
## Comments earn their maintenance cost
517+
518+
A comment ships only if it passes all three gates. Fail any: delete or rewrite.
519+
Borderline: delete — borderline means the information is reconstructible, which
520+
is what makes deletion cheap.
521+
522+
**Loss.** Three years from now, would losing this cost a maintainer real time
523+
rediscovering intent, an invariant, a constraint, or a failure mode the code and
524+
tests do not already make obvious?
525+
526+
**Elite.** Would SQLite, Redis, the Go standard library, or CPython write this
527+
comment, at this length? Those projects state the constraint and stop. They do
528+
not argue with an imagined objector.
529+
530+
**Upkeep.** Will it stay true without maintenance? A comment that hand-syncs a
531+
value the code owns — a count, an offset, a line reference, a duplicated
532+
constant — is false the first time that value moves.
533+
534+
### Ceiling
535+
536+
One or two lines. A comment reaching four is either carrying several facts, in
537+
which case split it, or arguing, in which case cut it to the fact.
538+
539+
Rationale, alternatives weighed, and the story of how the code got here belong
540+
in the commit message: timestamped, attached to the exact diff, and free to
541+
maintain.
542+
543+
A comment often holds both a constraint and the deliberation that found it. Keep
544+
the constraint, cut the deliberation. "Runs at most once per second" survives;
545+
"this is the right trade for now" does not.
546+
547+
### Keep
548+
549+
- Why over how: upstream quirks, protocol and compatibility constraints,
550+
performance tradeoffs still part of the contract.
551+
- Invariants, preconditions, ordering, lifetime, and concurrency requirements
552+
that types and tests cannot express.
553+
- Code that looks wrong but is not, so a later cleanup does not reintroduce the
554+
bug.
555+
- A high-level sketch of an algorithm whose local operations do not reveal the
556+
whole.
557+
558+
### Delete
559+
560+
- Narration of the next lines; code translated into English.
561+
- Restated names, types, defaults, or control flow.
562+
- Values duplicated from the code and hand-synced.
563+
- Justification, hedging, or apology for a choice.
564+
- Speculation about future requirements.
565+
- History version control already holds, including commented-out code.
566+
- Ticket and issue numbers. They say nothing to a reader without tracker access,
567+
and they rot when the tracker moves. Unfinished work goes in the tracker, not
568+
the source.
569+
- Transient observations — "currently", "for now", "the latest release" —
570+
that go stale with no nearby edit.
571+
572+
### The upkeep gate in practice
573+
574+
It reaches values that track our own code. It does not reach frozen external
575+
facts.
576+
577+
Bad (Delete):
578+
579+
```python
580+
# There are 321 tests to complete for servers.
581+
```
582+
583+
Good (Keep):
584+
585+
```python
586+
# CPython < 3.11 has no ExceptionGroup, so this branch stays.
587+
```
588+
589+
### Documentation exception
590+
591+
Doctests, minimal usage examples, and param, return, and raises lines on public
592+
API are exempt from the loss gate — they serve the caller, not the maintainer.
593+
They are exempt from nothing else. Ceiling: a good man page entry.
594+
595+
NumPy-style `Parameters`, `Returns`, and `Attributes` sections and executable
596+
doctests fall under this exception — autodoc ships every field whether or not
597+
you describe it, and a doctest that runs is also a test.
598+
516599
## AI Slop Prevention
517600

518601
Treat AI slop as **review-hostile noise**, not as proof that text or
@@ -570,8 +653,9 @@ on unrelated code while still resolving.
570653

571654
### Preservation & Context
572655

573-
**When unsure, leave the text in place and ask.** Subjective cleanup
574-
must never be a reason to remove load-bearing rationale.
656+
Subjective cleanup must never remove load-bearing rationale. Adjudicate
657+
comments with the comment policy above; borderline cases are deleted, not
658+
kept.
575659

576660
- **Preserve the "Why":** You MUST NOT delete comments that document
577661
invariants, protocol constraints, platform quirks, security

0 commit comments

Comments
 (0)