Skip to content

agent-hygiene@v1

Hygiene rules for the AI-coding era — patterns that show up disproportionately in commits authored or co-authored by Claude Code, Cursor, Copilot agent, Aider, Codex, and other coding agents. Each rule targets a failure mode that happens more often with agents than with humans, but the rules themselves are agent-agnostic — they catch any commit matching the pattern, no special-casing on author identity.

Composes with the existing hygiene rulesets — reach for all three on agent-heavy projects:

extends:
- alint://bundled/hygiene/no-tracked-artifacts@v1
- alint://bundled/hygiene/lockfiles@v1
- alint://bundled/agent-hygiene@v1

no-tracked-artifacts@v1 already covers OS / editor / build junk (.DS_Store, *.bak, *.swp, node_modules/, .env, 10 MiB+ files); this ruleset focuses on the patterns that are distinctly agent-shaped — versioned-duplicate filenames, scratch / planning docs, AI-affirmation prose, debug residue, and model-attributed TODO markers.

Defaults are non-blocking on the heuristic checks (info / warning) and error only on unambiguous bugs (debugger; in non-test source). Override severity per-rule in your own config once you’re ready to enforce.

Filename looks like a versioned duplicate (e.g. app_old.js, api_FINAL.py, utils_copy.ts). Replace the original instead of keeping a parallel copy.

Scratch / planning documents should not be committed at the repo root. Move the content into a real doc (CHANGELOG, ADR, design doc, README) or delete it once the work is done.

AI-style affirmation phrasing in committed content. These are characteristic of agent-authored prose; trim before merge.

console.log / .debug / .trace left in non-test source. Route through the project logger or remove before merge.

debugger; / breakpoint() must not be committed. These halt execution at runtime. Remove before merge.

Agent-attributed TODO marker. Resolve, convert to a tracked issue, or remove the model attribution. These outlive the session that wrote them.

The full ruleset definition is committed at crates/alint-dsl/rulesets/v1/agent-hygiene.yml in the alint repo (the snapshot below is generated verbatim from that file).

# alint://bundled/agent-hygiene@v1
#
# Hygiene rules for the AI-coding era — patterns that show up
# disproportionately in commits authored or co-authored by
# Claude Code, Cursor, Copilot agent, Aider, Codex, and other
# coding agents. Each rule targets a failure mode that happens
# *more often* with agents than with humans, but the rules
# themselves are agent-agnostic — they catch any commit
# matching the pattern, no special-casing on author identity.
#
# Composes with the existing hygiene rulesets — reach for
# all three on agent-heavy projects:
#
# extends:
# - alint://bundled/hygiene/no-tracked-artifacts@v1
# - alint://bundled/hygiene/lockfiles@v1
# - alint://bundled/agent-hygiene@v1
#
# `no-tracked-artifacts@v1` already covers OS / editor / build
# junk (`.DS_Store`, `*.bak`, `*.swp`, `node_modules/`, `.env`,
# 10 MiB+ files); this ruleset focuses on the patterns that are
# *distinctly* agent-shaped — versioned-duplicate filenames,
# scratch / planning docs, AI-affirmation prose, debug residue,
# and model-attributed TODO markers.
#
# Defaults are non-blocking on the heuristic checks (`info` /
# `warning`) and `error` only on unambiguous bugs (`debugger;`
# in non-test source). Override severity per-rule in your own
# config once you're ready to enforce.
version: 1
rules:
# --- Versioned-duplicate filenames -------------------------------
# Agents tend to write `app_old.js` / `api_FINAL.py` /
# `utils_copy.ts` instead of replacing the original.
# Combined with `hygiene-no-editor-backups` from
# `no-tracked-artifacts@v1` (which catches `*.bak` / `*~` /
# `*.swp`), this gives broad coverage of the
# leftover-artefact filename surface.
#
# Important non-coverage: we deliberately do NOT match
# `*_v[0-9]*` / `*-v[0-9]*`. Real codebases use those for
# legitimate versioning — API versions
# (`gitlab_v1_callback.py`), schema migrations
# (`076_add_v1_tables.py`), release notes
# (`release-notes-v1.md`), and versioned tests
# (`test_v1_api.py`). The other suffixes below are
# unambiguous — `_old`, `_new`, `_FINAL`, `_copy`, `_backup`,
# `.copy.` are almost never legitimate part of a real
# filename's identity.
- id: agent-no-versioned-duplicates
kind: file_absent
paths:
- "**/*_old.*"
- "**/*_old"
- "**/*_new.*"
- "**/*_final.*"
- "**/*_FINAL.*"
- "**/*_copy.*"
- "**/*_backup.*"
- "**/*.copy.*"
level: warning
message: >-
Filename looks like a versioned duplicate (e.g.
app_old.js, api_FINAL.py, utils_copy.ts). Replace the
original instead of keeping a parallel copy.
# --- Planning / scratch docs at repo root ------------------------
# Agents spawn planning files (PLAN.md, NOTES.md, ANALYSIS.md,
# …) as part of their workflow and frequently forget to delete
# them before committing. Best-practice AGENTS.md templates
# explicitly tell agents to remove these post-merge — this rule
# enforces the discipline.
#
# `root_only: true` so a legitimate `notes.md` deeper in the
# tree (e.g. `docs/notes.md` for a feature, or a per-package
# `packages/foo/NOTES.md`) does not trigger.
- id: agent-no-scratch-docs-at-root
kind: file_absent
paths:
- PLAN.md
- NOTES.md
- ANALYSIS.md
- SUMMARY.md
- FIX.md
- DECISION.md
- TODO.md
- SCRATCH.md
- DEBUG.md
- TEMP.md
- WIP.md
root_only: true
level: warning
message: >-
Scratch / planning documents should not be committed at
the repo root. Move the content into a real doc
(CHANGELOG, ADR, design doc, README) or delete it once
the work is done.
# --- AI-affirmation prose in source ------------------------------
# Reviewers consistently flag these stock phrases as "AI smell."
# The pattern is narrow enough that legitimate code shouldn't
# match — info-level so it's a soft nudge, not a hard gate.
#
# The exclude list covers content that legitimately QUOTES
# AI-style text from upstream sources (CHANGELOG entries,
# roadmap rationale, cookbook examples) or captures it as
# test fixture output (snapshot tests, fixtures dirs).
- id: agent-no-affirmation-prose
kind: file_content_forbidden
paths:
include: ["**/*.{rs,ts,tsx,js,jsx,py,go,java,kt,rb,md}"]
exclude:
- "**/*test*/**"
- "**/__tests__/**"
- "**/fixtures/**"
- "**/CHANGELOG*"
- "**/ROADMAP*"
- "**/*.snap"
pattern: "(?i)(you'?re absolutely right|excellent question|happy to help|great (point|question)|let me know if you need)"
level: info
message: >-
AI-style affirmation phrasing in committed content. These
are characteristic of agent-authored prose; trim before
merge.
# --- Debug residue ------------------------------------------------
# `console.log` / `.debug` / `.trace` left in non-test JS / TS
# sources. The exclude list balances catching real production
# leftovers vs. legitimate logging in build tooling, browser
# demos, and vendored content.
#
# The leading `(?:^|[\s;{(])` ensures we don't match
# `myconsole.log(...)` or other false positives where `console`
# is part of a longer identifier.
- id: agent-no-console-log
kind: file_content_forbidden
paths:
include: ["**/*.{ts,tsx,js,jsx,mjs,cjs}"]
exclude:
# Test files and directories — `**/*test*/**` matches
# any segment containing "test" (`tests/`, `e2e-tests/`,
# `cross-sdk-tests/`, `test_helpers/`, …) — broader than
# `**/test*/**` which only matches segments STARTING
# with "test".
- "**/*.{test,spec}.*"
- "**/*test*/**"
- "**/__tests__/**"
- "**/fixtures/**"
# Build / dev tooling config files (vite.config.ts,
# rollup.config.mjs, etc.) often log intentionally.
- "**/*.config.*"
# Build / utility scripts — agent harnesses and CI
# glue legitimately log; src/ is where this rule earns
# its keep.
- "**/scripts/**"
# Browser-facing demos and websites — `console.log` is a
# legitimate browser-debugging tool, and the codebase
# may keep example logs intentionally.
- "**/website/**"
- "**/public/**"
- "**/demo/**"
- "**/demos/**"
- "**/examples/**"
# Vendored / third-party code we don't own.
- "**/vendor/**"
- "**/vendored/**"
- "**/third_party/**"
- "**/3rdparty/**"
# Agent worktrees and harness scratch space — these are
# ephemeral copies of the working tree (e.g. Claude
# Code's `/parallel` worktrees), not real source.
- "**/.claude/**"
pattern: '(?:^|[\s;{(])console\.(log|debug|trace)\s*\('
level: warning
message: >-
`console.log` / `.debug` / `.trace` left in non-test
source. Route through the project logger or remove
before merge.
- id: agent-no-debugger-statements
kind: file_content_forbidden
paths:
include: ["**/*.{ts,tsx,js,jsx,mjs,cjs,py}"]
exclude:
- "**/*.{test,spec}.*"
- "**/*test*/**"
- "**/__tests__/**"
- "**/fixtures/**"
- "**/scripts/**"
- "**/website/**"
- "**/public/**"
- "**/demo/**"
- "**/demos/**"
- "**/examples/**"
- "**/vendor/**"
- "**/vendored/**"
- "**/third_party/**"
- "**/3rdparty/**"
- "**/.claude/**"
# Require `;` after `debugger` so the rule doesn't trip
# on the WORD "debugger" appearing in prose comments
# (`* called by the vscode debugger`). Same idea for
# `breakpoint()` — must include the parens, not just the
# word.
pattern: '(?:^|[\s;{(])(debugger\s*;|breakpoint\s*\(\s*\))'
level: error
message: >-
`debugger;` / `breakpoint()` must not be committed.
These halt execution at runtime. Remove before merge.
# --- Model-attributed TODOs --------------------------------------
# `TODO(claude:)`, `FIXME(cursor:)`, `XXX(gpt:)` etc. — TODO
# markers that name a coding agent. They outlive the session
# that wrote them and are typically actionable items the agent
# intended to come back to but never did.
#
# Excludes documentation and changelogs — projects that
# *describe* these patterns (alint's own ROADMAP / CHANGELOG /
# cookbook are the canonical case) trip the rule on their own
# examples otherwise.
- id: agent-no-model-todos
kind: file_content_forbidden
paths:
include:
["**/*.{rs,ts,tsx,js,jsx,py,go,java,kt,rb,scala,c,cc,cpp,h,hpp,md}"]
exclude:
- "**/CHANGELOG*"
- "**/ROADMAP*"
- "**/cookbook/**"
- "**/*test*/**"
- "**/__tests__/**"
- "**/fixtures/**"
pattern: '(?i)\b(TODO|FIXME|XXX|HACK)\s*\(\s*(claude|gpt|copilot|cursor|gemini|codex|aider|chatgpt)\b'
level: warning
message: >-
Agent-attributed TODO marker. Resolve, convert to a
tracked issue, or remove the model attribution. These
outlive the session that wrote them.