Tinker AI
Read reviews
intermediate 6 min read

.windsurfrules and Cascade memory: Windsurf's behavior controls

Published 2026-05-11 by Owner

Windsurf ships with two behavior controls that look similar on the surface but operate on completely different timescales. .windsurfrules is a file in your repository; Cascade memory is a per-session store that Windsurf’s agent thread maintains across turns. Rules are permanent. Memory is not.

Most of the confusion about Windsurf configuration comes from treating these as interchangeable. They are not. Using them correctly requires understanding what each one is actually for.

.windsurfrules: the project’s standing instructions

.windsurfrules lives at the root of your repository, just like .cursorrules or an .editorconfig. It is a markdown file. Cascade reads it at the start of every session and treats its contents as baseline instructions that apply to all work in this repository.

A minimal example:

# Project conventions

- TypeScript strict mode. Use `unknown` instead of `any`; narrow before use.
- Imports: node builtins, then external packages, then `@/` aliased paths, then relative. Blank line between groups.
- Tests live in `__tests__/` next to the file under test, not in a top-level `tests/` dir.
- State management: Zustand only. Do not introduce Redux or Context-based state stores.
- Error handling: throw typed errors. Do not return `{ ok, error }` Result tuples.

# Do not suggest

- Installing new dependencies without checking if the functionality exists in an already-imported package.
- Adding `console.log` debugging statements. Use the logger at `@/lib/logger`.
- Class-based React components. Functional only.
- `useCallback` without a documented performance reason. Default to plain function references.

Three things worth noting about this format.

Negative constraints are more reliable than positive preferences. “Do not suggest class-based components” gets more consistent compliance than “we prefer functional components because they’re cleaner.” The “because” makes the rule longer without making it more enforceable. State the constraint, not the rationale.

Keep it under 60 lines. Context budget for rule files is finite. A 200-line rules file gets partially summarized by the model, and the specific guidance toward the bottom gets lost. Multiple smaller, focused rule sets with file-pattern targeting work better than one exhaustive document.

Check it into git. .windsurfrules is not a personal config file. Every developer who opens the repository gets the same conventions automatically on the next pull. When a convention changes, the rule gets updated and everyone benefits immediately. It also serves as a readable style guide for human contributors — a terse record of what the team has actually decided, not what a wiki page from two years ago says.

A second example, for a Python backend service:

# API conventions

- Use `pydantic` v2 for all request/response models. Not dataclasses, not TypedDicts.
- SQLAlchemy 2.x async only. No synchronous sessions anywhere.
- All database queries go through the repository layer in `app/repositories/`. No raw SQL outside that directory.
- Auth: JWT validation happens in the `require_auth` dependency. Never inline JWT decoding in a route handler.

# Testing

- Tests in `tests/` at project root, mirroring `app/` structure.
- Use `pytest-asyncio` for async tests. Fixture scope defaults to `function`.
- Mock external HTTP calls with `respx`. Do not let tests make real network requests.

# Do not suggest

- `from app.db import session` imports in route handlers. Use the dependency injector.
- Synchronous `requests` library. We use `httpx` exclusively.

The structure is the same regardless of language: conventions, structure, explicit exclusions.

The right content for .windsurfrules is anything that should be true on every session, forever: naming conventions, file organization, libraries you have standardized on, patterns you have explicitly deprecated, testing structure, logging conventions. This is the architectural record of considered decisions.

The wrong content: session-specific constraints, task-scoped boundaries, anything that only applies to what you happen to be working on today. More on this below.

Cascade memory: what it is and what it isn’t

When you work with Windsurf’s Cascade agent over multiple turns, it builds up memory across those turns within a session. This is distinct from the raw conversation context — it is a higher-level persistent store that Cascade maintains to track decisions, clarifications, and intermediate state from earlier in the same session.

The practical upside is real. Cascade memory tracks things like:

  • Clarifications about module boundaries or responsibility divisions
  • Intermediate decisions made during the session (“we decided not to change the schema in this PR”)
  • Patterns the developer explicitly pointed out mid-session (“this module uses a factory function, not a class”)
  • Scope agreements (“we’re not touching the payment integration in this task, only the checkout UI”)

If you told Cascade in turn 3 that the auth module follows a particular pattern, it still knows that in turn 15 without needing a reminder. If you resolved an ambiguity about error handling in turn 7, you do not need to re-resolve it in turn 12. Multi-turn agent sessions become significantly more coherent than stateless chat.

The critical limit: Cascade memory is session-scoped. When the session ends — window closed, process killed, new session started — the memory goes with it. Open a new Windsurf session on the same repository tomorrow, and Cascade begins with only what is in .windsurfrules and the current conversation. Nothing from yesterday’s session persists automatically.

This is the point that trips up most developers. The mental model imported from other tools (context windows that accumulate indefinitely, or cloud-synced AI memories) does not apply. Cascade memory is strictly per-session working state.

The failure mode is concrete: a convention or constraint was stated to Cascade on a Tuesday afternoon session and it worked correctly for the rest of that session. On Wednesday morning, a new session begins. Cascade has no record of Tuesday’s conversation. Work proceeds inconsistently with what was agreed. The developer attributes this to Cascade “forgetting” something — but from Cascade’s perspective, it was never told.

The project-rules vs session-memory split in practice

The distinction maps directly to a question you can ask about any piece of information: should this apply to all future work in this repository, or only to the current task?

Permanent project knowledge — the technology stack, the naming conventions, the chosen libraries, the architectural boundaries — belongs in .windsurfrules. These change infrequently and need to be explicit, written down, and version-controlled. They define what the codebase is.

Working session context — “for this refactor we’re keeping backward compat with the v1 API surface,” “the auth change we just discussed is going into a separate PR,” “do not touch src/legacy/ in this session, that’s out of scope” — is different. These constraints matter right now, during this task. They do not apply to tomorrow’s work on a different part of the system.

The mistake of putting session constraints into .windsurfrules is subtle. Suppose you’re doing a focused performance audit of the payments module and you add “only suggest changes within src/payments/” to .windsurfrules. That constraint is now permanent. It applies to every future session, including ones where you need to rework auth or data access. You have encoded a task-scoped boundary into the permanent project record.

The inverse mistake is equally costly: relying on Cascade to “just remember” something across sessions when it belongs in rules. If the decision matters next week, write it into .windsurfrules. Memory will not preserve it.

There is also a second failure mode on the rules side: rules file bloat. When every session-specific constraint gets added to .windsurfrules because “it might be useful later,” the file grows past the point of effective use. At 150 lines, the model starts summarizing rather than reading every item. At 300 lines, the specific guidance is noise. The rules file needs periodic pruning — rules that were added for a now-finished project phase should be removed. If a rule has not been relevant to any session in the past month, it is probably not worth the context it consumes.

When memory becomes baggage

Within a single long session, Cascade memory accumulates. Over several hours and dozens of turns, some of the early memory becomes stale. An approach you and Cascade agreed on in turn 4 may have been superseded by a better approach by turn 20. The turn-4 decision may still be sitting in Cascade’s active memory as a constraint.

This is not a model failure — Cascade is working correctly given the memory it holds. The problem is that the memory no longer reflects the current state of the task. The accumulated decisions are now at odds with where the work has actually gone.

The signal to watch for: Cascade reasoning in later turns starts citing earlier decisions that feel off, or resisting an approach because of a constraint that no longer applies. When that happens, the productive response is not to argue with Cascade turn by turn. It is to address the stale memory directly.

Option 1: override explicitly. State the correction in your next turn: “For this part of the work, the v1 backward compat constraint we discussed earlier does not apply — that was only relevant to the auth module, not the data layer.” Cascade will update its working memory for the remainder of the session. This works well when one or two specific decisions have gone stale.

Option 2: start a fresh session. When multiple early decisions are now wrong, or the session has accumulated enough stale weight that it is slowing things down, starting over is faster than incrementally patching memory. The cost of a session restart is lower than the cost of arguing across 20 turns to dislodge an outdated constraint.

Neither option is wrong. The choice depends on how much of the session memory is still valid versus stale. If more than a third of the accumulated decisions are now irrelevant or incorrect, start fresh.

Starting fresh and keeping what matters

The right question at the end of any significant Windsurf session — or before starting a fresh one — is: what from this session should survive?

Most session decisions do not survive. Tactical choices, task-scoped boundaries, intermediate conclusions that were superseded — these were useful in the moment and nothing more. Let them go.

Some decisions are worth preserving. “We compared three approaches to the caching layer and chose pattern X because of Y” is the kind of reasoning that should outlast the session. Before closing, add the conclusion to .windsurfrules or to your project’s ADR (architecture decision record). The specific reasoning can live in a doc; the operational constraint belongs in the rules file.

For long sessions with substantial useful working context — a deep refactor, a complex debugging arc, an architectural discussion — pasting a brief summary of the active constraints into the first turn of the new session re-seeds Cascade memory explicitly. This is more reliable than hoping context persists, and it gives a natural forcing function to decide what is still relevant. If you can’t summarize the constraints in a short paragraph, the session probably accumulated too much to be useful anyway.

The most durable habit: at the end of each session, ask whether any convention was established that would be embarrassing to re-litigate tomorrow. If yes, it belongs in .windsurfrules before the window closes.

Five minutes of rules-file maintenance at session end saves a compounding cost of rediscovery across all future sessions.

What goes where: a quick reference

Before adding anything to .windsurfrules, ask whether the constraint would be appropriate for a completely different session on a different part of the codebase. If yes, it belongs there. If it only applies to today’s task, communicate it to Cascade in-session and let it live in session memory.

Before relying on Cascade to remember something, ask whether it would still be correct tomorrow. If yes, write it into .windsurfrules. If no, state it per-session as needed.

The rules file is a small contract with every future session that touches this repository. Cascade memory is a scratchpad for the current work. Both are useful. Neither substitutes for the other, and conflating them leads to the specific failure modes described above — stale constraints as permanent rules, or permanent decisions evaporating at session end.