DEV Community

Piekwerk
Piekwerk

Posted on Edited on

Writing Cursor rules that actually fire: activation modes explained

A Cursor rule that never fires is indistinguishable from a rule you never wrote. There is no warning, no log line, no UI flag. You find out when the agent, for the fifth time, generates API errors as raw strings despite your beautifully written rule saying not to. This article is about the activation mechanics that decide whether a rule loads at all.

The frontmatter

Every .mdc file in .cursor/rules/ has YAML frontmatter with three fields:

---
description: "Error handling for payment API routes"
globs: ["src/api/payments/**/*.ts"]
alwaysApply: false
---

Your rule content here.
🏆 ยอดนิยม: piekwerk/writing cursor rules that actually fire activation modes explained fl - ประเด็นร้อน 🏆 ยอดนิยม: piekwerk/writing cursor rules that actually fire activation modes explained fl - ประเด็นร้อน
Field Controls Used by
description What the model sees when deciding whether the rule is relevant Auto Agent and Agent Requested modes
globs Which file paths trigger inclusion Auto Agent; matches against files in context
alwaysApply Force-load the rule into every request Overrides everything when true

The four activation modes

Cursor's UI presents four "rule types". They map onto the frontmatter like this:

UI mode Frontmatter When it loads
Always alwaysApply: true Every request, everywhere. Costs tokens always
Auto Agent globs set, alwaysApply: false When files matching the globs appear in the agent's context
Agent Requested description set, no globs, alwaysApply: false The model reads all rule descriptions and decides to pull the rule in
Manual @ruleName mention Only when explicitly invoked in chat

Practical guidance:

  • Always is for rules that are genuinely universal. Keep the count low; every Always rule spends your instruction budget on every request.
  • Auto Agent (globs) is the workhorse. Scope by path and the rule loads exactly when relevant code is in play. This is how you afford specific knowledge without bloating always-on config.
  • Agent Requested is worth using for cross-cutting rules that do not map to a path: "when writing database migrations.." applies to files whose names vary. The risk is that the model's relevance judgment is a judgment; a weak description means rare firing. Write descriptions as retrieval queries, not labels: "Applying when writing or modifying database migrations, schema files, or seed data" beats "Database stuff".
  • Manual is for heavyweight references you explicitly want: style guides, long API conventions.

The five silent failure modes

1. Broken glob. globs: "src/api/**/*.ts" when your API lives in app/api/ matches nothing. The rule never loads. Nothing tells you. This is the most common failure by a wide margin, and it survives code review because the glob looks plausible.

2. Single-string glob quirks. Cursor accepts a single string or an array of strings. globs: "src/a/**/*.ts,src/b/**/*.ts" (comma-joined in one string) does not do what you hope. Use an array.

3. Description-only rules with bad descriptions. In Agent Requested mode the description is the entire retrieval surface. "Coding standards" fires on vibes. Include the trigger vocabulary the model needs to match against the task at hand.

4. alwaysApply sprawl. Every rule someone marks "important" becomes alwaysApply: true, and suddenly you are back to a 900-line implicit CLAUDE.md, except worse because it is twelve files and nobody has seen the total.

5. Filewatcher drift. Globs are matched against your repo as it exists today. Move src/api to apps/web/src/api in a restructure and every rule scoped to the old path dies on the same day, quietly. Nobody re-audits globs during a restructure.

Auditing: make firing observable

Because Cursor will not warn you, test the globs yourself. The essence of an audit is: does each glob match at least one real file, and roughly how many?

# rules_audit.py -- check every .mdc glob against the repo
import pathlib, re, sys

broken = 0
for mdc in pathlib.Path(".cursor/rules").glob("*.mdc"):
    text = mdc.read_text()
    m = re.search(r'globs:\s*\[?(.+?)\]?
', text)
    if not m:
        print(f"{mdc.name}: no globs found")
        continue
    # hand the glob list to pathspec/minimatch of your choice here
    ..
🏆 ยอดนิยม: piekwerk/writing cursor rules that actually fire activation modes explained fl - ประเด็นร้อน 🏆 ยอดนิยม: piekwerk/writing cursor rules that actually fire activation modes explained fl - ประเด็นร้อน

Top comments (2)

🏆 ยอดนิยม: piekwerk/writing cursor rules that actually fire activation modes explained fl - ประเด็นร้อน
 
rulestack profile image
Rulestack •

The glob-scoped mode has a close cousin in Claude Code, and we measured what it saves there: the same 2,004-word rule set cost +3,330 tokens when pasted inline, and +0 as a path-scoped file, until a Read touched a matching file and the rule arrived at 2,987 tokens on that turn. That leaves a caveat that might sit alongside your five rather than inside them: we only tested the Read tool as the trigger, not grep or a shell cat, so a correct glob could still stay quiet if what counts as a file reaching the agent is narrower than it sounds. Do you know whether Cursor's glob match fires on files the agent only searched, or only on ones it opened?

🏆 ยอดนิยม: piekwerk/writing cursor rules that actually fire activation modes explained fl - ประเด็นร้อน
 
piekwerk profile image
Piekwerk •

Honest answer: I don't know, and the docs don't settle it. Cursor only says a rule auto-attaches when a matching file is in context (docs.cursor.com/context/rules), wording that sounds like it covers search results but never commits. Everything I verified for the article was the opened path, so I have the same blind spot as your Read-only test, and your caveat belongs alongside the five. The cheap way to settle it: scope a rule to one file, have the agent search a symbol in that file without opening it, then ask it to quote the rule back, or watch the request's token count jump by roughly the rule size. One thing your numbers do pin down: the rule arrived at 2,987 tokens on the turn it fired, nearly the 3,330 of pasting it inline, so the real saving is the zero-cost turns before a matching Read, not a discount on the firing turn.