Windsurf / persistent guidance
Windsurf rules: keep your guidance separate
.devin/rules/*.md; .windsurf/rules/*.md is a legacy fallback. Our Windows OpenSpec test preserved custom rules but replaced an edited generated workflow. File generation is tested here; Cascade loading and model compliance are Untested.The old Windsurf documentation URL now redirects to Devin Desktop documentation. Do not silently apply a current desktop feature to an older installed client. We did not run either desktop client or submit a model task.
Choose the rule location and activation
The current rules guide describes a shared global file at ~/.codeium/windsurf/memories/global_rules.md and per-project Markdown rules. Global guidance is continuous. A workspace rule selects one of four triggers:
| Trigger | Documented purpose |
|---|---|
always_on | Continuous guidance |
glob | Guidance tied to matching files |
model_decision | Description-led selection by Cascade |
manual | Explicit rule mention |
Choose the smallest scope that expresses the constraint. This is our example for an evidence-reporting rule, not a vendor template:
---
trigger: always_on
---
Report the actual validation command and its output.
Label any check you did not execute Untested.
Keep location-scoped shared guidance in the existing AGENTS.md intent; this article owns the Windsurf-specific rules and integration question.
Eight fixtures, seventeen actual CLI operations
We used the official, already-installed OpenSpec CLI, not a substitute loader. Each case started in a new temporary repository directory with separate XDG configuration and data directories. Telemetry was disabled in the child environment. Each invocation received closed stdin. The harness removed credential-like environment entries and never performed authentication or a model request.
openspec --version
# Actual output: 1.13.2
openspec init --tools windsurf --profile core --no-animation
openspec update --force
The executable was invoked through Node using an absolute path to bin/openspec.js. The version-pinned CLI accepted windsurf, reported Devin Desktop, and wrote six workflow files plus six skill files under .devin/. The fresh fixture had fifteen total files. It did not create a custom rule file.
| Case | Controlled change | Observed after update | Boundary |
|---|---|---|---|
| 01 Fresh | No pre-existing guidance | 6 workflows, 6 skills; no rules generated | Not a Cascade rule listing |
| 02 Legacy custom | Legacy rule, custom workflow, root .windsurfrules | All 3 original files retained byte for byte | Retention is not activation |
| 03 Current custom | Custom rule and workflow in .devin/ | Both original files retained byte for byte | No rule precedence measured |
| 04 Edited generated | Replace generated opsx-propose.md with 97 bytes | Restored to its original 15,926-byte hash | Generated body is replaceable |
| 05 Missing generated | Move that generated file outside the test repository | File recreated with its initial hash | Recovery of this file only |
| 06 Repeat init | Run the same init a second time | No duplicate workflow paths; custom rule unchanged | Not a multi-root client test |
| 07 Both roots | Different custom files with matching names in both roots | All 4 original files retained byte for byte | Does not identify a client winner |
| 08 BOM / CRLF | UTF-8 BOM, Windows line endings, and a .txt rule sibling | Both original files retained byte for byte | Does not establish parser acceptance |
All seventeen operations returned exit zero. We counted files from the recorded filesystem manifests and compared SHA-256 digests; we did not infer preservation from the terminal's success message. Case 06 accounts for the extra invocation: eight init calls, eight updates, and one repeated init.
Observation CSV Complete command captures Version and scope record
A generated file can exceed the documented workflow ceiling
Our fresh fixture's workflows were larger than the CLI's file count suggests. We read their actual UTF-8 bytes, counted JavaScript UTF-16 units and Unicode code points, and checked their hashes against the initial manifest. Both character counts agreed for these six files.
| Generated file | UTF-8 bytes | Code points / UTF-16 units | Compared with 12,000 |
|---|---|---|---|
opsx-apply.md | 10,492 | 10,476 | Below |
opsx-archive.md | 15,996 | 15,972 | Above |
opsx-explore.md | 19,742 | 19,734 | Above |
opsx-propose.md | 15,926 | 15,920 | Above |
opsx-sync.md | 15,547 | 15,535 | Above |
opsx-update.md | 10,974 | 10,974 | Below |
The current workflow documentation states a 12,000-character limit. Four generated files exceed it under both counts. This is a measured artifact/documentation mismatch, not evidence of rejection, truncation, or a broken model task. We have not observed what a desktop client does with these files.
A terminal exit code alone cannot close that gap. Keep the file and its version, then inspect it in the intended client before relying on the workflow. If you choose to shorten a generated body, retain the original and recheck it after any refresh. Case 04 shows why an edit that looks successful today can disappear on the next forced update.
Keep customization outside the replaceable surface
Case 04 is deliberately small: its modified workflow had ninety-seven bytes, while the original had 15,926. After the forced refresh, the resulting digest matched the initial generated file, not our edit. The adjacent custom rule survived. This supports separating custom guidance from generated integration files in this version and these fixtures; it does not promise that every custom filename is safe under every tool.
Case 05 moved the workflow to an evidence file outside the test repository rather than deleting its only copy. A forced update recreated the absent path with the same initial digest. The retained copy makes the before/after comparison recoverable. Do this in a disposable repository before treating an update command as a repair for your real project.
Case 07 kept differently worded files with matching names under both directory roots. Their digests stayed distinct and unchanged. That is a file-preservation result, not an answer to which rule Cascade would select. Likewise, case 08 retained the BOM, line endings and wrong-extension sibling. A writer leaving a file alone does not demonstrate that a reader accepts it.
Our practical checklist is therefore three separate records: the path and hash you placed, the generated paths and hashes after refresh, and the actual client's evidence for loading or invocation. Do not replace the third with the first two. We have the first two; the third remains Untested.
Where workflows fit
Windsurf workflows describe an explicitly invoked sequence, rather than the standing constraints in a rule. The current guide places them in .devin/workflows/ with legacy .windsurf/workflows/ support and describes invocation by slash name. It also limits these workflows to Cascade, not Devin Local.
The OpenSpec tool reference explains its current alias and agent-specific invocation. Our measured CLI printed /openspec-propose as its getting-started hint while writing opsx-propose.md as a workflow. Keep the selected agent visible when deciding which entry point to use; neither hint was executed inside a desktop agent in this lab.
Replay without touching your project
The downloadable harness embeds all eight starting fixtures. It makes fresh temporary directories, calls your already-installed official CLI, saves complete stdout/stderr and before/after manifests, and prints the result directory. It does not install software, purchase access, log in, or open a model session. It keeps the moved file and the original edited file as evidence.
Self-contained replay script Exact starting fixtures
node windsurf-rules-replay.mjs C:\path\to\node_modules\@fission-ai\openspec\bin\openspec.js
Read the version before comparing outputs. Count the final paths, compare each starting custom file's digest, and check the restored workflow against its initial digest. The JSONL envelopes preserve failed and successful process fields rather than reconstructing terminal messages from a summary. CSV is a convenience view, not the original output.
This was not a Windsurf installation test. No Windsurf command was available on PATH and neither of two standard Windows installation paths contained Windsurf.exe in our scoped check. That is not a machine-wide absence proof. We did not inspect personal memories, change a global rules file, or run a desktop loader. Model compliance, trigger matching, root precedence, workflow acceptance and hot reload remain Untested.
FAQ: what does the evidence settle?
Did init create a rule for my project?
Not in our fresh 1.13.2 fixture. It created six skills, six workflows, and three OpenSpec files. A successful integration setup is not proof that a persistent custom rule exists. Check the actual manifest.
Why did my workflow edit disappear after update?
Our deliberate edit was overwritten by update --force, restoring the initial generated hash. Keep durable custom guidance in a separate file, and preserve a copy before refreshing generated files. This result is specific to the tested path and version.
Does keeping a legacy rule prove it loaded?
No. Our custom legacy files survived the CLI's writer. The experiment did not launch the desktop reader. Discovery, activation and model compliance need separate client evidence and remain Untested.
Are the four long workflows known to be rejected?
No. Their measured character counts exceed the current documented ceiling. Rejection and truncation were not observed. Do not describe an artifact-size discrepancy as a confirmed runtime failure.
Do both roots mean both rule bodies win?
No. We observed four custom files survive under the two roots; we did not observe a selection decision or generated answer. Avoid claiming an order or combining conflicting rules on the strength of file hashes alone.
Sources and limits
- Current rules documentation: locations and activation modes.
- Current workflow documentation: invocation, agent boundary and character ceiling.
- OpenSpec supported tools: current integration paths and naming.