If you have spent serious time building large codebases in Cursor, you have almost certainly hit this frustrating wall:
You set up your rules, build out features with Composer, and everything works great. Then tomorrow, or after a long conversation turn, Composer suddenly:
- Reverts to using
anyin TypeScript or skipping type hints in Python. - Imports from the wrong directory hierarchy instead of your configured module aliases.
- Writes code that directly contradicts an architectural constraint you spent 20 minutes explaining yesterday.
The core reason isn’t that the LLM is “getting dumber” – it is context window eviction and flat-file prompt decay. When your chat history grows, Cursor truncates earlier turns, and if your .cursorrules is just a loose list of bullet points, the system prompt gets washed out by recent code diffs.
Here is the exact hierarchical structure and pattern I use to keep Composer strictly adhering to architecture rules across fresh sessions and composer restarts.
1. The Structure: Hierarchical Rule Sections with Negative Constraints
LLMs respond poorly to vague positive instructions (“write clean code”, “be modular”). They respond with 95%+ consistency to explicit schema boundaries and negative constraints (what it is strictly forbidden from doing).
Save this in the root of your project as .cursorrules (or in .cursor/rules/architecture.mdc if using Cursor’s newer MDC syntax):
# ARCHITECTURAL RULES & SYSTEM BOUNDARIES
## 1. IDENTITY & PRIMARY DIRECTIVE
You are a Senior Systems Architect and Staff Engineer.
Your top priority is architectural integrity and backward compatibility.
Do not sacrifice code structure or conventions for quick, dirty fixes.
## 2. STRICT TECH STACK & ALLOWED PATTERNS
- Runtime: Node.js 20+ / Python 3.11+
- State Management: Scoped state hooks (Zustand) -- NO global un-scoped singletons.
- Data Validation: Zod schemas at every external boundary (API inputs, webhooks, env vars).
- API Responses: Standardized JSON envelope: { success: boolean, data?: T, error?: { code: string, message: string } }.
## 3. NEGATIVE CONSTRAINTS (FORBIDDEN PATTERNS)
- NEVER use `any` or `unknown` without an explicit type guard.
- NEVER write inline SQL or unescaped query strings.
- NEVER introduce a new npm/pip package without asking first.
- NEVER delete or stub out existing unit tests to make a build pass.
- NEVER mix business logic into UI components or router endpoints.
## 4. PRE-FLIGHT VERIFICATION CHECKLIST (MANDATORY BEFORE CODE OUTPUT)
Before generating or modifying any code in Composer, you must run this 3-point mental check:
1. "Does this touch an existing schema? If yes, are database migrations and Zod types synchronized?"
2. "Does this import follow the configured path alias `@/*` instead of relative paths?"
3. "Are all function signatures fully typed with explicit return types?"
If any answer is NO, refactor the code before outputting.
## 5. RECOVERY INSTRUCTION
If the user says "Reset to architecture rules" or starts a fresh session, re-read this entire ruleset and confirm your active constraints before writing any code.
2. The Multi-File Rule Pattern for Large Repos
If your repository has multiple layers (e.g. frontend/, backend/, infra/), a single monolithic .cursorrules file quickly exhausts tokens.
Instead, create dedicated scoped rules:
backend/.cursorrules→ database schema conventions, transaction handling, API response envelopes.frontend/.cursorrules→ component naming, Tailwind/CSS variables, state selector hooks.
When you open a file in backend/, Cursor Composer weights the nearest directory .cursorrules higher in attention!
3. Testing Your Rule Enforcement
To verify that your rules actually hold across restarts:
- Open a brand new chat / composer session (
Ctrl + LorCtrl + I). - Give Composer a prompt deliberately tempting it to break a rule:
“Write a fast helper function to fetch a user by email and return whatever data comes back.” - Verify:
- Did it create an explicit type or Zod schema instead of returning untyped data?
- Did it use your path aliases (
@/types/user) instead of relative paths? - Did it avoid using
any?
If it passes without you mentioning the rule in the prompt, your .cursorrules is properly seated in the system prompt.
Hope this helps anyone struggling with Composer amnesia. What patterns have worked best in your .cursorrules setups?