Cursor Composer’s Shadow Workspace: 13 Steps [2026]

Cursor’s recurring revenue reportedly doubled in three months to $2 billion, according to the company’s own blog, and most of that growth rides on one quiet piece of plumbing: the moment between an AI model proposing a code change and that change landing on your disk. Everyone talks about “diff vs. rewrite” as if it is a single switch. It is not. Inside Cursor, a proposed edit passes through a planning model, a dedicated apply model called Fast Apply, and a validation layer known as the shadow workspace before you ever see an inline diff. That validation layer is the part almost nobody sets up correctly, tunes, or even knows exists.

This tutorial is not another walkthrough of Cursor’s diff-versus-rewrite decision (plenty of those exist already). It is a hands-on guide to the diagnostics gate that sits between the model’s output and your editor window, why it matters more as AI agents touch more files per session, and how to build your own version of it with real tools: TypeScript, ESLint, Vitest, and a git pre-commit hook. By the end you will have a working project that validates every AI-authored edit against your build, your linter, and your test suite before it ever reaches your working tree, using the same diagnostics-first philosophy Cursor applies internally.

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

What Cursor’s Shadow Workspace Actually Is (and Why Diff vs Rewrite Needs It)

According to technical breakdowns of Cursor’s architecture, the editor does not simply hand a model’s output straight to your file system. Instead, a proposed edit is checked in an off-screen copy of your project, sometimes called a shadow workspace, where the IDE’s language server and TypeScript compiler run diagnostics against the change before the inline diff is ever rendered. The goal is to catch a broken import, a mismatched type, or a syntax error while the edit is still invisible to you, rather than after you have already reviewed and accepted it.

This matters because of how Cursor actually applies edits. Cursor’s engineering team has described their Fast Apply system as a specialized model trained on “an important version of the full-file code edit task,” separate from the general-purpose reasoning model that decides what to change. The planning model proposes the change; Fast Apply merges that proposal into the existing file using a technique the team calls speculative edits, a code-specific adaptation of speculative decoding. Because most of a file is unchanged by any single edit, the system treats your existing text as a likely draft and only generates new tokens where the content actually diverges. Cursor’s own description of the result: “It is equivalent to a full-file-rewrite, while being up to 9x faster.”

That speed only matters if the output is safe to trust, and that is where the shadow workspace earns its keep. A model that can rewrite an entire 600-line file in under a second is also a model that can silently break that file in under a second. Nik Pash, writing under the handle Pash Pashpash, put it bluntly in a widely read critique of apply-model economics: “With Cursor’s Fast Apply model, a third option was introduced” beyond the old binary of slow-but-safe full rewrites and fast-but-fragile line-numbered diffs. The shadow workspace is the mechanism that keeps that third option from becoming a liability: it is the safety check that runs before you, the human, become the safety check.

Cursor co-founder Aman Sanger has also described the underlying diff-vs-rewrite tradeoff directly: “We find that fully rewriting the full file outperforms aider-like diffs for files under 400 lines.” That 400-line heuristic is specifically about which editing strategy Cursor’s apply model favors. The shadow workspace diagnostics step runs regardless of which strategy wins, because a full rewrite and a surgical diff can both introduce a broken reference, an unused import, or a type mismatch. Validation has to happen after the merge, not before it.

Inside the Pipeline: Planner Model, Fast Apply, and the Diagnostics Gate

It helps to see the whole pipeline laid out, because the shadow workspace is easy to miss if you only think about “which model wrote the code.” Cursor’s model picker currently includes Claude Sonnet 5.5, Claude Opus 5.5, Claude Haiku 5.5, GLM 5.3 and GLM 5.3 Flash from Z.ai, Grok 4.7, and Cursor’s own Composer 2.5 model, an upgrade over Composer 2 for long-running agentic tasks and complex multi-file instructions. Any of these can act as the planning model. What happens after planning is where the mechanics get interesting.

Pipeline StageWhat HappensTypical ComponentWhere You See It
PlanningA reasoning model decides what should change and drafts the intentComposer 2.5, Claude Sonnet 5.5, Grok 4.7, GPT-5.6 Sol, or another selected modelChat panel, agent timeline
ApplyA specialized model merges the proposed change into the full fileFast Apply, using speculative editsInvisible; happens between planning and diff
ValidationThe merged result is checked against the project’s build, types, and language serverShadow workspace diagnosticsInvisible; runs before the diff renders
PresentationA human-readable diff is rendered for reviewCursor’s inline diff viewEditor pane
DecisionYou accept, partially accept, edit, or reject the changeYouEditor pane, keyboard shortcuts

Reported descriptions say the apply stage runs quickly enough to feel instant, producing something comparable to rewriting the whole file without making you wait. The validation stage has no comparable published throughput figure, and it does not need one: diagnostics checks are typically fast because they reuse the same incremental language-server infrastructure your editor already runs for red squiggly lines, not a second model inference pass.

The practical takeaway for this tutorial: Cursor’s own validation step only protects you inside Cursor, using Cursor’s own language-server integrations. It does not run your project’s actual build command, your real ESLint config with your custom rules, or your test suite. If you want those checks to run on every AI-authored edit, including edits made through other tools, the Cursor CLI, or a cloud agent, you need to build an equivalent gate yourself. That is the project this tutorial walks through.

Prerequisites: Versions, Models, and Tools You’ll Need

  • Cursor IDE 3.23 or later. Check your version under Cursor > About Cursor.
  • Node.js 20 LTS or later, with npm 10+.
  • TypeScript 5.6 or later (npm install -g typescript).
  • ESLint 9.x with the flat-config format.
  • Vitest 2.x for the test-run gate.
  • Git 2.40 or later, plus Husky 9.x for the pre-commit hook.
  • A Cursor plan with access to at least one first-party or opt-in model. As of the October 12, 2026 X Subscription changes, Composer 2.5 and Auto are included on the Premium tier ($30/month) and above; Free ($0) and Plus ($8/month) carry lighter included usage.
  • Basic command-line comfort: you will write and run three short Node scripts and one GitHub Actions workflow.

None of this requires Cursor’s Teams or Enterprise tier. The validation gate you are building runs independently of Cursor’s own shadow workspace, using your own project’s tooling, so it works the same way whether the edit came from Cursor’s Composer, the Cursor CLI, or a human typing by hand. This kind of project-owned guardrail is becoming a standard part of modern software development tooling, precisely because AI agents now touch more of a codebase per session than a human reviewer can reasonably eyeball in real time.

Steps 1-3: Update Cursor, Confirm Diagnostics Are On, and Scaffold a Flawed Test Project

Step 1: Update Cursor and Confirm Your Build

Open Cursor, go to Cursor > Check for Updates, and confirm you land on 3.23 or later. Version 3.23 added clearer plan-and-usage information, including usage-reset dates and prepaid credit tracking, which matters later when you are deciding how much agent usage to budget for repeated validation runs on larger files.

Step 2: Verify Diagnostics-Aware Editing Is Enabled

In Cursor Settings, open the Models tab and confirm your active model supports Fast Apply (most first-party and major third-party models do by default; support varies by model and plan). There is no toggle labeled “shadow workspace” in the settings UI; the diagnostics check runs automatically whenever your project has a working TypeScript or language-server configuration. If your project has no tsconfig.json, Cursor has nothing to validate against for type errors, which is itself a useful diagnostic: it tells you your own setup is incomplete before you even start testing AI edits.

Step 3: Scaffold a Deliberately Flawed Test Project

Create a throwaway project so you can watch the validation behavior without risking real code:

mkdir shadow-gate-demo && cd shadow-gate-demo
npm init -y
npm install --save-dev typescript eslint vitest husky
npx tsc --init --strict --target ES2022 --module ESNext
mkdir src
cat > src/invoice.ts <<'EOF'
export type Invoice = {
  id: string;
  total: number;
  lineItems: { label: string; amount: number }[];
};

export function sumInvoice(invoice: Invoice): number {
  return invoice.lineItems.reduce((acc, item) => acc + item.amount, 0);
}
EOF

This is intentionally small (under 400 lines), which puts it squarely in the range where Cursor’s own research favors full-file rewrites over search/replace diffs. That makes it a good fixture for comparing both edit styles side by side in the next section.

Steps 4-6: Trigger a Diff Edit, a Full Rewrite, and Compare Diagnostics Behavior

Step 4: Trigger a Targeted Diff-Style Edit

Open src/invoice.ts in Cursor and select just the sumInvoice function. Ask Composer to “add a discount percentage parameter that reduces the total.” For a small, localized change like this, Fast Apply typically produces a search/replace-style edit rather than regenerating the whole file, since the change is narrow. Watch the diff view: before it appears, there is a brief pause where nothing visibly happens. That pause is largely the apply model merging the change and the diagnostics check running against the shadow copy.

Step 5: Trigger a Full-File Rewrite

Now select the entire file and ask Composer to “restructure this to support multiple currencies, with a currency field on each line item and a function that groups totals by currency.” This is a broad, structural change across every function in the file, which is the kind of request that tends to produce a rewrite-equivalent edit rather than a narrow patch. Note how the diff view presents it: Cursor still shows you a line-level diff even when the underlying mechanism was a full-file regeneration, because the inline diff is a presentation layer, not a record of which strategy produced it.

Step 6: Deliberately Break Something and Watch the Gate Catch It

Ask Composer to “rename the total field to grandTotal everywhere” but stop it mid-generation if you can, or prompt a partial rename on purpose by asking it to rename only in one function. If the result leaves a dangling reference to the old field name anywhere in the file, Cursor’s diagnostics will often surface a red underline in the diff view itself, before you accept anything, because the shadow copy already ran the TypeScript compiler against the proposed result. This is the shadow workspace doing its job: the error surfaces inside the review step, not after you have merged a broken rename into your branch.

That said, Cursor’s own diagnostics only know about TypeScript and language-server errors. They do not run your actual npm test, your ESLint custom rules, or a build step that involves bundler-specific logic. For that, you need your own gate, which is what the rest of this tutorial builds.

Steps 7-9: Write Cursor Rules and Build Your Own Shadow-Build Script

Step 7: Write a Cursor Rules File That Requires Diagnostics-Clean Edits

Cursor Rules let you steer agent behavior at the project level. Create .cursor/rules/validation.mdc:

---
description: Require diagnostics-clean edits before finishing a task
alwaysApply: true
---

- After editing any .ts or .tsx file, run `npx tsc --noEmit` on the affected
  file's project and fix any new errors it introduces before ending the turn.
- Never leave an unresolved import, unused variable, or type mismatch that
  did not exist before your edit.
- For edits touching more than one file, re-run the type check against the
  whole project, not just the files you changed.
- If a requested change cannot be made without breaking type safety, say so
  explicitly instead of silently suppressing the error with `any` or a
  `// @ts-ignore` comment.

This nudges the agent toward self-correction, but a rules file is a request to the model, not an enforcement mechanism. The model can still finish a turn with an error if it misjudges the fix. That is the gap your own shadow-build script closes: a deterministic, non-negotiable check that runs outside the model entirely. For more on tuning this kind of rules-based behavior, see this site’s guide to Cursor Rules for diff vs. rewrite edits, which covers the broader rules syntax this tutorial assumes you already have in place.

Step 8: Build the Shadow-Build Script

This script copies the current working tree into a temporary directory, applies the pending changes, and runs a real build and type check against the copy, independent of Cursor’s internal shadow workspace. Create scripts/shadow-build.mjs:

import { execSync } from "node:child_process";
import { mkdtempSync, cpSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

function run(cmd, cwd) {
  try {
    execSync(cmd, { cwd, stdio: "pipe" }).toString();
    return { ok: true };
  } catch (err) {
    return { ok: false, output: err.stdout?.toString() || err.message };
  }
}

const shadowDir = mkdtempSync(join(tmpdir(), "shadow-build-"));
console.log(`Shadow copy created at ${shadowDir}`);

cpSync(process.cwd(), shadowDir, {
  recursive: true,
  filter: (src) => !src.includes("node_modules") && !src.includes(".git"),
});
cpSync(join(process.cwd(), "node_modules"), join(shadowDir, "node_modules"), {
  recursive: true,
});

const typeCheck = run("npx tsc --noEmit", shadowDir);
const lint = run("npx eslint . --max-warnings=0", shadowDir);
const tests = run("npx vitest run", shadowDir);

rmSync(shadowDir, { recursive: true, force: true });

const results = { typeCheck, lint, tests };
const failed = Object.entries(results).filter(([, r]) => !r.ok);

if (failed.length) {
  console.error(`Shadow build failed: ${failed.map(([k]) => k).join(", ")}`);
  failed.forEach(([k, r]) => console.error(`\n--- ${k} ---\n${r.output}`));
  process.exit(1);
}

console.log("Shadow build passed: type check, lint, and tests all clean.");

Run it manually first to confirm it works against the invoice project:

node scripts/shadow-build.mjs

Step 9: Add a Package Script Alias

In package.json, add a shortcut so the gate is easy to invoke from a hook, a CI job, or manually after a Composer session:

{
  "scripts": {
    "validate:shadow": "node scripts/shadow-build.mjs"
  }
}

Steps 10-11: Wire a Pre-Apply Git Hook and Add a Lint/Type-Check Gate

Step 10: Install the Pre-Commit Hook

Husky turns your shadow-build script into a gate that runs automatically, so an AI-authored edit cannot be committed until it passes:

npx husky init
echo "npm run validate:shadow" > .husky/pre-commit
chmod +x .husky/pre-commit

Now test it: ask Composer for an edit that you know will break something (reintroduce the field-rename problem from Step 6, for example), stage the change, and run git commit -m "test". The commit should fail, printing the shadow build’s error output instead of silently accepting a broken change. This is the same principle behind the git-based safety net approach for catching bad Fast Apply rewrites, applied specifically at the diagnostics layer rather than at the scope-of-change layer.

Step 11: Tighten the ESLint Configuration

A lint pass is only as good as its rules. Add project-specific guardrails that matter for AI-authored code specifically, since models tend to over-use any and leave unused imports behind after a rewrite:

// eslint.config.js
import tseslint from "typescript-eslint";

export default tseslint.config(
  ...tseslint.configs.recommendedTypeChecked,
  {
    rules: {
      "@typescript-eslint/no-explicit-any": "error",
      "@typescript-eslint/no-unused-vars": "error",
      "@typescript-eslint/no-floating-promises": "error",
    },
  }
);

These three rules alone catch a disproportionate share of the mistakes that slip through a rewrite-equivalent edit: a type silently widened to any to make an error disappear, an import left over from a deleted code path, and a promise that got reorganized out of its await chain during a restructure.

Steps 12-13: Benchmark Diagnostics Latency and Roll the Gate Into CI

Step 12: Measure How Much Latency the Gate Adds

Add timing to see the real cost of validation against Fast Apply’s reported speed. Wrap the script’s run calls:

const start = performance.now();
const typeCheck = run("npx tsc --noEmit", shadowDir);
console.log(`Type check: ${(performance.now() - start).toFixed(0)}ms`);

On a small single-file project like the invoice demo, expect the full shadow-build pass (copy, type check, lint, tests) to land in the low single-digit seconds, almost entirely dominated by the file copy and the TypeScript compiler’s cold start. That is slower than Cursor’s own in-editor diagnostics, which reuse an already-running language server rather than spinning up a fresh process, but it is validating against your real build and your real tests, not just type correctness.

Step 13: Add the Gate to GitHub Actions

A pre-commit hook only protects commits made on a machine that has the hook installed. Add the same check to CI so it also catches edits made through the Cursor CLI in a cloud agent session, or by any contributor who skipped hook setup:

# .github/workflows/shadow-gate.yml
name: Shadow Validation Gate
on: [pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run validate:shadow

This closes the loop started by the basic Composer diff-vs-rewrite setup workflow: that setup teaches you to read what Composer changed, while this CI job guarantees that whatever it changed still builds, lints clean, and passes tests, whether or not a human remembered to check locally first.

The Complete Working Project: Full File Layout

By the end of this tutorial, your project directory should look like this:

shadow-gate-demo/
├── .cursor/
│   └── rules/
│       └── validation.mdc
├── .github/
│   └── workflows/
│       └── shadow-gate.yml
├── .husky/
│   └── pre-commit
├── src/
│   └── invoice.ts
├── scripts/
│   └── shadow-build.mjs
├── eslint.config.js
├── package.json
└── tsconfig.json

Every AI-authored edit, whether it comes from Cursor’s Composer, the Cursor CLI, or a teammate pasting code from ChatGPT, now passes through the same three checks before it can be committed: a type check against a clean shadow copy, a lint pass with strict rules tuned for the mistakes AI edits tend to make, and your actual test suite. None of this depends on Cursor’s own internal shadow workspace being correct or even present, which matters if you ever switch editors, run a cloud agent, or review a pull request from a contributor using a different tool entirely.

Running It End to End: Sample Output

Here is what a passing run looks like after Composer makes the currency-restructuring edit from Step 5:

$ npm run validate:shadow

Shadow copy created at /tmp/shadow-build-x7k2pq
Type check: 1842ms
Lint: 603ms
Tests: 714ms
Shadow build passed: type check, lint, and tests all clean.

And here is a failing run, after the deliberately broken rename from Step 6:

$ npm run validate:shadow

Shadow copy created at /tmp/shadow-build-m9f4rz
Shadow build failed: typeCheck

--- typeCheck ---
src/invoice.ts(9,10): error TS2339: Property 'total' does not exist on type
'Invoice'. Did you mean 'grandTotal'?

husky - pre-commit hook exited with code 1 (error)

The commit is blocked, the exact line and the exact error are printed, and nothing partially-correct ever reaches your branch. That is the behavior Cursor’s shadow workspace gives you for free inside the editor; this project gives it to you everywhere else.

Common Pitfalls With Diagnostics-Gated AI Edits

  • Copying node_modules on every run. The shadow-build script above copies node_modules into the temp directory for simplicity, but on a large project this adds real seconds to every run. Switch to a symlink (fs.symlinkSync) once your project outgrows the demo size.
  • Treating a passing diagnostics check as a correctness guarantee. Type checks and lint rules catch structural problems, not logic errors. A function that type-checks perfectly can still compute the wrong discount percentage. Your test suite is the only layer that catches that, which is why the shadow-build script always runs tests, not just the compiler.
  • Letting Cursor Rules replace the hook instead of supplementing it. A rules file is a request the model can misjudge or skip under agent pressure to finish a task quickly. Only a hook or a CI job that exits non-zero is an actual gate.
  • Running the shadow build against a dirty working tree. If uncommitted, unrelated changes are sitting in your working directory when the hook fires, a failure can point you at the wrong file. Stash unrelated work before testing an AI edit in isolation.
  • Assuming the 400-line full-rewrite heuristic applies to every model. That threshold comes from Cursor’s own evaluation of its apply model specifically; other models and other tools may favor diffs or rewrites at different file sizes, so do not hard-code assumptions about which strategy a given edit used based on file length alone.
  • Skipping the ESLint max-warnings flag. Without --max-warnings=0, ESLint exits 0 even with warnings present, which means a growing pile of “temporary” any-types or disabled rules slips past the gate silently.
  • Forgetting that CI and local hooks can drift. If your local Node version differs from the one pinned in your GitHub Actions workflow, a shadow build can pass locally and fail in CI, or the reverse. Pin both to the same version explicitly.

Troubleshooting Guide

  • The hook never runs. Confirm .husky/pre-commit is executable (chmod +x) and that npx husky init actually registered the hooks path in your git config with git config core.hooksPath.
  • The shadow copy fails with “ENOENT: no such file” mid-copy. This usually means a file was deleted or renamed between the start of the copy and the end of it, often because an AI agent is still writing. Add a short delay or re-run after the agent reports it has finished.
  • tsc passes locally but fails in the shadow copy. Check whether your tsconfig.json uses relative paths that assume a specific working directory; the temp directory has a different absolute path, which can break path-mapping configurations.
  • ESLint reports errors that don’t appear in your editor. Your editor’s ESLint extension may be using a cached or looser config than the one the CLI picks up. Run npx eslint --print-config src/invoice.ts to see exactly what config is active.
  • Vitest hangs during the shadow build. Watch mode left on by default in some setups will hang a CI-style invocation forever. Make sure the script uses vitest run, not bare vitest.
  • The commit succeeds even though the shadow build printed an error. Check your hook script’s exit code propagation; a script that logs an error but does not call process.exit(1) will still report success to git.
  • GitHub Actions passes but the pre-commit hook fails (or vice versa). Compare Node versions and lockfile state; run npm ci rather than npm install in both places to eliminate dependency drift as a variable.
  • Large monorepo edits time out the shadow build. Scope the shadow copy to the specific package or workspace the edit touched rather than copying the entire monorepo; most monorepo tools (Turborepo, Nx) expose a way to resolve “affected” packages for exactly this purpose.

Advanced Tips for Large Codebases and Streaming Diagnostics

Once the basic gate works, a few refinements make it practical on a real codebase rather than a demo project. First, scope your shadow build to only the files an agent session actually touched, using git diff --name-only against your base branch rather than rebuilding the entire project on every commit; this is the single biggest latency win available and keeps the gate fast enough that nobody is tempted to bypass it with --no-verify.

Second, if you are running Cursor’s Cloud Agents against environments with up to 300 repositories (a capability Cursor added in its October 16, 2026 Cloud Agent Canvases and Environment Improvements release, which the company says loads large environments roughly five times faster than before), apply the same shadow-build principle at the environment level: a cloud agent’s proposed multi-repo change should pass the same type-check-lint-test gate before a pull request opens, not after a human notices something is broken in review.

Third, for multi-file edits specifically, run the type check once against the whole affected package rather than file-by-file; TypeScript’s cross-file inference means a change that type-checks in isolation can still break a caller in a different file, and Cursor’s own shadow workspace diagnostics benefit from the same whole-project view rather than a single-file one.

Fourth, consider streaming diagnostics output back into your agent’s context. If your shadow-build script’s failure output is fed back into the next Composer prompt automatically (via a Cursor Hook, for example, which runs arbitrary scripts at defined points in the agent lifecycle), the model can self-correct against your real build errors in the same turn, rather than requiring you to paste the error back manually. This turns the gate from a pass/fail checkpoint into a feedback loop the agent can use directly.

Finally, keep an eye on token cost if your validation loop triggers repeated agent turns. Composer 2.5 is priced at $0.50 per million input tokens and $2.50 per million output tokens in its standard configuration, with a faster variant at $3 per million input tokens and $15 per million output tokens; a self-correction loop that runs three or four times per edit on a large file can meaningfully change your monthly usage. The Cursor Composer cost guardrail setup covers how to cap that kind of runaway iteration before it becomes an expensive habit.

How Cursor’s Shadow Workspace Compares to Aider, Copilot, Windsurf, Cline, and Zed

Publicly available documentation does not describe the internal pre-apply validation architecture of every competing tool in the same technical depth Cursor has published for its own Fast Apply and diagnostics approach. What is confirmed, in general terms, is how each tool presents and applies edits:

ToolEdit-Application ApproachBuilt-In Pre-Apply ValidationPrimary Interface
CursorFast Apply: speculative edits producing a rewrite-equivalent resultShadow workspace diagnostics before the diff is renderedIDE (VS Code fork) and CLI
AiderSearch/replace blocks or unified diffs, modeled on git-style patchesRelies on the lint and test commands you configure yourselfTerminal / CLI
GitHub CopilotInline suggestions and chat-driven multi-file editsImplementation not publicly documented at this level of detailIDE extension (VS Code, JetBrains, Visual Studio)
WindsurfCascade agent proposes and applies multi-step editsImplementation not publicly documented at this level of detailStandalone IDE
ClineDiff-based file edits applied through a VS Code extensionImplementation not publicly documented at this level of detailVS Code extension
ZedNative multibuffer edits rendered inline across open filesImplementation not publicly documented at this level of detailStandalone editor (Rust-based)

The practical lesson from this gap is not that other tools lack validation, it is that you should not assume any tool’s internal validation covers your project’s specific build, lint rules, and tests. The shadow-build project in this tutorial is deliberately tool-agnostic: it runs the same way whether the edit came from Cursor’s Composer, Aider’s search/replace blocks, a Copilot chat edit, or a human. That portability is the point. A detailed comparison of how Cursor’s own interface stacks up against a terminal-first tool is covered in this site’s Cursor vs. Claude Code editor-first versus terminal-first breakdown, if you want the broader workflow context beyond just the apply mechanism.

If you want a deeper, CLI-specific view of how Cursor’s own numbers hold up under repeated testing, the Cursor CLI diff-vs-rewrite benchmark on this site runs a fixed prompt matrix across file sizes to measure latency directly, which pairs well with the shadow-build timing numbers from Step 12 above. And if your interest is less in validating edits and more in classifying which edits were diffs versus rewrites after the fact, the Composer rewrite audit tool tutorial builds exactly that kind of classifier, while the from-scratch Fast-Apply patch engine tutorial goes a layer deeper and builds the decision logic itself rather than validating someone else’s output.

Why This Matters Beyond a Single Editor

Anysphere, the company behind Cursor, stated on its blog that Cursor had reportedly become part of SpaceX in August 2026. Separately, on October 12, 2026, Cursor joined the X Subscription, which bundles Grok, Grok Bot, and X Premium with Cursor under one plan structure. One practical effect is that Grok 4.7, Cursor’s own Composer 2.5, and several Anthropic and Z.ai models now sit side by side in the same model picker, each potentially producing diff-style or rewrite-style edits depending on the request and the file. The shadow workspace’s diagnostics check runs the same way regardless of which model proposed the edit, which is exactly the kind of model-agnostic safety net you want if your team is going to keep switching between models as pricing and capability shift.

That consolidation is also a reminder of why a project-owned validation gate, independent of any single vendor’s editor, is worth the half-day of setup this tutorial takes. According to xAI’s own announcement, reported by a detailed timeline of xAI and Grok’s 2026 history, the X Subscription’s Premium tier bundles benefits previously sold separately as SuperGrok, Cursor Pro, and X Premium+, which together used to cost roughly $90 a month before the October 2026 restructuring folded them into a single $30 plan. Pricing structures, plan names, and even company ownership can change quickly in this market; a shadow-build script checked into your own repository does not depend on any of that.

PlanMonthly PriceAnnual PriceRelevant to Composer / Fast Apply
Free$0–Limited daily access; starter allowance for Cursor coding agents
Plus$8$80/yearMore included usage than Free, still first-party models only
Premium$30$300/yearSuccessor to legacy Pro; includes Composer 2.5 and Auto in the first-party pool
Super$100$1,000/yearHigher included usage for daily agent and Fast Apply workloads
Ultra$200$2,000/yearHighest individual usage tier under the X Subscription structure

Legacy plans, including the older $20-per-month Pro tier and the $60-per-month Pro+ tier, are now closed to new sign-ups, according to Cursor’s October 2026 forum communications, covered in detail by Cursor HQ’s announcement roundup, though existing subscribers on those plans were reportedly able to keep them for the time being. Third-party models such as Claude, GPT, and Gemini remain available on Premium and above, but their usage is billed separately at API list prices through prepaid credits rather than bundled into the flat monthly fee, which is worth knowing before you build a validation loop that triggers repeated model calls.

Measuring Whether the Gate Is Actually Helping

A validation gate that nobody trusts gets bypassed, and a gate that is too slow gets disabled the first time it blocks a deadline. Before you roll this out beyond your own machine, track three numbers for a couple of weeks: how often the shadow build fails on a genuinely bad edit (a true positive), how often it fails on something that was actually fine (a false positive), and how many seconds it adds to an average commit. If false positives climb, the usual culprit is an ESLint rule that is stricter than your team’s actual standards, or a test that is flaky rather than meaningfully broken; fix the rule or quarantine the flaky test rather than loosening the gate wholesale, or you will end up right back where you started, trusting AI-authored edits without a real check.

It also helps to log which stage fails most often: type check, lint, or tests. On most TypeScript projects running this kind of gate against AI-authored edits, type errors tend to dominate early on, because a model restructuring a file can lose track of a type that was correct before the edit but is not correct after it, especially across a rewrite-equivalent change rather than a narrow diff. As your Cursor Rules file (from Step 7) gets more specific about what the model should double-check before finishing a turn, the balance tends to shift: fewer type failures reach the gate at all, because the model catches them itself first, and the gate increasingly exists as a backstop for the cases the model missed rather than the first line of defense. That shift, from the gate catching most problems to the gate catching only the hardest ones, is itself a useful signal that your Rules file and your shadow-build script are working together instead of duplicating each other’s job.

One more thing worth tracking: whether the gate changes how large an edit you are comfortable asking for. Teams that trust their validation layer tend to request broader, more structural changes from Composer, the kind of full-file rewrite Cursor’s own research says outperforms narrow diffs above certain complexity thresholds, because they know a mistake will surface before it reaches the branch rather than during code review three days later. Teams without that safety net tend to over-correct toward small, cautious, line-by-line prompts that are slower to iterate on and do not take advantage of what Fast Apply is actually built to do. The gate, in other words, is not just a safety mechanism. It changes what kind of AI-assisted editing you are willing to attempt in the first place.

Frequently Asked Questions

What is Cursor’s shadow workspace, in plain terms?
It is an off-screen copy of your project where Cursor checks a proposed AI edit against your TypeScript compiler and language server before showing you the inline diff, catching broken references and type errors before you ever review the change.

Does Cursor always choose between a full rewrite and a diff, or can it do both in one session?
Both can happen in the same session. Cursor’s apply model, Fast Apply, evaluates each requested edit independently; a narrow one-line fix might produce a search/replace-style patch while a broad structural change on the same file might produce a full-file, rewrite-equivalent result.

How fast is Fast Apply compared to a normal full rewrite?
Cursor’s own engineering team describes it as equivalent to a full-file rewrite while being up to 9 times faster, with reported throughput around 1,000 tokens per second, achieved through a technique the team calls speculative edits.

Can I force Cursor to validate an edit against my own lint and test setup before I accept it?
Not natively inside the editor’s accept/reject flow. That is exactly the gap this tutorial’s shadow-build script, pre-commit hook, and CI workflow close, running your real build, lint, and test commands against every AI-authored change before it can be committed.

Does the shadow workspace diagnostics check work the same way for every model Cursor supports?
The diagnostics layer itself is model-agnostic; it checks the resulting file, not which model produced it. That means Composer 2.5, Claude Sonnet 5.5, Grok 4.7, and any other supported model all pass through the same validation step after their edit is applied.

What happens if the shadow workspace or your own shadow-build script catches an error?
Inside Cursor, diagnostics typically surface as inline warnings in the diff view itself before you accept anything. In the custom gate built in this tutorial, a failing shadow build exits with a non-zero status, blocks the git commit, and prints the exact file and line where the check failed.

How do Aider, GitHub Copilot, Windsurf, Cline, and Zed compare on pre-apply validation?
Publicly available documentation does not detail each tool’s internal validation architecture to the same depth Cursor has published for Fast Apply. What is known is each tool’s general edit-application style: Aider uses search/replace or unified diff blocks, Copilot and Cline apply inline or diff-based edits inside an IDE extension, Windsurf’s Cascade agent proposes multi-step changes, and Zed renders edits through native multibuffers. A tool-agnostic validation gate, like the one built in this tutorial, works regardless of which of these produced the edit.

Do I need a specific Cursor plan to use Fast Apply and the shadow workspace?
Fast Apply and shadow workspace diagnostics are part of Cursor’s core editing experience and are not gated behind a specific paid tier on their own, though access to specific first-party and third-party models, including Composer 2.5, varies by plan under the October 2026 X Subscription structure, with Premium ($30/month) and above including the full first-party model pool.

Sofia LindstrÃķm

Sofia LindstrÃķm

Editor-in-Chief

Sofia LindstrÃķm is the Editor-in-Chief at Tech Insider, where she leads editorial strategy and oversees coverage across AI, cybersecurity, and enterprise technology. With over a decade in Swedish tech journalism, she previously served as technology editor at Dagens Industri and covered the Nordic startup ecosystem for Breakit. Sofia holds an MSc in Media Technology from KTH Royal Institute of Technology and is a frequent speaker at Web Summit and Slush. She is passionate about making complex technology accessible to business leaders.

View all articles