Documentation: .claude/rules/ frontmatter format incorrect - globs works, paths with quotes/YAML list does not
Summary
The documentation for .claude/rules/ frontmatter is incorrect or incomplete. After extensive testing, the documented paths: format does not work in several configurations, while the undocumented globs: format works reliably.
Environment
- Claude Code CLI
- WSL2 (Linux on Windows)
- Project with
.claude/rules/directory containing multiple rule files
Testing Methodology
Created multiple test files with different frontmatter formats and checked /memory output:
| Format | Loads? |
|--------|--------|
| No frontmatter (unconditional) | YES |
| globs: "**/*.cs" | YES |
| paths: **/*.cs (unquoted) | YES |
| paths: "**/*.cs" (quoted) | NO |
| paths: + YAML list | NO |
Expected Behavior
Based on documentation and GitHub issues, this should work:
---
paths:
- "**/*.cs"
- "**/Controllers/**"
---
Actual Behavior
Only these formats work:
---
globs: **/*.cs, **/Controllers/**
---
Impact
Users following the documentation will have non-functional path-scoped rules with no error messages indicating why rules are not loading. This is a silent failure that is very difficult to debug.
Suggested Fix
- Update documentation to show the working format (
globs:) - Or fix the parser to support the documented
paths:formats
Workaround
Use globs: with comma-separated unquoted patterns:
---
globs: pattern1, pattern2, pattern3
---
11 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Tried workaround using globs: with comma-separated unquoted patterns:
Unfortunately all rules files
.claude/rules/are loaded into the system prompt at conversation start, regardless of whether the user is working with files matching those patterns.Claude CLI in windows env
https://github.com/amondnet/claude-memory-test?tab=readme-ov-file#test-results
Root cause analysis confirms your findings
I've traced the CC binary's rule loading pipeline and can confirm the behavior you documented.
Root Cause (detailed in #19377)
The
paths:field is processed by_9A(), a CSV parser that iterates character by character. Three failure modes:_9A()receives a JS Array, iterates elements not characters → broken concatenation["a", "b"]→ same issuepaths: "**/*.cs"fails whilepaths: **/*.csworks suggests an additional quoting issue specific topaths:vsglobs:Why
globs:works butpaths:doesn't with quotesBoth fields go through
_9A(), but thepaths:field likely has additional processing (path resolution inzKL()) that doesn't handle quoted strings properly. The quotes may be preserved in the path instead of being stripped, causing the glob to literally include"characters.Proposed Fix
Plus defensive handling in
_9A()itself for non-string inputs.See full analysis at: #19377 (comment)
Confirmed working format for path-scoped lazy loading (v2.2.x, VSCode extension)
Did some systematic testing using an
InstructionsLoadedhook to audit which rule files load and when. Here's what I found:What does NOT work
| Format | Result |
|--------|--------|
|
paths:as YAML array with quoted strings | Rules either load eagerly at session start or not at all ||
paths:as YAML array without quotes | Same — does not lazy-load ||
globs:(comma-separated, unquoted) | Always loads eagerly at session start, even withalwaysApply: false|| Simple directory paths (e.g.
paths: app/javascript/) | Does not load at all |What WORKS
Key requirements:
paths:(notglobs:) as a single unquoted CSV line — not a YAML array, not quotedalwaysApply: falsepaths:as CSV alone (withoutalwaysApply: false) still loaded eagerlyAudit log proof
Session start — only always-on rules load:
After reading a matching
.jsfile — path-scoped rule lazy-loads:Audit hook setup
For anyone wanting to reproduce, add this to your
~/.claude/settings.json:With
~/.claude/hooks/log-instructions.sh:Then
tail -f ~/.claude/instruction-load-audit.login a separate terminal to watch in real-time.Workaround that works reliably: use
globs:instead ofpaths:The
globs:field uses a different code path that handles comma-separated patterns correctly. It works with quoted strings, multiple patterns, and across all contexts (project rules, user rules, worktrees).We maintain claude-kit, a Claude Code configuration factory used across ~10 projects. We switched all 30+ rule files to
globs:after hitting the same silent failures withpaths:. Zero issues since.Hope this helps others land here from search. Would be great to see
globs:documented officially orpaths:fixed to use the same parser.@luiseiman
The report above says:
Don't you experience the same?
I tried some time ago and found out that the instructions are loaded into context regardless of what files Claude is working on. Claude is still usually able to apply them discriminately, but the context gets bloated so you can just put everything into CLAUDE.md with the same effect.
@dsent Good catch — yes,
globs:alone loads eagerly. That's intentional on our side.In dotforge we use
globs:for eager loading because our rules are lightweight (< 50 lines each) and the context overhead is acceptable. We're not trying to lazy-load — we want glob-scoped rules that load at session start.For lazy loading (rule loads only when Claude touches a matching file), @maxjeltes nailed the working combination:
Key constraints:
paths:must be unquoted CSV on a single line — no YAML arrays, no quoted strings (parser bug confirmed by @Johntycour in #19377)alwaysApply: falseis required — without it, evenpaths:loads eagerlySo the two working patterns are:
| Goal | Frontmatter |
|------|------------|
| Eager (always in context) |
globs: **/*.ts, **/*.tsx|| Lazy (on file match only) |
alwaysApply: false+paths: **/*.ts, **/*.tsx|globs:withoutalwaysApply: false≠ lazy. It filters but doesn't defer. The context bloat you're seeing is expected behavior for that format.Closing for now — inactive for too long. Please open a new issue if this is still relevant.
Why was this closed? This is still an existing issue. And only because people don't re-explain it every time it still is. The syntax we now have to use (
paths: **/*.ts, **/*.js) is not compliant with how front matter should look, it's not even legit YAML as far as I'm aware. And worst of all, the documentation states something that is just not working.I hate auto-closing issues. I hate Claude day by day more and more, I feel betrayed and sad about it.