Lovable or Cursor keeps breaking features that worked. How do I stop it?

Checked on

AI builders change more than you ask for, so a prompt about one page can break another. You can't stop that completely, but you can make every break easy to spot and easy to undo. Save a restore point before each change. Plan bigger changes before any code is written. Name the files the AI may touch. Put your “never change this” rules in Lovable Knowledge or a Cursor rules file. Read the list of changed files before you publish. Then open your live sign-up, log-in and payment pages after every publish.

Symptoms

  • You ask for a small change (a new button, a colour, a text fix) and something unrelated stops working: sign-up, log-in, a form, the Pay button, a page that opened yesterday.
  • You fix that, and a third thing breaks.
  • The preview looks fine, but the live site is broken, or the other way round.
  • The AI says “Done!” and doesn't mention the files it rewrote.

Why it happens

  • The AI edits more than one file. To add a button, it may also rewrite the layout, the router, or the file that sets up your database client. Each of those can break a page you never mentioned.
  • It doesn't remember your rules. Unless you write them down somewhere it reads every time, the AI can rename a route, change an environment variable name, or “tidy up” a working form.
  • Long chats drift. Lovable's own docs say that in very long conversations with a lot of context, instructions may not always be followed consistently.
  • Nobody re-checks the old features. You test the thing you just asked for. Nobody opens sign-up, log-in and payment again, so a customer finds the break.

Step-by-step fix

1. Save a restore point before every change

Lovable: every change is saved as a version automatically. Before a risky change, open History (the toggle in the top bar) and bookmark the current version, so you can find the last good one quickly. If a change breaks something, click the earlier version, then Revert. Reverting restores your code and redeploys edge functions, but it does not roll back database data or migrations.

For bigger changes, use a draft: a separate copy of your project with its own chat and preview. Nothing in a draft reaches your project until you accept it, and nothing reaches your live app until you publish. One catch: a draft uses your project's real data.

Cursor: Agent creates checkpoints automatically. To undo a bad turn, hover over the earlier message and click Restore Checkpoint. Checkpoints are stored on your computer and separate from git, so they are for quick undos only. Commit before each task so you always have a permanent undo:

git add -A && git commit -m "before: add pricing toggle"
# if the next change breaks something:
git diff HEAD --stat        # which files did the AI change?
git restore .               # throw away uncommitted AI changes

2. Plan before it builds

For anything bigger than a text change, ask for a plan first.

  • Lovable: switch the chat to Plan mode (the mode picker next to the chat input). Plan mode never changes your code. It writes a plan you edit and approve before anything is built. Use Chat mode to ask how a part of your app works before you change it.
  • Cursor: press Shift+Tab in the chat input to switch to Plan Mode. Agent reads your code, asks questions and writes a plan you can edit before it builds.

Read the plan for one thing: which files and features it touches. If the plan mentions your log-in code, router, database client or payment code and you didn't ask for that, say no before it builds.

3. Keep prompts small and name the files

One change per prompt. Say which files may change and which must not:

  • Lovable: type @ in the chat to point at a specific file.
  • Cursor: type @ and pick the file or folder, for example @src/components/PricingCard.tsx.

A prompt like “add a yearly toggle to the pricing card, only edit PricingCard.tsx” breaks far less than “improve the pricing page”.

4. Write your “never change” rules down once

The AI reads these on every request, so you don't have to repeat them.

Lovable: open Project settings → Knowledge (up to 10,000 characters). For example:

Do not change these areas unless I name the file in my message:
- Log-in: the Supabase client, the auth context, route guards, /signup and /login pages.
- Payments: the checkout button, the checkout edge function and its environment variable names.
- Routes: never rename or remove an existing route.
- Environment variables: never rename one.
Before you finish any change, list every file you changed.

Cursor: create a project rule in .cursor/rules/ (it must be an .mdc file). alwaysApply: true includes it in every chat:

---
description: Keep working features as they are
alwaysApply: true
---
- Only edit the files I name in my message. Ask before touching any other file.
- Never rename or remove routes, environment variable names, or exported functions.
- Do not change log-in code (supabase client, auth provider, route guards, login and signup pages) or checkout code unless I ask for it by name.
- After every change, list the files you changed and run the tests.

You can also type /create-rule in Cursor's chat to have Agent write the rule file for you. If you prefer plain Markdown, Cursor also reads an AGENTS.md file in the project root.

5. Read the list of changes before you publish

You don't need to read every line. Look at the list of changed files and ask: “Why did this one change?”

  • Lovable: in History, open the version's menu and choose View code changes to see which files and lines changed.
  • Cursor: the diff view shows changes as the agent works, and you can stop it if it heads the wrong way. You can also type /agent-review to have Agent Review check your local changes, or use @Branch in a prompt to ask the agent to review everything on the branch.
  • Lovable with GitHub: connect GitHub (Project settings → Git) and changes sync both ways, so you can review commits there too.

If a file on your “never change” list changed and you didn't ask for it, revert first and prompt again with tighter limits.

6. Re-check the old features after every publish

In Lovable, changes reach your live app only when you click Publish (then Publish changes). That is the moment to re-check. Open your live address in a private window and check four things:

  1. The home page opens.
  2. The sign-up page shows its email field.
  3. The log-in page shows its form.
  4. The payment or pricing page shows its Pay button. Stop there. You don't need to pay.

If you'd rather not click through it each time, use the script below.

A 4-check test you can run after each publish

This Playwright test opens the four pages and checks that the fields and button are there, and that the page didn't throw a JavaScript error. It never types, submits, signs in or pays, so you can run it against your live site. We tested it with @playwright/test 1.64.

// smoke.spec.ts: run after every publish. It only opens pages and looks for
// fields and buttons. It never types, submits, signs in or pays.
// Setup:  npm i -D @playwright/test && npx playwright install chromium
// Run:    APP_URL=https://your-app.com npx playwright test smoke.spec.ts
import { test, expect, type Page } from '@playwright/test'

const APP_URL = (process.env.APP_URL ?? 'http://localhost:5173').replace(/\/$/, '')
// Change these to your app's real paths.
const PAGES = {
  signup: process.env.SIGNUP_PATH ?? '/signup',
  login: process.env.LOGIN_PATH ?? '/login',
  checkout: process.env.CHECKOUT_PATH ?? '/pricing',
}

// Fail the test if the page throws a JavaScript error while loading.
function watchErrors(page: Page) {
  const errors: string[] = []
  page.on('pageerror', (err) => errors.push(err.message))
  return errors
}

async function open(page: Page, path: string) {
  const res = await page.goto(APP_URL + path, { waitUntil: 'load' })
  expect(res?.status(), `${path} returned an error status`).toBeLessThan(400)
}

test('landing page loads', async ({ page }) => {
  const errors = watchErrors(page)
  await open(page, '/')
  await expect(page.locator('h1').first()).toBeVisible()
  expect(errors, 'JavaScript errors on the landing page').toEqual([])
})

test('signup page shows its email field', async ({ page }) => {
  const errors = watchErrors(page)
  await open(page, PAGES.signup)
  await expect(page.locator('input[type="email"]').first()).toBeVisible()
  expect(errors, 'JavaScript errors on the signup page').toEqual([])
})

test('login page shows email and password fields', async ({ page }) => {
  const errors = watchErrors(page)
  await open(page, PAGES.login)
  await expect(page.locator('input[type="email"]').first()).toBeVisible()
  await expect(page.locator('input[type="password"]').first()).toBeVisible()
  expect(errors, 'JavaScript errors on the login page').toEqual([])
})

test('checkout page shows a pay button', async ({ page }) => {
  const errors = watchErrors(page)
  await open(page, PAGES.checkout)
  const pay = page.getByRole('button', { name: /pay|buy|subscribe|checkout|upgrade/i })
    .or(page.getByRole('link', { name: /pay|buy|subscribe|checkout|upgrade/i }))
  await expect(pay.first()).toBeVisible()
  expect(errors, 'JavaScript errors on the checkout page').toEqual([])
})

“4 passed” means the four pages still look right. A failure names the page and what's missing, for example signup page shows its email field ... element(s) not found. If your log-in uses an email link with no password field, delete the password line. In Cursor you can ask Agent to “add this file and run it after every change”. Cursor's own guide recommends letting agents write and run tests so breaks are found before they ship.

Fix prompt for your AI builder

Paste this into Lovable or Cursor at the start of a change, or after something broke. Replace the parts in brackets first.

Fix prompt
I want this one change: [describe the change].
Rules for this change:
1. Before writing code, tell me which files you plan to edit and why. Wait for my OK.
2. Only edit those files. Do not touch log-in code (the Supabase client, auth provider, route guards, signup and login pages), checkout code, routes, or environment variable names unless I named them above.
3. Do not rename or remove any existing route, component, export or environment variable.
4. Do not change the database schema or RLS policies.
5. When you're done, list every file you changed, and tell me how to check that sign-up, log-in and payment still work on the live site.
If something already broke: [paste the error or describe what stopped working]. Find the last change that touched it, restore the previous working behaviour for that part only, and change nothing else.

How to confirm it's fixed

  1. Run the test above (or the 4 checks by hand) against your live address after you publish. All four should pass.
  2. Make one more small change with the rules above. The list of changed files should only contain the files you named.
  3. If log-in broke and won't come back, follow Lovable or Cursor broke my log-in after an update.

How to find out sooner next time

A break that nobody notices can stay on your live site for days. Heedza opens your live home, sign-up, log-in and payment pages on a schedule (once a day on Free, up to every 15 minutes on paid plans) and checks that each one opens and shows its form or Pay button. If one doesn't, it sends you a screenshot and a fix prompt for the tool you built with. It never types, signs in, submits forms or pays, so a form that shows but fails when submitted still needs your own test. See sign-up monitoring.

Let Heedza check this for you. Heedza’s alerts with a fix opens your home, sign-up, log-in and payment pages on a schedule. If one stops working, we send a picture of the page and a fix. It never logs in or fills in forms, so it can’t see errors that only show after that.

Made with Lovable, Cursor, Bolt, Replit or v0? Free for 1 site: start free or see prices.

More questions

Does reverting in Lovable undo database changes?

No. Reverting restores your code and redeploys edge functions, but data and migrations stay as they are. Undo those separately.

Are Cursor checkpoints enough?

They're good for undoing the last few agent turns. They're stored on your computer and separate from git, so commit before each task for a permanent undo.

Why does the AI ignore my rules sometimes?

Long chats make it more likely. Keep rules in Knowledge or a rules file rather than in one chat message, and start a new chat for a new task.

Where this comes from