You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(agents): make the no-inline-comments rule enforceable by every tool (#254)
* feat(agents): reject an inline comment where it is written
AGENTS.md has forbidden inline comments since it was written, and the rule
was still being broken — including by the agent that wrote several of the
comments now in packages/core. Nothing loaded the file and nothing checked
the rule, so it was advice that arrived only if someone thought to look.
CLAUDE.md is a symlink to AGENTS.md. Claude Code loads CLAUDE.md by name;
a symlink means the two cannot drift.
.claude/settings.json registers a PostToolUse hook that runs after every
file write. It counts the comments in the file just written, counts the
ones in the same file at HEAD, and rejects the write if the set grew,
naming each comment added. Comparing against HEAD rather than a checked-in
baseline keeps it quiet on the 1,700 comment lines already in the tree
while refusing every new one, with no list to maintain.
.gitignore ignored .claude wholesale, so no per-repository configuration
could travel between sessions. It now ignores the contents and tracks
settings.json and hooks/ — git cannot re-include a file whose parent
directory is excluded, hence `.claude/*` rather than `.claude/`.
The rule in AGENTS.md now names its exceptions, because the ambiguity is
what made it easy to break: /** */ counts as much as //, but suppressions,
compiler-read type annotations, and the prose in the six files a generated
reference is built from do not. Deleting the latter deletes a page.
scripts/comment-scan.test.ts is why the scanner can be trusted. One that
reads https:// inside a string as a comment would block honest work; one
that misses a comment after a regular expression would wave through what it
exists to catch. Both directions are covered.
This is deliberately not a CI gate. The rule is about how the codebase is
written, so the enforcement sits where the writing happens.
* feat(agents): enforce the comment rule for every tool, not only Claude Code
The PostToolUse hook added in the previous commit only fires in Claude Code.
Codex, another agent, or a person committing by hand met no resistance at
all, so the rule held for exactly one of the tools that write this code.
AGENTS.md now says what it is: the contract for every agent and for people.
Codex reads AGENTS.md by name and Claude Code reads CLAUDE.md, which is a
symlink to it, so both load one text rather than two that drift.
.githooks/pre-commit refuses a commit that adds a comment, whoever staged it.
core.hooksPath is pointed at .githooks by a prepare script, so pnpm install
arms it once. `git commit --no-verify` is the way past, which leaves a
deliberate choice behind rather than an accident.
pnpm comments:check is the same check as a command, comparing the working
tree against HEAD and naming every comment the change adds. AGENTS.md asks
for it before finishing, so an agent that reads instructions and one that
does not are both covered.
All three layers share scripts/comment-scan.mjs, and none of them are in
pnpm verify or CI — the rule is about how the code is written, so it is
enforced where writing and committing happen.
---------
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments