Skip to content

Commit 0ee300b

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 6185da2 commit 0ee300b

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
@@ -512,6 +512,89 @@ When stuck in debugging loops:
512512
3. **Document the issue** comprehensively for a fresh approach
513513
4. Format for portability (using quadruple backticks)
514514

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

517600
Treat AI slop as **review-hostile noise**, not as proof that text or
@@ -569,8 +652,9 @@ on unrelated code while still resolving.
569652

570653
### Preservation & Context
571654

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

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

0 commit comments

Comments
 (0)