[Bug] Allow rules under `~/.claude/` show as loaded per /permissions but don't match at runtime
Summary
Allow rules in ~/.claude/settings.json that target paths under ~/.claude/
itself appear in /permissions as loaded but never match at runtime. Edits
to files in any subdirectory under ~/.claude/ prompt for permission
despite a matching rule being present and active.
Tested all four combinations of {tilde, absolute path} × {**/*.md glob,
literal path} for both Edit(...) and Update(...) scope-name forms —
none matched.
Reproducer
- Create a markdown file at
~/.claude/rules/test-rule.md(therules/
subdirectory is the documented standard location for user rule files).
- Add to
~/.claude/settings.json:
``json``
{
"permissions": {
"allow": [
"Edit(~/.claude/**/*.md)"
]
}
}
- Restart Claude Code, or run
/hooksto force-reload settings.
- Verify with
/permissionsthat the rule appears in the loaded allow list.
- Ask Claude Code to edit
~/.claude/rules/test-rule.md.
Expected (per docs): the Edit operation proceeds without a permission
prompt. The rule's gitignore-style ** matches the subdirectory, and the
docs explicitly state *"Edit rules apply to all built-in tools that edit
files."*
Actual: a permission prompt fires titledUpdate(~/.claude/rules/test-rule.md). The rule is bypassed.
What was tested
Each rule below was added to ~/.claude/settings.json, settings reloaded
via /hooks, and /permissions confirmed each rule was loaded. None
matched the runtime edit attempt.
| Rule | Match? |
| ---------------------------------------------------------- | ------ |
| Edit(~/.claude/**/*.md) | No |
| Edit(//<absolute home>/.claude/**/*.md) | No |
| Update(~/.claude/**/*.md) | No |
| Update(//<absolute home>/.claude/**/*.md) | No |
| Update(~/.claude/rules/test-rule.md) (literal path) | No |
| Update(//<absolute home>/.claude/rules/test-rule.md) | No |
Notes
- Auto-memory edits (under
~/.claude/projects/<encoded-slug>/memory/*.md)
are silent — they appear to have implicit allowlisting independent of the
~/.claude/** allow rules.
- Per the docs (https://code.claude.com/docs/en/permissions.md),
~/path
is documented as supported ("Path from home directory") and ** is
documented as recursive across directories.
- The runtime prompt label uses
Update(~/...)form, but/permissions
lists rules with whatever scope name (Edit or Update) was put in
settings.json. Update(...) rules with the same path patterns also
fail to match.
- Whether project-level
.claude/settings.local.json(e.g. with
defaultMode: "acceptEdits" and project-relative Edit(.claude/**))
interacts with user-global lookup is untested.
Environment
- Claude Code: 2.1.133
- OS: Linux x86_64
Impact
Users who store global rules, scripts, or skills under ~/.claude/ (a
documented and encouraged location) cannot allowlist edits to those files
without a permission prompt every time, despite writing rules that match
both the documented syntax and the gitignore-style spec.
This issue has 10 comments on GitHub. Read the full discussion on GitHub ↗