Path-scoped rules (.claude/rules/*.md) silently dropped when matching set exceeds an undocumented size budget
Type: bug + feature request
Component: project rules / context injection (.claude/rules/ with paths: frontmatter)
Version: Claude Code 2.1.193
Platform: macOS (Darwin 25.5)
Summary
.claude/rules/*.md files with paths: frontmatter are auto-injected as <system-reminder>s for
files whose path matches the globs. There is an undocumented total-size budget on what gets
injected for a single edited file. When the set of matching rules exceeds it, the harness drops
the largest rules first, silently — no warning, no indicator, no log. The dropped rule's
conventions then simply don't apply for that edit, and it is nearly impossible to notice.
This makes a documented, version-controlled team convention silently inert exactly when a rule
grows large enough to matter.
What I observed
- For a single file, a ~24KB matching rule was silently not injected while a ~22KB matching
rule for the same file was — i.e. eviction is largest-first against a shared total, not per-rule.
- The drop is invisible from inside a session: there is no surfaced list of which rules were injected
vs. dropped, so the only symptom is "the model didn't follow a rule that should have applied."
- The budget is a total across all matching rules, including the always-on global
~/.claude/rules/*.md set. On a real monorepo, a .tsx edit already carries ~16KB of global-rule
overhead (comments, react, typescript, biome, env-vars) before any project rule loads.
Scale, on a real repo
Reproducing the matching arithmetic across our rule set (byte sizes as a proxy for the token budget):
| Edited file | Matching rules | Total size |
|---|---|---|
| apps/app/**/*.tsx | 18 | ~101KB |
| packages/ui/**/*.tsx | 17 | ~88KB |
| apps/app/src/proxy.ts | 11 | ~62KB |
| apps/api/app/models/user.rb | 6 | ~46KB |
Whatever the exact threshold, the largest several rules in these domains are being evicted on every
edit, and nothing tells the author.
Why this is hard to detect
- No surfaced indicator of injected-vs-dropped for the current file.
- Largest-first eviction punishes the most important rule — your big, carefully-written rule is
exactly the one dropped; a pile of small ones survives.
- The threshold is undocumented, so authors have no size target to stay under.
- The recursive scan defeats the obvious workaround — moving a rule into a subdirectory of
.claude/rules/ does not exempt it; it still gets scanned and injected.
Requests
- Document the budget. Publish the limit (size or tokens), whether it's per-file-total or
global, and the eviction order, so authors can size rules deliberately.
- Surface injected vs. dropped. A visible indicator or
--debug/verbose output listing, for a
given edited file, which rules matched, which were injected, and which were dropped for budget.
Even a one-line N rules dropped for budget on <file> warning would make this detectable.
- Add a non-injecting / read-on-demand frontmatter flag. e.g.
inject: false(or
mode: on-demand) to mark a rule as a linkable reference doc that is not auto-injected and does
not count against the budget. Today the only workarounds are a never-matching paths: sentinel
(e.g. - "__never-auto-inject__/**") or moving the file out of .claude/rules/ entirely — both
are hacks, and the sentinel is non-obvious to anyone reading the rule later.
Current workarounds
To keep important rules from being silently evicted we split each large rule into:
<name>.md— the lean "always / never" core, kept small, with its realpaths:(stays injected).<name>-reference.md— cheat-sheets / recipes / edge-cases, marked non-injecting via the
__never-auto-inject__ sentinel, linked from <name>.md as "read on demand."
We also wrote an author-time detector that reproduces the matching arithmetic (matching rules sorted
largest-first, running total, flagged drops) so silent drops are at least catchable in review/CI —
but this only exists because the harness surfaces nothing. A first-party version of #2 would make it
unnecessary.
Suggested acceptance
- Docs page stating the budget, scope, and eviction order.
- A verbose/debug surface (or a soft warning) showing injected vs. dropped rules per file.
- A frontmatter flag to opt a rule out of injection while keeping it a linkable reference.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗