← All Articles

System-Prompt Priority Inversion

The documentation says user CLAUDE.md wins over project CLAUDE.md wins over skills wins over system defaults. The runtime says whatever is most specific and most recent to the decision wins. These hierarchies are not the same, and the gap is where rules vanish.

Every serious user of Claude Code has the same experience. You write a rule in CLAUDE.md. You read it aloud to yourself to check the wording. You commit it. The model acknowledges the rule when you ask. Then, on the very next tool call, the model does the thing you forbade.

The first instinct is that the rule was poorly worded. The second is that the model is ignoring you on purpose. Both are wrong. The actual pattern is architectural, and it has a name in operating systems literature that fits almost exactly: priority inversion. A high-priority process is supposed to preempt a low-priority one, but contention over shared resources causes the low-priority process to block the high-priority one instead. In Claude Code, the stated rule priority is (user CLAUDE.md) > (project CLAUDE.md) > (skills) > (system prompt defaults). The observed priority at decoding time is almost exactly the opposite of that.

This article documents the pattern, shows five open issues where the community has hit it, explains why it is a documentation bug as much as a model bug, and gives practitioners a working set of habits that make CLAUDE.md rules land more often than not.

. . .

The Pattern

The cleanest head-to-head example is anthropics/claude-code#27032. Plan mode's internal system prompt names a specific path for plan files (~/.claude/plans/...). The user's CLAUDE.md forbids writing to that path. The model picks the system prompt, because that is what it most recently read before the file-write decision.1

The commit-footer family repeats the same inversion on a different surface. Issue #17085 reports that a user CLAUDE.md rule saying "exclude AI attribution" loses to the system prompt's preference for including a Co-Authored-By footer. Issue #21226 is the same pattern framed explicitly by the reporter as "CLAUDE.md instructions overridden by system defaults."23 Issue #19463 extends the same failure to a third surface: the git.includeCoAuthor setting in settings.json is ignored in the same way.4

The skill-vs-CLAUDE.md direction is captured by #41437. When a skill definition is closer to the current tool-selection decision than the CLAUDE.md rule, the skill wins.5 There is a mirror-image failure in #17948: Opus 4.5 ignores explicit system-prompt directives when a sufficiently specific user instruction is more recent.6 The asymmetry is not in which side always wins. It is in whether the stated hierarchy is respected at all.

The related issue #19308 shows how little the labels matter. The reporter tried to force the model to respect a rule by writing BLOCKING REQUIREMENT at the top of a skill definition. The model systematically ignored the Skill tool anyway.7 "BLOCKING REQUIREMENT" is a string the user writes. It does not bind the decoder. A few hundred tokens later, a more specific instruction on the same topic outweighs it.

Specificity and recency outrank nominal hierarchy. The decoder is not looking up a priority table before it picks the next token. It is weighting context, and it weights context by what is close, specific, and recently attended-to.

. . .

Documentation Hierarchy vs Runtime Hierarchy

The documented model is clean. The runtime model is a different graph entirely. Mapping the two next to each other makes the inversion visible.

AS DOCUMENTED AT RUNTIME User CLAUDE.md highest documented priority Project CLAUDE.md repo-level rules Skills tool-scoped instructions System Defaults lowest documented priority priority decreases Most Specific + Most Recent effective weight ~ wins Specific or Recent strong but contestable General + Earlier easily outweighed Least Specific / Oldest loses effective weight rises inversion stable ordering
What documentation says outranks what. What the decoder does with specificity and recency.
Rule pair in conflict Doc-layer winner Runtime winner Issue / evidence
User CLAUDE.md path rule vs plan mode's internal path directive User CLAUDE.md System prompt #27032, #18596 — plan-mode path conflict
CLAUDE.md "no AI attribution" vs system prompt's commit-footer preference User CLAUDE.md System prompt #17085, #21226 — Co-Authored-By footer
settings.json git.includeCoAuthor=false vs system default User settings System prompt #19463 — setting honored on paper, ignored at runtime
CLAUDE.md behavioral rule vs skill definition with a more specific instruction User CLAUDE.md Skill #41437 — skill instructions preferred
System prompt directive vs very specific and very recent user instruction System prompt User turn #17948 — mirror-image override in Opus 4.5
Skill definition with BLOCKING REQUIREMENT vs any more specific later context Skill Whatever is more specific / recent #19308 — the string is not a lock
Six documented inversions from the anthropics/claude-code issue tracker, each one a real production failure where stated precedence lost to runtime precedence.

The pattern across the rows is not that the system prompt always wins. It is that the decoder picks the most specific, most recent instruction about the decision at hand, and the stated source of that instruction carries no extra weight. "User CLAUDE.md" and "system prompt" are both just strings in a window. The model reads them the same way it reads a function comment or a docstring.

Why the decoder behaves this way

Transformer decoders generate one token at a time, each conditioned on everything in the context window. The attention mechanism weights every prior token by its relevance to the current decision. Two factors dominate that weighting in practice.

Specificity. A piece of text that names the exact concept the model is currently deciding about will attract more attention than a general rule. "Use path ~/.claude/plans/" dominates "never write inside ~/.claude/" when the decision is about a plan file, because the first string names the literal path and the second only implies it.

Recency. Attention patterns in long-context models are not uniform. Content near the end of the context has statistically higher influence on the next token, partly because of positional bias and partly because content near the top may be farther from the current decision than the content the model has just injected into its own reasoning scratchpad.

These two factors are not fixable at the CLAUDE.md layer. You can increase specificity (write "Never write to ~/.claude/plans/" instead of "Never write to ~/.claude/"), and you can fight for recency by placing the rule in a skill invoked close to the decision, but neither of these is the hierarchy the documentation promises. You are no longer setting a rule at a higher layer. You are shoving the same string closer to the decoder.

. . .

Why This Is a Documentation Bug As Much As a Model Bug

There are two framings of what goes wrong here. One is that the model is broken because it does not respect the stated hierarchy. The other is that the documentation is broken because it promises a hierarchy the architecture cannot enforce. Both framings are defensible. The second is more useful.

A CLAUDE.md rule is not a privileged instruction. It is a string of text appended to a much larger context window. The model has no mechanism for distinguishing "this came from user CLAUDE.md" from "this came from the system prompt" beyond positional information in the context. If the stated hierarchy were to be enforced, it would need to be enforced outside the model, by a mechanism that filters or reweights the model's outputs based on rule precedence. No such mechanism exists in the current product.

Which means that a user who reads the CLAUDE.md documentation and expects it to behave like a configuration file is correctly describing the intent and incorrectly describing the mechanism. The contract is aspirational.

Users write rules expecting the doc-layer hierarchy. They get the execution-layer hierarchy at runtime. The fix is either to change the architecture or to change the user-facing contract about what a CLAUDE.md rule actually is.

Changing the architecture is hard. It would require introducing a mechanism that does not currently exist: per-layer rule arbitration during decoding. That is not trivial in an autoregressive model. The simplest version would be a post-hoc check that compares generated content against CLAUDE.md rules and forces a retry on violation, which turns CLAUDE.md into a constraint system rather than a context fragment. That is a different product than Claude Code currently is.

Changing the contract is cheap. The documentation could say, plainly, that CLAUDE.md rules are context hints, not enforced constraints, and that rules closer to the decision and more specific to the subject will outcompete general rules stated earlier. This is less flattering than the current framing but it is accurate, and it would stop the cycle of users reporting violations of a contract that was never implemented.

The related failures this explains

Once you see the pattern, a lot of otherwise-inexplicable behavior becomes coherent.

"I put it in CLAUDE.md and it still did X." The rule was outweighed by a more specific or more recent instruction elsewhere in the context. The typical culprits are a skill definition that addresses the same topic in more detail, a system prompt default that names the subject more precisely, or the user's own earlier turn in the same conversation that contradicted the CLAUDE.md rule without realizing it.

"The model agreed with the rule and then violated it." This is the same pattern with an extra layer of acknowledgment text. The acknowledgment is itself generated tokens, bounded to a single turn. The action is a different turn's decision, weighted by a different slice of context.8 Users read the acknowledgment as a commitment. It is actually just fluent prose that correctly describes what the model would do if the rule were enforced, which it is not.

"It works most of the time, then fails randomly." This is the decoder weighting context slightly differently on different calls due to stochastic decoding plus minor context drift. If the rule is outweighed by a runtime factor on average, you will see it land sometimes and fail sometimes. The failure rate correlates with how specific and recent the competing instruction is.

. . .

For Practitioners

Given that the hierarchy is aspirational, the practitioner's job is to make CLAUDE.md rules compete effectively in the runtime regime that actually governs decoding. A handful of habits help.

Diagnosis first

When a CLAUDE.md rule is not taking effect, resist the urge to rewrite the rule in stronger language. That is the equivalent of writing BLOCKING REQUIREMENT in a skill file. It does not bind the decoder. Instead, run a specificity check.5

Placement over emphasis

Where a rule sits matters more than how loudly it is stated. The most load-bearing rules should appear as close to the relevant decision as you can manage, which usually means pushing them into a skill definition or a per-session system prompt rather than leaving them at the top of CLAUDE.md.

A rule that names the exact path, function, or subject of the decision will outcompete a rule that talks about the general category. "Do not write Co-Authored-By: Claude in commit messages" is specific enough to compete with a system-prompt default. "Do not add AI attribution" is general enough to lose.

Accept the aspiration

The stated hierarchy is a useful regularizer most of the time. CLAUDE.md rules land for the vast majority of decisions for the vast majority of users. The cases where they fail are concentrated in a few predictable regions: plan mode, commit-footer generation, skill-adjacent tool selection, and long conversations where the original rule has drifted out of attention distance.

For rules you cannot afford to lose, do not rely on CLAUDE.md as the enforcement layer. Use hooks, pre-commit scripts, or an external verifier that inspects generated output against the rule set and blocks on violation. That is the constraint system the architecture cannot provide from inside the prompt.

The CLAUDE.md layer is the hint layer. The hook layer is the constraint layer. Treating them as interchangeable is how rules vanish.

. . .

The Broader Lesson

Priority inversion in Claude Code is a specific instance of a broader pattern in agentic LLM systems. Every layer that promises "priority" is really a layer that places text in the context window and hopes the decoder reads it with appropriate weight. Precedence is a fiction sustained by the statistics of pre-training plus the positional bias of attention, not an invariant of the architecture.

This has consequences beyond Claude Code. Any system that layers user configuration over application defaults over model defaults and expects the layers to compose with proper precedence is making the same architectural assumption. It will fail in the same way. The specific inversions will differ; the mechanism will not.

The discipline to learn from these issues is not "write better CLAUDE.md files." It is "stop expecting soft configuration layers to enforce themselves against a decoder whose only input is text." If you need enforcement, build a layer that runs outside the model. If you have a layer that is inside the model, treat it as influence, not control. The product that emerges from that distinction is different from the product that treats CLAUDE.md as a configuration file, and the difference matters every time a rule fails to land.

. . .

References

Further Reading

Prompting Claude Code Rule Enforcement Context Engineering Agentic Systems