My Complete Cursor Workflow for Shipping a SaaS Feature

By Rakshit Yadav (@yadavrakshit60)•Aug 2026•12 min read

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:

  1. Fill CURSOR.md so the stack and skip flags are not a guess.
  2. Spec the slice (Shift+Tab Plan Mode, or /feature-spec).
  3. Build in order: contract, schema, API, UI.
  4. Prove it: unit tests, a smoke E2E if the path is user-facing, review, security.
  5. 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

PhaseOwnerExit criteria
1 Specyou + tech-leadComplexity S/M/L, task table, what is out of scope
2a Contractbackend-architectRoutes, request/response types, error codes
2b Databasepostgres-proSchema + up/down migration, or Skip DB
2c APIapi-engineerAuth, Zod, tenant from session, { error, code }
2d UIreact-specialistloading / error / empty, or Skip UI
3a Teststest-automatorHappy path, validation, auth, tenant isolation
3b E2Etest-automatorOne smoke path, or Skip E2E
3c Reviewcode-reviewerAPPROVE, or a list of blocking issues
3d Securitysecurity-auditorPASS / FAIL. FAIL stops the ship
4 Gatedevops-engineerLint, 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/team submits 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:

  1. Owner invite returns 201
  2. Bad email returns 400 VALIDATION_ERROR
  3. No session returns 401
  4. 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:

  1. Stop generation the moment the diff leaves the slice (new ORM, new folder layout, drive-by rename).
  2. git checkout or discard the bad files.
  3. Tighten the plan: one more "not this" bullet.
  4. 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 billing is 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.

Related Tools & Agents

🛠️ Free Tool: agent-chain🤖 Agent: tech-lead🤖 Agent: api-engineer🤖 Agent: test-automator⚡ Command: /ship⚡ Command: /feature-spec⚡ Command: /build-apiSkill: saas-patternsSkill: api-patterns

See the /ship pipeline

The same phases, written as a slash command you can run in Cursor.

Open /ship