Back to Research

How .cursor/rules works, past the Cursor rules documentation

The Cursor rules documentation explains the .cursor/rules file format. This explains which of the four attachment modes to pick and why it matters.

Blagoveschensky Cathedral and Province Gymnasia NNovgorod, landscape painting by Ivan Shishkin (1870).
Rogier MullerAugust 15, 20264 min read

The file layout

Rules live in .cursor/rules/ at the repo root, one rule per file, with an .mdc extension. Each file is markdown with a small frontmatter block on top. You can also place a .cursor/rules directory inside a subdirectory, and those rules apply to work in that subtree, which is how monorepos keep frontend conventions away from backend tasks.

The old single .cursorrules file at the repo root still gets read, but it is the legacy path. Everything useful, especially scoping by file glob, only exists in the directory form. If you are still on the old file, moving is an afternoon.

Four .cursor/rules attachment modes, and how to choose

This is the part the Cursor rules documentation states plainly and everyone still gets wrong. A rule reaches the model in one of four ways, controlled by frontmatter:

  • Always. Set alwaysApply: true and it is in context for every request. Use for a handful of facts that are true everywhere: how to run tests, what the package manager is.
  • Attached by glob. Set globs and the rule loads when a matching file is in play. This is the mode most rules should use.
  • Requested by the agent. Give it a description and no globs, and the model pulls it in when the description matches the task. The description is the entire selection signal, so write it as a trigger condition, not a title.
  • Manual. No description, no globs. It loads only when you reference the rule by name in a prompt. Good for rare procedures like a release checklist.

A rule file looks like this:

---
description: Conventions for React components
globs: ["src/components/**/*.tsx"]
alwaysApply: false
---
Components are function declarations, not arrow consts.
Data fetching happens in the route loader, never in a component.

The failure that costs teams the most

Marking everything as always-apply. It feels safe. It is the reason your rules stop working. Every always-on rule occupies context in every request, including the ones it has nothing to do with, and once the block is long enough the model starts treating all of it as background noise. Teams come to us with a 600-line always-on rule set and the complaint that Cursor ignores their conventions. It does. That is what happens.

The fix is mechanical. Keep the always-on file under roughly twenty lines. Everything else gets a glob.

Second failure: rules written as values rather than instructions. "We care about clean code" changes nothing. "Never add a new dependency without asking; suggest a standard library approach first" changes behaviour. If a rule cannot be violated by a specific line of code, it is not a rule.

Checking that a rule fired

Do not assume. In a chat, the rules that were pulled in are visible in the context shown with the request. Open a file the glob should match, ask a question, and confirm the rule appears. If it does not, your glob is wrong, and the usual cause is a leading slash or a missing ** segment.

Cursor can also draft a rule from a conversation. When you have just spent ten minutes correcting the agent about the same convention, ask it to generate a rule capturing what it learned, then edit the result down. The generated version is always too long and roughly right.

What to do next

Open your existing rules and count the always-apply ones. Convert every rule that only concerns a subset of the codebase to a glob, and cut the always-on file to the five facts a new hire needs. Then run a task you know used to go wrong and see whether it still does.

If you want help putting this into practice, talk to us.

Related training topics

Related research

Ready to start?

Transform how your team builds software.

Book a 15-minute sync