chore: cap comment runs at two lines, and say where the reasoning goes
The user's instruction (2026-09-20): reasoning moves out of code comments into context/ prose, and a comment run is at most two consecutive lines — `///` and `//!` included, and test doc-comments too. scripts/check-comment-runs.py makes the rule checkable rather than remembered, which is the difference between a convention and a guard. It reports 1321 runs over the limit today; the move itself is improvise-9c8c. A ``` fence in a doc comment is exempt: a doctest is a running test rather than prose, and capping around one would delete it. `--list-doctests` finds the six blocks that need individual decisions. design-principles §7 said to record a bug in the test's doc-comment, which the cap contradicts — that evidence now goes to context/test-evidence/ keyed by test name, with the orphaning cost stated rather than discovered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -292,6 +292,17 @@ candidate for the store.
|
||||
concrete values. Wrap in `Option` at the edges (parsing, lookup, I/O) and
|
||||
unwrap early. Do not thread `Option` through deep call chains.
|
||||
|
||||
- **Reasoning lives in `context/`, not in comments.** A comment run is **at most
|
||||
two consecutive lines**, `///` and `//!` included, enforced by
|
||||
`scripts/check-comment-runs.py`. Where a reader would otherwise re-derive a
|
||||
rule wrongly, name the `context/` file that carries the argument — and only
|
||||
there, because a pointer on every function is noise. The prose lives in
|
||||
`context/code/<area>.md`; the rule exists because the arguments in this
|
||||
codebase are its most expensive artefact and a comment is the one place
|
||||
nothing checks them against the code (user, 2026-09-20; improvise-9c8c).
|
||||
A ``` fence in a doc comment is a doctest — a running test, not prose — and
|
||||
is exempt.
|
||||
|
||||
---
|
||||
|
||||
## 7. Testing
|
||||
@@ -314,8 +325,14 @@ habit of running them.
|
||||
|
||||
Write a test that **fails on the current code** before writing the fix. Prefer a
|
||||
small unit test targeting the broken function over an end-to-end test. After the
|
||||
fix, the test stays as a regression guard. Document the bug in the test's
|
||||
doc-comment (see `model/types.rs` → `formula_tests` for examples).
|
||||
fix, the test stays as a regression guard.
|
||||
|
||||
**Record the bug in `context/test-evidence/`, keyed by test name** — what broke,
|
||||
what fault was injected, and why the test can fail. It does not go in the test's
|
||||
doc-comment: comment runs are capped at two lines (see below), and the evidence
|
||||
is longer than that in every case worth having. The cost of the split is that a
|
||||
renamed test orphans its entry, which is why the entry names the test exactly
|
||||
and the checker looks for both halves.
|
||||
|
||||
### Use property tests judiciously
|
||||
|
||||
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fail if any .rs file carries more than two consecutive comment lines.
|
||||
|
||||
The rule (user, 2026-09-20): reasoning lives in context/ prose, not in code.
|
||||
A comment run is at most two lines, optionally naming a context/ file for the
|
||||
rest. See context/code/ for where the prose went, and improvise-9c8c for why.
|
||||
|
||||
Counts `///`, `//!` and `//` alike. Attributes are not comments and are
|
||||
ignored. A ``` fence inside a doc comment suspends the count, because a
|
||||
doctest is a running test rather than prose -- see --list-doctests.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
LIMIT = 2
|
||||
|
||||
|
||||
def comment_runs(lines):
|
||||
"""Yield (start_line_number, length) for each run, skipping doctest bodies."""
|
||||
run_start = None
|
||||
run_len = 0
|
||||
in_fence = False
|
||||
for n, raw in enumerate(lines, 1):
|
||||
s = raw.strip()
|
||||
is_comment = s.startswith("//")
|
||||
if is_comment and s.lstrip("/!").strip().startswith("```"):
|
||||
in_fence = not in_fence
|
||||
continue
|
||||
if is_comment and in_fence:
|
||||
continue
|
||||
if is_comment:
|
||||
if run_start is None:
|
||||
run_start, run_len = n, 0
|
||||
run_len += 1
|
||||
else:
|
||||
if run_start is not None and run_len > 0:
|
||||
yield run_start, run_len
|
||||
run_start, run_len = None, 0
|
||||
if run_start is not None and run_len > 0:
|
||||
yield run_start, run_len
|
||||
|
||||
|
||||
def rust_files(roots):
|
||||
for root in roots:
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
dirnames[:] = [d for d in dirnames if d != "target"]
|
||||
for f in filenames:
|
||||
if f.endswith(".rs"):
|
||||
yield os.path.join(dirpath, f)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("roots", nargs="*", default=["src", "crates", "tests", "examples"])
|
||||
ap.add_argument("--limit", type=int, default=LIMIT)
|
||||
ap.add_argument("--list-doctests", action="store_true",
|
||||
help="list doc comments containing a ``` fence and exit")
|
||||
args = ap.parse_args()
|
||||
roots = [r for r in (args.roots or ["src", "crates", "tests", "examples"]) if os.path.isdir(r)]
|
||||
|
||||
if args.list_doctests:
|
||||
for path in sorted(rust_files(roots)):
|
||||
for n, raw in enumerate(open(path, encoding="utf8", errors="replace"), 1):
|
||||
s = raw.strip()
|
||||
if s.startswith(("///", "//!")) and s.lstrip("/!").strip().startswith("```"):
|
||||
print(f"{path}:{n}")
|
||||
return 0
|
||||
|
||||
violations = []
|
||||
for path in sorted(rust_files(roots)):
|
||||
lines = open(path, encoding="utf8", errors="replace").read().split("\n")
|
||||
for start, length in comment_runs(lines):
|
||||
if length > args.limit:
|
||||
violations.append((path, start, length))
|
||||
|
||||
if not violations:
|
||||
print(f"comment runs: none longer than {args.limit} lines")
|
||||
return 0
|
||||
|
||||
worst = sorted(violations, key=lambda v: -v[2])
|
||||
print(f"{len(violations)} comment runs longer than {args.limit} lines:\n", file=sys.stderr)
|
||||
for path, start, length in worst:
|
||||
print(f" {path}:{start} {length} lines", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user