# 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.
