Lint Your CLAUDE.md: Stop Paying for Stale Context Every Session


Your CLAUDE.md is probably charging you rent every session.

Not because it’s too long. Because it’s stale. Path references to files that moved six months ago. An 18-line directory tree the agent rediscovers via ls on its own. A build commands section that’s a word-for-word copy of your README. Every one of those costs tokens on every spawn — and the hidden cost is worse: stale file paths actively mislead the agent into burning tool calls hunting ghosts before it starts the real work.

A commenter on r/ClaudeAI [1] audited theirs, deleted three dead paths, an 18-line directory tree, and a redundant tech-stack section. Ten minutes of work. Sessions noticeably cleaner.

We ran the same audit across nine of our own projects using @ctxlint/ctxlint [2]. Saved ~1,231 tokens per session across three of them and caught one real bug that had been silently misfiring every spawn. Here’s how to do it yourself — and how to tell the real findings from the false positives.

The two costs of a stale CLAUDE.md

Most people think of CLAUDE.md bloat as a “too many tokens” problem. That’s only half the bill.

Cost 1: Token bloat. Every session re-reads your CLAUDE.md. Every byte is billed. A 500-token directory tree at 5 sessions/day × 20 days/month × 5 engineers = ~750K tokens/month of pure re-encoding noise. Small per session, measurable at fleet scale.

Cost 2: Misdirected tool calls. This is the sneakier one. When your CLAUDE.md references src/utils/helpers.ts and that file was moved or deleted, the agent reads the context, plans to look at that path, spends a Read tool call (50–500ms, billed as input on the response turn), gets a “file not found,” then re-plans. Every ghost path in your context file is a silent trap. It doesn’t show up on your dashboard as a line item. It shows up as “Claude feels sluggish today.”

The second cost is the one that makes this worth fixing urgently — not just trimming.

The tool: ctxlint

ctxlint [2] is an npm package from YawLabs that reads your AI agent context files and cross-references them against your actual codebase. The premise: if your CLAUDE.md mentions a path that doesn’t exist, something is wrong.

npx @ctxlint/ctxlint check

Zero config required. It walks your repo and checks what’s actually there. Runs in a couple of seconds.

What it catches:

  • Stale file refs — paths mentioned in your CLAUDE.md that don’t exist in the repo
  • Directory trees — “here’s my folder structure” blocks that agents rediscover via ls anyway
  • Wrong commands — references to npm scripts that aren’t in package.json
  • Contradictions — your CLAUDE.md says X, your AGENTS.md says Y
  • Redundant sections — your “Build Commands” section duplicates your README
  • Linter overlap — style rules in CLAUDE.md that ESLint already enforces

What it supports: CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules, copilot-instructions.md, and MCP config JSON. It’s not Claude-specific — anything that feeds AI agent context is in scope.

Two comparable tools worth knowing: cclint by Carl Rannaberg [3] and claudelint by Patrick Dugan [4]. Same philosophy, similar scope. ctxlint is the one that gets mentioned most on Reddit and has the cleanest output format — but any of these will surface the main categories of rot.

What the audit found (real data from 9 projects)

Here’s the raw output across nine projects with CLAUDE.md files, before any cleanup (as of 2026-04-14):

ProjectErrorsWarningsTokens
Project A00339 (clean)
Project B11503
Project C11261
Project D21315
Project E52566
Project F31711
Project G1101,607
Project H1031,639
Project Ino CLAUDE.md

Four patterns accounted for almost everything.

Pattern 1: Directory trees

Three projects had hard-coded folder trees ranging from 11 to 34 lines. Combined per-session overhead: ~939 tokens across those three projects. The trees were exhaustive — every subfolder, every config file, every README. The agent rediscovers all of this via ls or find the moment it needs to navigate, so the pre-loaded version is pure noise.

Fix: Delete the tree. Keep a sentence or two for conventions that aren’t inferable — for example, “the legacy/ folder is read-only; src/ is the active migration target.” That’s information an ls won’t tell you.

Pattern 2: Duplicated README content

One project’s CLAUDE.md had a ## Build Commands section that was a 100% match with its README.md. That’s 282 tokens per session for content the agent could also read from the README on demand if it needed it.

Fix: Delete from CLAUDE.md and replace with a pointer: “Build commands: see README.md.” The agent can read the README if it needs to. You don’t need to pre-load it.

Pattern 3: Broken bridge refs

One project referenced a shared-bridge doc path that didn’t exist. Agents loading that context would attempt to read a ghost path every time they spawned. No crash, no error — just a silent wasted tool call per session. This was the single actual bug the audit caught.

Fix: Rename the ref to the actual path. Takes 30 seconds. Stops the bleeding immediately.

Pattern 4: Incomplete migrations

Several projects still used an old symlink name (_spam/) when a migration to a new name (_setu/) had been scheduled. One project even had an inline TODO note about it. The symlink still resolved, so nothing broke — but every mention of the old name was a reminder that a migration was deferred and never finished.

Fix: This one isn’t a quick sweep — it touches symlinks, hooks, and scripts, not just markdown. Worth scheduling as its own task rather than fixing mid-audit.

Total saved across the three projects we cleaned: ~1,231 tokens per session. One real bug fixed. One long-deferred migration surfaced and scheduled.

The judgment call: real signal vs. false positives

ctxlint isn’t perfect. Some of its errors are explanatory prose the tool can’t interpret semantically.

False positives from our run:

  • Template patternsYYYY-MM-DD-Description.md, prompts/<type>/, Taxes/{year}/Business/. These are naming templates, not paths. ctxlint flags them because nothing at that literal path exists. They’re not bugs.
  • Cross-project references — “_setu/ is a symlink to ~/Documents/spam/” gets flagged because ~/Documents/spam/ isn’t a file in the current repo. It’s explanatory text, not a file reference.
  • Ephemeral worktree matches — ctxlint found our legacy/, src/, and deploy/ folder references and suggested prefixing them with .claude/worktrees/<some-ephemeral-name>/. Those worktrees are short-lived; the suggestion was noise.

Rule of thumb: for each flagged finding, ask “would an agent reading this actually try to open the path?” If it’s clearly prose — the path is mentioned in a sentence, not cited as a reference — leave it. If it’s a bare file path the agent could plausibly attempt to Read, fix it.

Run it yourself

One command:

cd <project-root>
npx -y @ctxlint/ctxlint check

Read the output top-to-bottom. For each finding:

  1. Stale file refs — is this really a path the agent would try to open? If yes, fix or delete. If it’s explanatory prose, ignore.
  2. Directory trees — delete. Keep only annotations that describe non-inferable conventions.
  3. Token budget warnings — a “50%+ of this file is removable” hint is worth taking seriously. “>70% signal-to-noise” is usually fine.
  4. Redundant README sections — delete from CLAUDE.md; add a pointer to the README instead.
  5. CI coverage info — optional. Nice-to-have, not a bug.

Budget 5–15 minutes per project. Payback starts on the next spawn.

What a clean CLAUDE.md looks like

Related: CLAUDE.md hygiene is only part of the picture. Where you put shared content across agent files — skills vs rules vs in-agent-body — has a separate and significant cost impact. Skills vs Rules vs Reads covers that side of the equation.

After running the audit on our projects, the lean version tends to follow this shape:

# Project Name

One-line description of what this is.

## Quick Start
1. Read `_claude/` for session context
2. Read shared rules for cross-project conventions

## Conventions
- <the non-inferable bits only>
- Any rule that isn't obvious from the code

## Commands
- `/aspect` — load an aspect
- `#custom-command` — project-specific workflow (brief description)

Everything else the agent can discover on demand. Keep what’s non-inferable; delete what an ls or a README read would surface anyway.

Lint on a schedule

The insidious thing about CLAUDE.md rot is that it’s gradual. You add a file path reference. Three months later you delete the file. The ref stays. Repeat a few times and you’ve got a context file that’s half ghost.

Running ctxlint once on your existing projects will clean up the backlog. But stale refs will creep back as the codebase evolves. The best CLAUDE.md is one that matches reality today — which means checking periodically, not just once.

At minimum: run it when you do significant refactoring. Ideally: add it to your quarterly repo maintenance checklist. Some teams run it as a CI check — ctxlint’s --strict flag exits with a non-zero code on any warning or error, making it suitable for a pre-commit hook or CI gate.

The math is simple: 10 minutes of auditing pays back every session, indefinitely.


Pricing note: Token cost calculations above use Claude Sonnet 4.6 input rates as of 2026-04-14. The token-per-session savings figures are from our 9-project audit run on the same date.

Sources

  1. r/ClaudeAI — “Cleaned up my CLAUDE.md and my agent sessions got noticeably faster” — the post that sparked the audit
  2. YawLabs/ctxlint — GitHub — the primary linter used in this audit
  3. carlrannaberg/cclint — GitHub — alternative linter, similar scope
  4. pdugan20/claudelint — GitHub — second alternative linter
  5. DEV — “I built a linter that proves 74% of your AGENTS.md is wasting your AI agent’s time” — by the ctxlint author, with the motivating data
  6. Best Practices for Claude Code — Claude Code Docs — Anthropic’s own CLAUDE.md guidance