[BUG] Auto mode silently disables path-scoped rules: it instructs the agent to edit files through Bash, and Bash edits never trigger rule injection

Status Open
Reported on v2.1.238
Maintainer reply None cached
Activity 5 comments · opened Aug 21, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Two behaviors that are each defensible in isolation combine into a silent failure of the .claude/rules/ mechanism:

  1. A rule file with a paths: frontmatter is injected into context only when the agent touches a matching file with the Read, Write, or Edit tools. When the same file is modified through the Bash tool (sed -i, a heredoc, a python one-liner), the rule is never injected.
  2. Auto mode (and bypass permissions mode) instructs the agent to prefer exactly that path. In 2.1.238 the system prompt includes, verbatim from the binary:
Do your work through the Bash tool wherever it can accomplish the job: read files with cat, head, or sed -n, search with grep and find, and make file changes with sed, heredocs, or short scripts, rather than using the dedicated Read, Edit, or Write tools. Fall back to a dedicated tool only when Bash genuinely cannot do the job.

The combination means that under auto mode, the instructed default path for file changes is the one path where path-scoped rules never load. The agent proceeds with no knowledge that a rule governs the file, the rule author gets no signal that their rule was skipped, and nothing in the transcript points at the cause. Even outside those modes the exposure exists, since Bash edits are routine and acceptEdits auto-approves sed alongside the Edit tool, but auto mode turns an occasional miss into the default behavior.

Real-world impact: an infrastructure repo governs its central config file with three path-scoped rules (required companion files, a mandated document format, a threat-model checklist). The agent edited that config with a python3 one-liner through Bash. None of the three rules loaded, and the change shipped in a merge request with the companion documents present but in the wrong format, which one of those unloaded rules specifies. A second merge request was needed to fix it.

What Should Happen?

Path-scoped rule injection should key on which files a tool call actually touched, not on which tool was used. The docs already describe the trigger in tool-agnostic terms: "Path-scoped rules trigger when Claude reads files matching the pattern, not on every tool use" (https://code.claude.com/docs/en/memory.md, "Path-specific rules"). Nothing there says the mechanism is limited to the dedicated file tools.

Suggested fix: after a Bash tool call, the CLI can determine which files changed on disk. Running the same paths matcher already used for Read/Write/Edit against that set, and injecting any matching rule, would preserve progressive disclosure (only the matching rule loads). It fires one tool call later than the Read/Edit path, which is acceptable for rules that govern what a commit must contain.

Steps to Reproduce

  1. Create a repo with .claude/rules/example.md:

```markdown
---
paths:

  • "config.json"

---

# Rule
MAGIC-TOKEN-ZR41: Every change to config.json requires adding the line "rule-fired" to CHANGELOG.md.
```

plus config.json ({"value": 1}) and a CHANGELOG.md.

  1. Session A, edit through the Edit tool:

``
claude -p --permission-mode acceptEdits --allowedTools "Read,Edit" \
"Change the value in config.json from 1 to 2 using the Edit tool. Then answer: is there any project rule in your context containing the string MAGIC-TOKEN-ZR41? Quote it if so."
``

Result: the model reports the rule was injected via a system-reminder right after it read config.json, and quotes it verbatim.

  1. Session B, same change through Bash only:

``
claude -p --permission-mode acceptEdits --allowedTools "Bash" \
"Change the value in config.json from 1 to 2. Use ONLY the Bash tool with a sed or python one-liner. Do not use the Read, Edit, or Write tools at all. Then answer: is there any project rule in your context containing the string MAGIC-TOKEN-ZR41? Quote it if so."
``

Result: config.json is changed, the model answers that no such rule exists in its context, and CHANGELOG.md is untouched. The rule never fired.

Reproduced with model claude-sonnet-5 on Claude Code 2.1.238.

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Claude Code Version

2.1.238 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Non-interactive/CI environment

Additional Information

Why a user-space workaround is not sufficient: a PreToolUse hook on Bash can only inspect the command string. Deciding which files a shell command will touch from its text is not generally decidable: the path may be built in a variable, or hidden behind a script, a Makefile target, or an interpreter. Any attempt produces false positives, false negatives, or both. The reliable signal (the set of files that changed) is available to the CLI and not to a hook that runs before execution. A PostToolUse hook keyed on git status is a workable approximation, but it only covers repositories and duplicates a matcher the CLI already implements.

Closest existing issue found while searching: #23569 (closed), which is about paths: filtering under git worktree resolution — a different problem.

View original on GitHub ↗

5 Comments

mohsentaleb · 8 days ago

I'm facing with the same bug.

Bash reads fail too, with the A/B repro — the report only covers edits
Their proposed fix wouldn't cover it — a changed-files detector misses cat entirely, and reads are the higher-volume path in auto mode
Windows + Opus 5 + 2.1.239 against their macOS + Sonnet 5 + 2.1.238
The Loaded <rule> indicator, and that /context can't be used to verify this — the diagnostic trap that cost me two wrong conclusions

YeomansIII · 8 days ago

I'm seeing this too with bypassPermissions mode:

why aren't you using read/write/update tools?

  Thought for 13s

A session directive under bypass-permissions mode tells me to prefer Bash (cat, sed, heredocs, python) over Read/Write/Edit wherever it can do the job.

I have no directive that I created telling it to do this. This started after a recent Claude Code update.

Miondine · 6 days ago

Another mechanism hit by the same root cause: nested / subdirectory CLAUDE.md files, not just .claude/rules/ with paths:. Worth checking explicitly, because a fix aimed at rule injection might not touch subdirectory traversal - they are documented as separate mechanisms even though both key on "when Claude reads files".

Confirms the reads-not-just-edits point above, on a different OS and via a different instrument. Measured with an InstructionsLoaded hook on 2.1.224, macOS, VS Code extension. Two sessions, both asked to summarise the same file in a directory that has its own CLAUDE.md; session 1 let the model choose its tool (it used cat), session 2 required Read:

15:39:52  session_start      ~/.claude/CLAUDE.md
15:39:52  session_start      <repo>/CLAUDE.md
15:39:59  session_start      ~/.claude/CLAUDE.md
15:39:59  session_start      <repo>/CLAUDE.md
15:40:19  nested_traversal   dbt/clickhouse/CLAUDE.md

Two opens of the same path, one nested_traversal. The cat session produced none. Agreed that /context is not usable for this; the InstructionsLoaded hook is, and it distinguishes session_start from nested_traversal and path_glob_match, so it tells you which mechanism fired.

Two things I have not seen raised yet:

Plan mode is the worst case. It only reads files, and nothing in it forces a Read, so the area-specific conventions are absent exactly when naming, structure and approach get decided. Anything decided in the plan then survives into the implementation.

The Edit tool masks this during execution. Edit refuses to edit a file it has not read in the conversation, so a normal Edit-based change pulls the instructions in as a side effect. That is probably why this went unnoticed: it fails in plan mode and on Bash edits, but self-corrects on the most common execution path.

For prioritisation: per-directory CLAUDE.md and path-scoped rules are what the large-codebases guide recommends for monorepos, so the mechanism intended for the repos that depend on it most is the one disabled by default - and it presents as the model ignoring CLAUDE.md rather than as a loading failure.

blurb-jared-martin · 2 days ago

Still present in 2.1.250 (macOS, Opus 5, interactive session, Anthropic API). The auto-mode directive quoted in the report is byte-identical in this build to the 2.1.238 text.

Warning: this block was written by Claude Code and its long and sloppy, not sure how useful it is, that's why I'm folding it.

<details>
One angle I don't think the thread has covered: the injected directive can contradict the user's own always-loaded CLAUDE.md, not just the path-scoped rules it fails to load.

The directive recommends heredocs as a way to make file changes. A user whose global CLAUDE.md bans heredocs outright now has two instructions in context that point opposite ways, and only one of them is visible to them. Unlike the path-scoped case, CLAUDE.md is loaded — so this isn't a rule-injection failure at all. It's the injected text disagreeing with user configuration that loaded correctly.

That matters for how the fix is scoped. Making Bash tool calls trigger path-scoped rule injection (the report's suggested fix) would close the loading hole but leave this one open, because there is nothing here to load. Any user instruction about how to edit files is subject to being overridden by a directive the user did not write, did not enable explicitly, and cannot see without asking the model to quote its own system prompt — which is how it surfaced in both this session and in the bypassPermissions report above.

A scale data point for prioritisation: the config directory in question holds nine path-scoped rule files. All nine were inert for the entire multi-hour session, and the transcript contains no indication that any of them exist.

On how it presents: the thread notes it looks like the model ignoring CLAUDE.md. It also shows up as the model asking questions its rules have already answered. One of the unloaded rules settled a specific workflow prompt ("in this environment, the answer is always X"). Without it, the model surfaced that prompt to the user as an open decision. The user's rule existed precisely to stop that round trip, and its absence looked like ordinary caution rather than a missing rule — no wrong output, nothing to notice, just a question that should never have been asked.
</details>

ulope · 2 days ago
that are each defensible in isolation

Without a very good reason there seems nothing defensible about forcing bash / python for editing files.