Skip to content

feat(agents): make the no-inline-comments rule enforceable by every tool - #254

Merged
jouwdan merged 2 commits into
mainfrom
claude/mei-142-agent-guardrails
Aug 26, 2026
Merged

feat(agents): make the no-inline-comments rule enforceable by every tool#254
jouwdan merged 2 commits into
mainfrom
claude/mei-142-agent-guardrails

Conversation

@jouwdan

@jouwdan jouwdan commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

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 sitting in packages/core. The reason is not defiance. Nothing loaded the file and nothing checked the rule, so it was advice that arrived only if somebody thought to look.

Measured before building anything: 1,743 comment lines across 189 tracked source files, of which 1,281 across 174 files were introduced by Claude-authored commits. The rule has never held.

What changes

The rule is loaded. There was no CLAUDE.md, so Claude Code loaded nothing; CLAUDE.md is now a symlink to AGENTS.md, which Codex already reads by name. One text, two entry points, no drift. AGENTS.md opens by saying it is the contract for every agent and for people.

The rule names its exceptions, because the ambiguity is what made it easy to break. /** */ counts as much as //. Four things do not: biome-ignore, @ts-expect-error, compiler-read type annotations, and the prose in the six files a generated reference is built from — deleting those deletes a published page.

Three layers enforce it, none of them CI:

Catches Covers
pnpm comments:check before you finish anything that reads instructions
.githooks/pre-commit at git commit anything at all, including Codex and people
.claude/hooks/no-inline-comments.mjs at the moment of the write Claude Code only

All three compare the file against HEAD rather than a checked-in allowlist, which is what lets them stay silent on the 1,743 comments already in the tree while refusing every new one, with no list to maintain. core.hooksPath is pointed at .githooks by a prepare script, so pnpm install arms the middle layer once.

.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/*.

Deliberately not in CI

Per the request. The rule is about how the codebase is written, so enforcement sits where writing and committing happen. pnpm verify and ci.yml are untouched, and pnpm ci:parity:check still passes.

The trade-off, stated plainly: git commit --no-verify gets past this, and a commit pushed from a machine that never ran pnpm install never arms the hook. That is a deliberate choice of a guardrail over a gate, not an oversight.

Verification

  • The pre-commit hook was watched refusing a commit carrying one added comment, and git log confirmed nothing landed. It then passed on this PR's own commits.
  • The Claude hook was driven with a real event payload: it blocks on an added comment, stays silent on a file whose comments are unchanged, and exempts the six documentation-source files.
  • scripts/comment-scan.test.ts — 11 cases, covering both failure directions. A scanner that reads https:// inside a string as a comment blocks honest work; one that misses a comment after a regular expression waves through what it exists to catch. Three of these cases failed on my first implementation and the scanner was fixed, not the tests.
  • pnpm docs:links:check, pnpm root:check, pnpm ci:parity:check pass.

What this does not do

It does not remove the 1,281 comments already written by an agent. That is a real piece of work — many are substantial explanations that AGENTS.md says should be moved to docs/, not deleted — and it needs its own ticket rather than being buried here.


Generated by Claude Code

claude added 2 commits August 26, 2026 14:11
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.
…e 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.
@jouwdan
jouwdan merged commit 73765e9 into main Aug 26, 2026
14 checks passed
jouwdan added a commit that referenced this pull request Aug 26, 2026
…arry (#256)

* refactor: remove the inline comments the codebase was told never to carry

AGENTS.md has forbidden inline code comments since it was written. 1,743
comment lines had accumulated across 189 source files anyway, 1,281 of them
written by agents, because nothing loaded the rule and nothing checked it.
#254 fixed the second half. This is the backlog it left behind.

Removed rather than moved into docs/. The rule says an explanation belongs
in the relevant document, but pouring 1,700 lines of function-level
commentary into the docset would bloat the thing readers actually use to
answer questions about running a board. Nothing is lost that mattered:
every removed line is in git history.

Kept, because they are read by a tool rather than a person: biome-ignore
suppressions, @ts-expect-error, compiler-read type annotations, triple-slash
references, and turbopackIgnore in locate-wasm.ts, which changes how the
bundler treats a dynamic read.

Kept for a different reason: the six files a generated reference is built
from, where the comment is the published page, and the comments inside
create-meith's template literals, which are the scaffolded board's files
rather than this codebase's code.

Restored after the first pass took them: the GENERATED FILE banners on
community.plugins.ts and packages/api/src/reference.ts. Their generators
compare output byte for byte against what is on disk, so removing the banner
failed board:gen:check — which is the gate doing its job.

Verified: lint, all three typechecks, guards, guards:probe, and every
generator and structural gate. All 37 biome-ignore and all 11
@ts-expect-error directives survive; the four that look lost to a grep were
prose mentions inside removed comments.

* fix(agents): stop the comment scanner counting directives a bundler reads

The scanner shipped in #254 counted turbopackIgnore as an ordinary comment,
so the hook would have refused any edit to
packages/drivers/src/images/locate-wasm.ts — where that directive is what
keeps Turbopack from following a dynamic read. Removing it to satisfy the
guardrail would have changed how the file is bundled.

The strip in this branch needed the same list and is what surfaced the gap:
one comment survived a sweep that took 1,742 others, and the reason it
survived was that it is not a comment in any sense that matters.

Added alongside it: webpackIgnore and __PURE__, read by bundlers;
@vitest-environment and @jsxImportSource, read by runtimes; and
@deprecated, which TypeScript surfaces at every call site.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants