The problem with growing rules
Every rule with alwaysApply: true gets injected into the model’s context on every turn in that workspace. Rules start small. Then you add usage guidelines, commit conventions, quality-gate steps, a logging protocol… and suddenly always-on rules are hundreds of lines, and you notice:
- Responses feel slower on fast models (more input to attend to).
- The same instructions appear in several files, phrased slightly differently each time — drift is inevitable.
- When you update a policy, you update it in three places and miss two others.
This guide documents two techniques that reduced our .cursor/ instruction files from 2,317 to 1,574 lines (32% reduction) across 57 files in a March 2026 compression pass, while improving consistency rather than cutting coverage. Cursor has since added native context diagnostics; those belong in the same budget story, so they are included below. The 32% figure is a dated pass, not a claim about anyone’s current tree size.
Technique 1: The backbone-doc pattern
Core idea: rules and agents are behavior triggers — keep them short. Stable reference content (procedures, tables, rationale, examples) goes in Markdown under a docs/ (or your-docs/) tree and is read on demand.
The rule of thumb
A rule should answer: “What should the agent do and when?”
A doc should answer: “How exactly does the agent do it?”
Before — an always-on rule with the full pre-change backup procedure embedded (13 lines):
# rules/backup.mdc — before (13 lines, always loaded)
## Pre-change backup
**Required before**: rewriting most of a file, editing 3+ config files
in one batch, deleting/moving files, restructuring sections, or merging files.
**Not required**: adding a few lines to one file, CHANGELOG/log appends,
fixing a typo or broken link, updating a single cross-reference.
**Steps** — do these *before* editing live files:
1. Copy each affected file to `<subtree>/_archive/YYYY-MM-DD_<slug>/`
with `_YYYYMMDD_HHMM.<ext>.bak` naming...
2. Create metadata folder `_archive/YYYY-MM-DD_<slug>/MANIFEST.md`...
3. Append to `_archive/archive-log.md` one row per subtree.
4. Then perform edits on live files.
After — rule carries the trigger and a reference (3 lines):
# rules/backup.mdc — after (3 lines)
## Pre-change backup
MUST: take a backup before substantial edits (3+ files, rewrite, delete/move).
See your project’s archive / backup guide (e.g. `your-docs/archive-guide.md`).
SKIP: single-file edits, log appends, typo fixes.
The full procedure lives in a doc file the agent reads when it needs it. The always-on rule carries only the trigger condition and a pointer.
What to extract vs. keep in rules
| Keep in rules / agents | Extract to long-form docs |
|---|---|
| Trigger conditions (“when to do X”) | Step-by-step procedures |
| Must/Prefer/Avoid/Never declarations | Decision rationale and examples |
| Cross-references to docs | Tables that rarely change |
| Persona identity (1–2 lines) | Full workflow explanations |
Where the text should load
Not every instruction belongs in alwaysApply: true. Three load scopes:
| Load scope | When it is in context | Use for |
|---|---|---|
Always-on (alwaysApply: true) |
Every turn in that workspace | Short triggers the agent must not miss |
| File-scoped (globs / apply-to-files) | When matching files are in play | Heavy guidance for one language or folder |
| On-demand docs | When the agent opens the linked file | Procedures, tables, rationale, examples |
Promotion to always-on needs a stronger reason than promotion to docs. File-scoped rules are the middle ground.
Technique 2: A shorthand dictionary
Once you have 10+ instruction files, you’ll find the same multi-word phrases repeated everywhere: “pre-change backup”, “append CHANGELOG row”, “LLM tier classification”, “grading server environment”. Each repetition is a maintenance hazard and a token cost.
A shorthand dictionary assigns a 2–4 character code to each high-frequency concept. Agents load the dictionary once (from an always-on rule reference or file header), then understand compact instructions throughout all other files.
Example dictionary (TSV format)
# code expansion canonical source (yours)
PCB Pre-change backup: copy to _archive/, MANIFEST _archive/README.md
CL+ Append one CHANGELOG row (newest-first) rules/changelog.mdc
BL? Check backlog: update or add story your-docs/backlog.md
AL Log action (When, Agent, Action, Scope, What, Why) rules/logging.mdc
QG Quality gate: build + sanitizers + cppcheck your-docs or skills path
→ See / refer to (cross-reference pointer) —
§ Section within a document —
Severity tags (RFC 2119 style)
Pair shorthand with severity tags to eliminate ambiguous wording:
| Tag | Meaning |
|---|---|
MUST: |
Required; violation is a defect |
PREFER: |
Recommended; deviate only with reason |
AVOID: |
Discouraged; explicit justification needed |
NEVER: |
Prohibited; no exceptions without user consent |
Before and after — post-edit workflow
Before (5 lines across multiple files):
# rules/post-edit.mdc — before
## After editing config files
1. **Backlog**: check your backlog tracker — update existing story or add new one.
2. **CHANGELOG**: append one row per your project’s changelog conventions.
3. **Logs**: log the action per the logging rule.
After (1 line):
# rules/post-edit.mdc — after
MUST: after config edits → BL? → CL+ → AL. SKIP BL? for trivial changes.
The verbose version stays in your long-form docs; the rule carries only the compact form.
The measured result (March 2026 pass)
We ran this on a .cursor/ config workspace with 57 instruction files:
| Category | Files | Before | After | Reduction |
|---|---|---|---|---|
Rules (.mdc) |
16 | 537 lines | 292 lines | 46% |
Agents (.md) |
19 | 665 lines | 422 lines | 37% |
Skills (SKILL.md) |
11 | 820 lines | 576 lines | 30% |
| Commands | 11 | 295 lines | 284 lines | 4% |
| Total | 57 | 2,317 | 1,574 | 32% |
The always-on rules (the most expensive category) dropped from 194 to 183 lines in Phase 1 alone (shorthand proof-of-concept on one rule). The full rollout covered all 57 files over two sessions.
Treat this as a compression-pass measurement. Instruction trees grow again after a pass (new skills, commands, docs). Re-measure your own tree; do not quote 32% as a current size.
After that 57-file pass we also merged three always-on logging rules into one, then trimmed the remaining always-on set from six rules to four. Those were always-on-only; they were not a second 57-file measurement.
A later experiment compressed two file-scoped rules to shorthand-only bodies (trigger + links, no parallel prose). That is not a wide always-on rollout. One file held (50 → 32 lines). The other compressed, then grew in bytes past its pre-change snapshot; a later recompress cut it again (41 / 4231 B → 33 / 2595 B) — still above the first after-change size, and still not always-on.
The always-on slice grew again after those trims: we inlined procedures so agents actually follow them (a pointer-only always-on rule was being skipped). Use Context Usage Breakdown on a live session; do not quote 32% as current size.
More important than the line count: consistency. When six files all said “pre-change backup” in different words, drift was inevitable. When they all say PCB, the canonical definition in the dictionary is the single source of truth.
Native diagnostics: Context Usage Breakdown
Line counts are a proxy. Cursor 3.3 added a per-session Context Usage Breakdown across rules, skills, MCPs, and subagents (changelog, 2026-05-06). Use it when latency creeps up or “Taking longer than expected” appears: it shows which always-on rules and loaded skills are eating the window, so you know what to compress, demote to on-demand docs, or take out of alwaysApply.
The techniques above still matter. The breakdown tells you where the cost is this session; backbone-doc + shorthand is how you shrink the always-on slice.
Three different “compact” surfaces
These names collide. Mixing them up wastes a session:
| Surface | What it is | What it is not |
|---|---|---|
| Compact chat (Cursor 3.4) | Transcript display density (how much tool activity you see) | Model input tokens |
| Product context compaction | Window-full summarization of the parent thread; can cut an in-flight write | Compact chat; a /compact slash command |
/compact |
A VS Code roadmap placeholder in some write-ups | A shipping Cursor slash command in typical setups |
Land the deliverable before compact. For notes, matrices, and other canonical drafts, put a short file on disk first (skeleton write, then fill). Do not keep the only copy in chat. If product compaction fires, resume from the plan or checklist plus the on-disk draft, not from the summary.
Compact chat is a reading UX. Product compaction is a context-window event. They are not interchangeable.
How to apply this to your setup
Step 1: See what is actually in context
Open Context Usage Breakdown on a long session. List alwaysApply: true rules and count their lines. Any always-on rule over ~30 lines deserves scrutiny. Look for:
- Full procedures that could live in a linked doc.
- The same concept described in multiple rules with slightly different wording.
- Rationale paragraphs that don’t change agent behavior (they’re for humans — move them to your long-form docs tree).
Step 2: Create a docs backbone
For each heavy concept in your rules, create (or designate) a canonical your-docs/my-concept.md (or any folder you use for long-form policy). Move the encyclopedic content there. Update the rule to carry a one-line trigger + a link.
# Before (in the rule)
## Commit conventions
Messages should use the imperative mood, stay under 72 characters,
reference issue IDs in the footer, and include an "AI:" line if an
agent shaped the change. The body (optional) should explain *why*, not
*what*. For squash commits, keep only the topmost subject line...
# After (in the rule)
## Commit conventions
MUST: follow your-docs/commit-conventions.md (or your team’s policy URL).
Key: imperative subject ≤72 chars; "AI:" footer if agent-assisted.
Step 3: Build your shorthand dictionary
Create agent-shorthand.tsv (or a small .mdc rule) with codes for your most frequently repeated multi-word concepts. Good candidates: anything appearing in 4+ files.
Reference it from one always-on rule:
# rules/your-always-on.mdc — add one line
Shorthand codes: agent-shorthand.tsv (same repo; path relative to workspace root).
Start with 5–10 codes. More than ~25 creates cognitive load; the point is that the codes are self-documenting (PCB = Pre-Change Backup, not a mystery).
Step 4: Apply to one rule first
Pick one heavy always-on rule, apply backbone-doc extraction and shorthand replacement, and verify the agent still behaves correctly in a test session. Only then roll out to remaining files.
Step 5: Do not keep the only copy in chat
When the session is already heavy, write a short on-disk draft before a long implement. Product compaction can summarize away an in-flight write. Resume from the file.
What this does NOT do
- It doesn’t reduce how many rules you have — only how long each one is.
- It doesn’t help with conversation history growth (that’s
New Chat). - The savings are in always-on context, not in tool-call context (file reads, grep results, terminal output are per-turn). For verbose terminal dumps, prefer a one-line script summary (or a small JSON envelope) over pasting interactive CLI transcripts — see One-line script outcomes for Cursor agents.
- Compact chat does not shrink model input. Product compaction does not replace a file on disk.
- Later always-on trims (merge logging rules; drop unused always-on files) and file-scoped shorthand-only pilots are not the same as the 57-file table. Always-on can grow again when you inline what agents were ignoring.
- If your rules are already tight (< 20 lines each), the gain from shorthand will be small; the breakdown may still show MCP or skill bloat.
Related public patterns
Cousins, not clones — complementary threads, not substitutes for this pattern:
| Thread | Why it is related |
|---|---|
| Nested JSON skillsets (~90% claim) | Different compression shape (nested JSON), not a per-rule shorthand dictionary |
| A map of where tokens go | Survey of spend surfaces; not a measured rule-file pass |
| Monorepo loads all rules | Problem report; this guide is a shrink-the-always-on-slice response |
| MDC rules best practices | Authoring/troubleshooting; not backbone-doc + dictionary |
| Customizing how Cursor summarizes context | Product summarization behavior; distinct from Compact chat vs compaction above |
Tips
-
Measure before and after, then confirm with Context Usage Breakdown on a real session.
PowerShell:
Get-ChildItem -Recurse *.mdc,*.md | Select-Object Name, @{n='Lines';e={(Get-Content $_).Count}} | Sort Lines -DescBash:
find . -name '*.mdc' -o -name '*.md' | xargs wc -l | sort -rn -
Keep rules readable for humans too — severity tags (
MUST:) are clearer than**Required before all else:**and shorter. -
Don’t over-compress: if a rule becomes cryptic without reading 3 docs, it’s too short. The goal is “trigger condition + reference”, not “cipher”.
Happy to share the full shorthand dictionary format, a line-count helper, or help with applying the backbone-doc pattern to a specific rule.