Your Rules Are Fighting Each Other
How to find the conflicts, stop the chaos, and get your AI following instructions again.
Your AI is ignoring your rules. You've checked the syntax. You've verified the globs. The file is right there. But it keeps doing the wrong thing.
Here's what's probably happening: you have two rules that disagree, and the AI is picking one arbitrarily. You're not crazy. Your rules are fighting each other.
This post shows you how to find the fights, declare a winner, and prevent future conflicts.
The Four Ways Rules Fight
Rule conflicts come in flavors. Knowing which one you're dealing with is half the battle:
1. Direct Contradiction
Two rules give opposite instructions for the same thing.
# rules/base/naming/RULE.md
---
description: "Naming conventions"
globs: ["**/*.ts"]
---
Use camelCase for all variables.
# rules/project/style/RULE.md
---
description: "Project style guide"
globs: ["**/utils/**"]
---
Use snake_case for utility functions.
File src/utils/helper.ts matches both. Which naming convention wins?
2. Implicit Contradiction
Rules that don't directly conflict but pull in opposite directions.
# rules/conciseness/RULE.md
Keep code concise. Remove unnecessary syntax.
# rules/typescript/RULE.md
Always add explicit type annotations.
When types can be inferred, these rules disagree. "Remove unnecessary syntax" says skip the type. "Always add type annotations" says include it.
3. Priority Ambiguity
Two rules at the same priority level with different instructions.
# rules/formatting/RULE.md
---
alwaysApply: true
---
Use 2-space indentation.
# rules/team-standards/RULE.md
---
alwaysApply: true
---
Use 4-space indentation.
Both are alwaysApply: true. Both apply to all files. No clear winner.
4. Scope Overlap
Rules with overlapping globs that give different instructions.
# rules/frontend/RULE.md
---
globs: ["**/*.tsx"]
---
Use functional components with hooks.
# rules/legacy/RULE.md
---
globs: ["**/legacy/**"]
---
Use class components for legacy compatibility.
File src/legacy/Dashboard.tsx matches both globs. Which pattern applies?
How You Got Here
You didn't mess up. Conflicts emerge naturally:
- Different authors — Your teammate added a rule without checking yours
- Different times — That rule from six months ago still exists
- Different scopes — Base rules vs project overrides, both claiming authority
- Inherited rules — Symlinked shared rules conflict with local ones
- Implicit assumptions — Every rule thinks it's the only one on that topic
The bigger your rule system, the more likely this happens. It's not a bug—it's growth.
Who Wins? (The Hierarchy)
When rules disagree, you need a tiebreaker. Here's the pecking order:
Resolution Hierarchy
1. More specific glob wins
"src/api/**/*.ts" > "**/*.ts"
2. Project rules override base
"rules/project/*" > "rules/base/*"
3. Explicit override declaration wins
Rule with "overrides: base/naming"
4. Later in load order wins (less reliable)
5. If still ambiguous → flag for human resolution
Specificity Examples
| Rule A Glob | Rule B Glob | Winner |
|---|---|---|
**/*.ts |
src/api/**/*.ts |
Rule B (more specific path) |
**/*.ts |
**/*.tsx |
Neither (different file types, no conflict) |
src/** |
src/utils/** |
Rule B (deeper path) |
**/*.ts |
**/*.ts |
Ambiguous (need other resolution) |
Finding the Fights
Don't wait for weird behavior. Build conflict detection into your workflow:
A Rule That Catches Conflicts
# .cursor/rules/conflict-detection/RULE.md
---
description: "Detect and resolve rule conflicts before they cause problems"
alwaysApply: true
---
# Conflict Detection
## Pre-Application Check
Before applying rules to a file:
1. Gather all rules matching the file
2. Check for contradictory instructions
3. Apply resolution priority
4. If unresolvable, ask user
## Detection Patterns
Flag when:
- Two rules mention the same topic with different values
- Glob overlap exists with different instructions
- Both rules are alwaysApply with conflicting guidance
## Resolution Protocol
1. Check specificity (more specific glob wins)
2. Check origin (project > base)
3. Check explicit overrides
4. If still ambiguous → report conflict and ask
## Reporting Format
⚠️ Rule Conflict Detected
Rules:
- [rule A path]: "[instruction A]"
- [rule B path]: "[instruction B]"
Both apply to: [file path]
Resolution:
- [rule X] has higher priority because [reason]
- OR: Cannot determine priority, please clarify
Which should I follow?
1. [option A]
2. [option B]
Check Before Things Break
Run this before you're confused:
# Conceptual - analyze rule files for potential conflicts
/analyze-rules --conflicts
## Potential Conflicts Found
### Naming Convention Conflict
- rules/base/naming/RULE.md: "camelCase"
- rules/project/style/RULE.md: "snake_case"
- Overlap: **/utils/**/*.ts
### Indentation Conflict
- rules/formatting/RULE.md: "2 spaces"
- rules/team-standards/RULE.md: "4 spaces"
- Overlap: all files (both alwaysApply)
Resolve these conflicts? [Y/n]
What It Looks Like When It Works
flowchart TB
U["User Request"]:::primary
M["Rule Matching"]:::secondary
C["Conflict Detection"]:::warning
R["Resolution Applied"]:::accent
U --> M --> C --> R
Scenario: Create utility function in src/utils/stringHelpers.ts
| Step | Details |
|---|---|
| Rules Matched | base/naming → camelCase, project/style → snake_case, base/documentation → add JSDoc |
| Conflict | Naming convention: camelCase vs snake_case |
| Resolution | project/style wins (more specific glob) → use snake_case |
Resolved output:
/**
* Capitalizes the first letter of a string.
*/
export function capitalize_first(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
Tip: Consider consolidating naming rules to avoid future conflicts. Run
/analyze-rules --conflictsfor full report.
Stop Future Fights
Once you've cleaned up the mess, prevent the next one:
1. Say What You're Overriding
Rules can declare what they replace:
# rules/project/naming/RULE.md
---
description: "Project-specific naming conventions"
globs: ["src/**/*.ts"]
overrides:
- base/naming # Explicitly overrides base naming rule
---
# Project Naming Conventions
This rule OVERRIDES base/naming for this project.
For utility functions in src/utils/, use snake_case.
For all other functions, use camelCase.
2. Narrow Your Scope
Broad rules cause more fights. Be specific:
# Instead of broad rule:
# rules/naming/RULE.md with glob: "**/*.ts"
# Use scoped rules:
# rules/naming/variables/RULE.md with glob: "**/*.ts"
# rules/naming/functions/RULE.md with glob: "**/*.ts"
# rules/naming/utils/RULE.md with glob: "**/utils/**/*.ts"
3. Know Who Owns What
Maintain a registry so you know where to look:
# .cursor/rules/REGISTRY.md
## Naming Domain
Owner: base/naming/RULE.md
Overrides:
- project/utils-naming/RULE.md (for **/utils/**)
## Formatting Domain
Owner: base/formatting/RULE.md
No overrides.
## Documentation Domain
Owner: base/documentation/RULE.md
No overrides.
4. Test for Conflicts
Add conflict checks to your artifact tests:
# .cursor/tests/conflicts.yaml
tests:
- name: "no_naming_conflicts"
type: conflict_check
domain: naming
expect: no_conflicts
- name: "formatting_has_single_authority"
type: authority_check
domain: formatting
expect: single_owner
Conflicts You'll Definitely Hit
These are the usual suspects:
"Base says X, project says Y"
Shared base rules don't fit your project. Classic.
# Conflict:
base/naming: "camelCase everywhere"
project/style: "snake_case for data models"
# Resolution:
1. Add explicit override to project rule
2. Or narrow base rule to exclude data models
"TypeScript says X, React says Y"
Language conventions vs framework conventions. Both have good reasons.
# Conflict:
typescript/style: "Use interface for object types"
react/patterns: "Use type for props"
# Resolution:
1. Scope typescript rule to exclude *.props.ts
2. Or add explicit guidance: "For React props, use type"
"Move fast" vs "Don't break things"
Your velocity rules fight your quality rules. Tale as old as time.
# Conflict:
rapid-dev: "Skip tests for prototypes"
quality: "All code must have tests"
# Resolution:
1. Scope quality rule with: "except files in prototypes/"
2. Or add decision point: "If prototype, skip tests"
"We don't do that anymore"
Legacy rules nobody deleted conflict with your new standards.
# Conflict:
(old) legacy/RULE.md: "Use moment.js for dates"
(new) standards/RULE.md: "Use date-fns for dates"
# Resolution:
1. Scope legacy rule to legacy/ directory only
2. Remove legacy rule if no longer needed
"WTF Is Happening?" (Debug Prompts)
When the AI does something weird, ask:
"What rules are you following for this file?" See what's actually in play.
"Are any of these rules contradicting each other?" Force it to check for conflicts.
"Why did you choose camelCase over snake_case here?" Make it explain its reasoning.
"For this task, follow project/style regardless of other rules" Override everything and force a specific rule.
The Short Version
-
Conflicts will happen. Your rule system grew. That's good. Now manage it.
-
Find conflicts before they find you. Check proactively, not when you're debugging.
-
More specific wins.
**/utils/**beats**/*.ts. Always. -
Project beats base. Local customization takes priority.
-
Say what you're overriding. Use the
overridesfield. Remove ambiguity. -
Narrow your scope. Broad rules cause more fights.
-
Test for conflicts. Don't wait for weird behavior to tell you something's wrong.
Keep Going
- Rules, Agents, Commands, MCP... WTF? — The big picture of how everything fits
- Rules on Rules — Write rules that don't suck
- Testing Artifacts — Catch problems before they bite
Rule conflicts mean your system is growing. That's not a problem—it's a sign you're doing something worth organizing. The goal isn't zero conflicts. It's knowing how to fix them fast.