Cursor Rules: The Four Types and When Each Fires

A rule that never fires is worse than no rule, because the team believes it is covered. Cursor decides whether a rule applies from three frontmatter fields, and one combination of them produces a rule that only runs if somebody remembers to mention it by hand. Teams write that combination by accident more often than any other.

statusreviewedpublished2026-09-08read7 minauthorEditorial Teampolicyeditorial

Where rules live

Project rules go in .cursor/rules as .mdc files, committed with the repository so the whole team gets the same behaviour and any change to it arrives through review. Subdirectories work, so .cursor/rules/frontend/components.mdc is a valid path and a good habit once you have more than a handful.

One detail causes more confusion than everything else combined: plain .md files in that directory are ignored. A rule saved as spec-first.md is not a rule. It is a file. Nothing warns you. If you want plain markdown, the supported route is AGENTS.md, which Cursor also reads.

User rules are a separate mechanism, global to your Cursor environment rather than to a project. They are the wrong place for anything a colleague also needs, which is most things. If a rule matters to the codebase, it belongs in the codebase.

Three fields, four types

Cursor derives the rule type from the frontmatter. There is no type: field — the combination is the type.

TypeFires whenFrontmatter
Always applyEvery chat sessionalwaysApply: true
Apply intelligentlyThe agent judges it relevantdescription present, no globs
Apply to specific filesA matching file is in playglobs set, alwaysApply: false
Apply manuallyOnly when @-mentioned in chatalwaysApply: false, no description, no globs

The highlighted row is the one to watch. It is what you get by writing a rule body, saving the file, and leaving the frontmatter mostly empty — which is exactly what someone does when they are in a hurry and assume rules apply by default. The rule is well written, committed, reviewed, and inert.

Glob syntax is standard: * for one segment, ** for recursive, and comma-separated patterns for several at once, as in docs/**/*.md, docs/**/*.mdx.

Choosing the trigger for a spec-first rule

The type decides whether a rule is load-bearing or decorative, so pick it from the rule's purpose rather than from habit.

Rule purposeType to useWhy
Non-negotiables: never edit generated output, no new dependency without approvalAlways applyA boundary that only holds sometimes is not a boundary
Contract discipline for API surfacesSpecific files, globs: openapi/**, src/api/**Attaches exactly when the risky files are open, and stays silent otherwise
Migration and schema safetySpecific files, globs: migrations/**The same reason, with higher stakes
The spec-drafting procedureApply intelligently, with a descriptionYou want it when a request is underspecified, which no glob can detect
Anything with side effects: release steps, data backfillsApply manuallyDeliberate invocation is the point; automatic firing is the hazard

For the "apply intelligently" case, the description is doing real work: it is what the agent reads to decide relevance. Write it as trigger conditions, not as a summary. "Use when a request names a feature or fix but leaves scope, failure paths, or acceptance criteria undecided" gets selected. "Spec writing guidelines" does not.

A working set of three

Most teams need fewer rules than they think. Three files cover the common ground.

.cursor/rules/boundaries.mdc
---
alwaysApply: true
---
- Generated output is off limits. Edit the generator, not its files.
- No new dependency for a problem under ~20 lines.
- Migrations, auth, and billing need an approved spec before edits.

.cursor/rules/api-contracts.mdc
---
description: Contract rules for API surfaces
globs: openapi/**, src/api/**
alwaysApply: false
---
- A response shape change is a contract change: version it or keep
  the old shape working.
- Every new error path needs a documented code and a test.

.cursor/rules/spec-first.mdc
---
description: Use when a request names a feature, bug, or API change but
  leaves scope, failure paths, or acceptance criteria undecided. Produces
  the spec before any implementation.
alwaysApply: false
---
Write the spec first. Do not propose an implementation in this turn.
Required: goal / non-goals / behaviour / failure paths /
acceptance criteria (AC-1..n) / evidence per criterion / rollback.
Leave unknowns as {to-be-filled}. Never invent a requirement.

Note what is not in boundaries.mdc: formatting preferences, naming style, import order. Anything a linter or formatter already enforces is wasted space in an always-applied rule, and always-applied rules are the ones you pay for on every session. Keep individual rules under about 500 lines and split rather than grow them.

Two failure modes

Everything set to alwaysApply. It feels safe and it is the most common mistake. Every session then carries every rule, the useful instructions compete with the trivial ones for attention, and the set becomes too expensive to extend — so the next genuinely important rule does not get written. Reserve always-apply for boundaries you would enforce in review anyway.

Rules that describe intentions instead of behaviour. "Write clean, maintainable code" cannot be followed or violated in a checkable way. "Every new endpoint needs a test named after the behaviour it proves" can. This is the same standard a good acceptance criterion meets, and for the same reason: an instruction that cannot be checked will not change what gets produced. Our acceptance criteria guide covers the phrasing in more depth.

A rule that did not fire: the diagnosis order

Here is the shape of the incident, because it repeats. A team adds a rule requiring an approved spec before any migration edit. Weeks later an agent writes a migration during a routine change, nobody notices until review, and the first instinct is to rewrite the rule in stronger language. That is almost always the wrong move: the rule was fine, and it never reached the model.

Check three things, in this order, before touching a single word of the body.

CheckWhat you are looking forSymptom if this is the cause
1. The extensionIs it .mdc? A .md file in .cursor/rules is ignored outrightThe rule has never once had an effect, in any file, since it was added
2. The typeDoes the frontmatter select a trigger at all, or did it land on manual?Same as above, and the rule works when @-mentioned by hand
3. The glob depth* matches one segment; ** recursesIt fires in some directories and not others

The third one is the subtle case and worth stating plainly. globs: migrations/* matches migrations/001_init.sql and does not match migrations/2026/09/add_retry_column.sql, because * stops at a path segment. A team that organises migrations into dated folders has a rule that quietly covers only the flat files nobody writes any more. Write migrations/** unless you specifically mean one level.

The general lesson is the one worth carrying to any instruction mechanism: an instruction that never loads and an instruction the model chose to ignore look identical from the outside. They have completely different fixes, so establish which one you have before editing anything. Open a chat with a matching file and confirm the rule is attached — five seconds of verification beats a week of assuming.

Rules, AGENTS.md, and the spec

Cursor reads AGENTS.md as well, in the project root or nested in subdirectories, with the more specific file taking precedence. That leaves a real choice about where a given fact belongs, and the answer follows from portability and triggering.

Put it inWhen
AGENTS.mdRepository-wide context every agent tool should see. Portable across twenty-plus tools, so this is the default for stack, commands, and boundaries
An .mdc ruleYou need Cursor's triggering: attach only for migrations/**, or let the agent select a procedure by description
The spec for one changeGoal, non-goals, acceptance criteria, evidence. Anything that would need rewording for the next feature

State each fact in one place. A boundary written in both AGENTS.md and an always-applied rule will drift apart, and nobody will be able to say which version the agent actually used. The AGENTS.md durability test is the tool for deciding the third row: if a line would need different wording for the next feature, it is not repository context at all.

What to check after a week

Rules are cheap to write and easy to leave broken, so verify them the way you would verify anything else. Open a chat in a file the globs should match and confirm the rule is actually attached rather than assuming. Grep the directory for .md files that should have been .mdc. Count your always-applied rules — more than a few is a signal that the set needs re-typing rather than extending. And when an agent does something a rule forbade, treat it as a triggering bug first: check which type the rule is before rewriting its wording, because a well-worded rule that never fires reads exactly like a rule the model ignored.

If you are still deciding what the spec itself should contain, the Cursor spec template gives you the fields, and the spec packet generator produces them from a plain description.

References

Field names and rule types verified against the linked Cursor documentation on 2026-09-08. Cursor changes quickly; check the source before relying on a specific field.

Keywords: Cursor rules · .mdc frontmatter · alwaysApply · globs · AGENTS.md · spec-first AI coding