← Back to Guides · AI WORKFLOWS
My Complete Cursor Workflow for Shipping a SaaS Feature
Chat-shaped Cursor work produces chat-shaped products. You type "add team invites," the agent writes a page, a route, and half a schema, then you notice there is no email, no tenant check, and no test. The feature is "almost done" for three days. That is not a model problem. It is a missing pipeline.
This is the workflow I use to ship a SaaS feature in Cursor: spec, contract, schema, API, UI, tests, review, security, then a real ship gate. You can run it by hand. AgenticKit's /ship command is the same sequence written down so I do not skip a phase when I am tired. tech-lead routes. api-engineer writes the contract. test-automator owns the gate. The playbooks are saas-patterns and api-patterns.
The takeaway
One feature. One definition of done. Lint, test, and build have to pass. A security pass has to say PASS. If the agent drifts, revert to the plan. Do not patch a confused chat.
The sequence:
- Fill
CURSOR.mdso the stack and skip flags are not a guess. - Spec the slice (
Shift+TabPlan Mode, or/feature-spec). - Build in order: contract, schema, API, UI.
- Prove it: unit tests, a smoke E2E if the path is user-facing, review, security.
- Run the scripts in
CURSOR.md. Ship or hold.
Cursor's own agent best-practices say the highest-leverage change is planning before coding, and starting a new conversation when you move to a new unit of work. The rest of this article is that advice applied to a multi-tenant SaaS feature.
Before you type
If the agent does not know your package manager, your ORM, or whether this repo even has a database, it will invent one. That is how you get Prisma in a Drizzle app.
Fill CURSOR.md first. Minimum fields:
- Stack table: framework, language, database, ORM, auth, payments, test runner, deploy, package manager
- Exact scripts: lint, test, build, optional E2E
- Ship pipeline skips: Skip DB, Skip UI, Skip E2E, Skip AI, Skip Mobile
- Auth model and the tenant field name, if you have tenants
- What not to do
The rules article covers the always-on rule that forces the agent to read this file. Without that rule, CURSOR.md is a note to yourself.
Skip flags matter more than people think. If you are adding a webhook and there is no UI, check Skip UI. If you are changing copy on a settings page, check Skip DB. A full /ship on a CSS tweak is how you get a "helpful" migration.
The pipeline
| Phase | Owner | Exit criteria |
|---|---|---|
| 1 Spec | you + tech-lead | Complexity S/M/L, task table, what is out of scope |
| 2a Contract | backend-architect | Routes, request/response types, error codes |
| 2b Database | postgres-pro | Schema + up/down migration, or Skip DB |
| 2c API | api-engineer | Auth, Zod, tenant from session, { error, code } |
| 2d UI | react-specialist | loading / error / empty, or Skip UI |
| 3a Tests | test-automator | Happy path, validation, auth, tenant isolation |
| 3b E2E | test-automator | One smoke path, or Skip E2E |
| 3c Review | code-reviewer | APPROVE, or a list of blocking issues |
| 3d Security | security-auditor | PASS / FAIL. FAIL stops the ship |
| 4 Gate | devops-engineer | Lint, test, build from CURSOR.md scripts |
This is the table inside /ship. You do not need the command to follow it. You do need to stop after a failed gate instead of prompting "just finish it."
Complexity L does not go through this pipeline in one chat. Split it with /feature-spec first, then ship each slice.
Worked example: invite a teammate by email
Imagine a Next.js + Postgres + Stripe app. Users already sign in. You want an owner to invite a teammate by email into the current organization.
Phase 1: Spec
Open Plan Mode (Shift+Tab) or run:
/feature-spec Invite a teammate by email into the current organization.
Owner only. Email must already be a user, or create a pending invite.
A useful spec is short:
- In: Owner on
/settings/teamsubmits an email. - Out: Row in
organization_invites, email sent, invitee can accept and land in the org. - Not in this slice: Roles beyond owner/member. Resend. SSO. CSV import.
- Complexity: M. Split if you do not already have email sending.
- Assumptions: Session has
organizationId. Resend (or your provider) is already wired for transactional mail. If it is not, that is a prior slice.
If the agent asks more than two questions, answer them. If it asks a third, tell it to state assumptions and proceed. Infinite clarifying questions are a stall.
Save the plan. Cursor can store plans in .cursor/plans/. That file is what you revert to, not the chat transcript.
Phase 2a: Contract
Write the API before the table.
POST /api/invites
body: { email: string }
201: { id, email, status: "pending" }
400 VALIDATION_ERROR
401 UNAUTHORIZED
403 FORBIDDEN // not an owner
409 CONFLICT // already a member or invite exists
POST /api/invites/:id/accept
200: { organizationId }
401 / 404
Error codes are part of the contract. If you skip them here, the API rule will nag later, or the tests will.
Phase 2b: Schema
One table. Tenant-leading index. Down migration.
CREATE TABLE organization_invites (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
email text NOT NULL,
invited_by uuid NOT NULL REFERENCES users(id),
status text NOT NULL DEFAULT 'pending',
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_invites_org_email
ON organization_invites (organization_id, email);
If CURSOR.md has Skip DB checked, stop. You are in the wrong pipeline.
Phase 2c: API
Handler anatomy from the API rule: auth, then Zod, then a service call, then a mapped response. The route file stays thin. The service reads session.organizationId, not a client-supplied org id.
Webhook-shaped work (the outbound email) should be idempotent. If the insert succeeds and the mailer fails, you still have a row you can resend later. Do not wrap "insert + email" in a way that leaves no row and a confused user.
Phase 2d: UI
A settings section, not a new marketing page.
- Server Component page that loads current members and pending invites
- Small client form for the email
loading.tsx,error.tsx, empty state ("No teammates yet. Invite someone.")
If the API exists and you only need the page, /build-page is the right command. /ship is for the full slice.
Phase 3: Tests, review, security
Four API cases, minimum:
- Owner invite returns 201
- Bad email returns 400
VALIDATION_ERROR - No session returns 401
- Member of org A cannot invite into org B (404 or 403, pick one and keep it)
Then a Playwright smoke: sign in as owner, submit an email, see "Invite sent." Skip E2E if CURSOR.md says so, but do not skip the tenant test.
Review is a different role from the writer. If you are the human, read the diff as if you did not prompt it. If you use a reviewer agent, do not let the same chat that wrote the code approve it. That failure is why the first workflow broke.
Security on this slice is IDOR and invite enumeration. FAIL if an invite id from another org is readable. FAIL if the accept path does not check the signed-in email.
Phase 4: Gate
Run the exact scripts from CURSOR.md. Not a guessed npm test in a pnpm repo.
pnpm lint
pnpm test
pnpm build
Red means HOLD. You do not ship a red build because the UI looks right in the preview.
Chat hygiene
Cursor's guidance is the one I follow:
Start a new conversation when you move to a different feature, the agent repeats the same mistake, or you finished a logical unit (spec done, or API done, or UI done).
Continue when you are iterating on the same slice and the agent needs the last decision.
Long chats accumulate noise. After enough turns the agent "helpfully" refactors a file you did not mention. That is the signal to stop, commit what is good, and open a fresh thread with @ the spec file.
Do not paste the entire previous transcript. Point at the plan file and the files that changed.
When it goes sideways
The agent will eventually write the wrong thing. The expensive move is to keep prompting "no, the other way" inside the same thread.
What I do instead:
- Stop generation the moment the diff leaves the slice (new ORM, new folder layout, drive-by rename).
git checkoutor discard the bad files.- Tighten the plan: one more "not this" bullet.
- New chat. Attach the plan. Run the same phase again.
This is slower than one more prompt, and faster than a day of undoing a confused agent. Official Cursor advice is the same: go back to the plan rather than fixing an in-progress mess.
When not to use the full pipeline
- A typo, a color token, a one-line copy change
- A bug with a stack trace: use
/fix, not/ship - API-only work:
/build-api - Page-only work when the API exists:
/build-page - A large rewrite: spec it, then ship slices. One
/ship Add billingis how you get a half Stripe integration and a new settings IA you did not ask for
If you want to see which agents a feature would call, type it into the Agent Chain tool. It is the same order as the table above.
The next article in this series is the post-mortem: what went wrong with the first version of this workflow. The rules that make the pipeline hold are in Cursor rules for SaaS founders.
Pick one small feature this week and run it through spec, build, and gate. Not a rewrite. A teammate invite, a password reset, a "resend receipt" button. The pipeline gets cheaper the second time you use it.