Build a SaaS starter app on Replit that I can fork for future products, with {{database}} for storage.
Screens: landing page with a pricing section, signup and login, an app shell with sidebar navigation, a settings area (profile, workspace, billing), and an admin-only members list.
Auth: use Replit Auth rather than hand-rolled sessions. Every role check happens on the server; hiding a button is not enforcement.
Data model: users, workspaces, memberships with a role of owner, admin, or member, invitations with single-use expiring tokens, and a subscriptions table holding plan and status even though payments are stubbed.
Behaviors:
- New signups get a personal workspace automatically.
- Owners invite by email; the invite link opens a join page and the token dies after acceptance or expiry.
- Billing is a stub that flips the plan field, ready for {{addon}} to be wired in later as its own milestone.
Edge cases: the last owner of a workspace cannot be demoted or removed, an accepted invitation cannot be reused, and deleting a workspace requires typing its name to confirm. The members list and the no-workspace state both need designed empty screens.
Verify: app users here are Replit identities, so you cannot create a second account to test with. Prove role enforcement server-side instead: write a dev-only test script that stubs the session with two fake user ids, seeds one as owner and one as member of the same workspace, calls the members-list and workspace-delete endpoints as the member, and asserts both are rejected with 403. Assert too that an invitation token fails on its second use. Paste the script output in chat, then set the app up for deployment; I will run the two-browser invite walkthrough myself once it is live.What Replit Agent is good at
Replit Agent builds and runs full applications inside Replit's browser workspace. You describe the product, and it scaffolds the codebase, provisions storage, runs the app against a live webview, and prepares it for deployment on Replit's hosting, no local environment required. It suits developers who want a hosted prototype by the end of a session, and teams building internal tools that need a real URL and a real database rather than a static demo.
The difference from its neighbors is ownership of the whole loop. v0 hands you UI code to take elsewhere, and Bolt scaffolds in the browser but leans on you for hosting decisions; Replit Agent builds, stores data, and deploys in one place, with checkpoints you can roll back to when a change goes wrong. That makes it strongest for full-stack apps with persistence and auth, and the wrong pick when you only need a component dropped into an existing repository.
How to prompt Replit Agent
Write the first message as a complete spec, not an opening line of a conversation. Replit Agent commits real effort to the first build, so the cheapest corrections are the ones you never need. Include five things every time: the screens or endpoints as an explicit list, the data model with field names, which Replit services to use (Replit Auth for login, the built-in database for storage, Secrets for anything sensitive), the empty and error states, and the checks it must pass before declaring the work done.
Scope each request to one milestone. "Build the API and seed data, nothing else yet" produces better results than a whole product description, because you can verify in the webview before the next layer goes on, and roll back to a checkpoint if a follow-up damages something that worked.
Be explicit about verification. The agent can run the server, execute commands in the shell, and hit its own endpoints, but it will often skip this unless the prompt demands it. End prompts with concrete proof: "curl each endpoint and paste the status codes" or "change a seed row and confirm the chart moves."
Never paste API keys into the chat. Add them in the Secrets pane and refer to them by environment variable name; the agent will wire them up correctly from the name alone.
All 20 Replit Agent prompts
App 5 prompts
Build a to-do app as a full-stack Repl: React frontend, Express backend, and the built-in Postgres database. Hold your questions until the first working version is in the webview. Data model: a tasks table with id, title, notes, due_date, completed_at, position, and user_id. Wire sign-in through Replit Auth so each user only sees their own tasks. Screens: a single main view with an always-focused quick-add input at the top, the task list below, and filter tabs for Today, Upcoming, and Done. Editing happens inline, no modal. Completed tasks drop to the bottom with strikethrough. Behaviors: Enter adds a task, clicking the checkbox toggles completion, drag to reorder persists position to the database. Overdue tasks show the due date in red text. Empty and error states: a first-run screen with one sample task and a hint pointing at the quick-add box. If a write fails, keep the optimistic update visible, show a retry toast, and reconcile on success. Styling: neutral background, one accent color, generous line height, list rows tall enough for touch. Done when: I can add, complete, reorder, and delete a task in the webview, refresh the page, and see identical state. Then create a checkpoint and suggest a deployment type.
Build a blog with its own admin CMS in one Repl. Two areas, one codebase. Public site: a home page listing published posts newest first with title, excerpt, and date. A post page at /posts/:slug rendering markdown with code highlighting. An RSS feed at /feed.xml. A 404 page for unknown slugs that links back home. Admin: everything under /admin sits behind Replit Auth, and only the account I sign in with first gets editor rights, tracked in an editors table. The post editor has title, a slug auto-generated from the title but editable, a markdown body with a live preview pane, a cover image uploaded to Object Storage, and a draft or published toggle. Drafts never appear on the public site or in the feed. Data: a posts table with id, title, unique slug, body_md, cover_url, status, published_at, and updated_at in the built-in Postgres database. Seed two sample posts so the public pages render immediately. Behaviors: saving a draft never changes published_at, publishing sets it once, and changing the slug of a published post writes a redirect row from the old slug. Empty and error states: the admin list with zero posts shows a create button front and center. A failed cover upload keeps the editor content intact and reports the reason inline. Done when: I write a draft, preview it, publish it, see it on the home page and in the feed, and confirm a signed-out visitor cannot reach /admin.
Build a small e-commerce store in this Repl: React storefront, Express API, the built-in Postgres database, product images in Object Storage, and Stripe in test mode with keys from Secrets. Confirm your plan, then build the storefront before the admin. Data: products with price_cents, currency, stock, and an images relation. Carts keyed to a session cookie for guests. Orders with line items that snapshot the price paid, never a join back to the live product price. Storefront: a product grid with image, name, and price. A product page with a gallery and an add-to-cart button that reflects stock, sold out disables it. A cart drawer with quantity steppers and a subtotal. Checkout hands off to Stripe Checkout, with success and cancel routes returning to the store. Payments: create the Stripe session server-side from cart contents, verify the webhook signature, and only mark an order paid from the webhook, never from the success redirect. Decrement stock inside the same transaction that marks the order paid. Admin: behind Replit Auth, a product list with create and edit forms, image upload straight to Object Storage, and an orders view showing payment status. Empty and error states: an empty cart drawer links back to the grid. A failed webhook signature returns 400 and logs. Stock gone at checkout time removes the line, names the item, and recalculates the total. Done when: I finish a test-card purchase in the webview, the order flips to paid via the webhook, stock drops by the purchased quantity, and the receipt shows the snapshotted prices.
Build a booking app in this Repl for a single provider taking appointments. React frontend, Express API, the built-in Postgres database. Plan first, list your assumptions about working hours and slot length, and let me correct them before building. Data: availability_rules for weekly working hours, bookings with starts_at and ends_at stored in UTC, and an overlap guard enforced in Postgres, via an exclusion constraint or an equivalent unique scheme, so two confirmed bookings can never collide. The database enforces this, not the request handler. Booking flow: a public page showing the next two weeks as day columns, open slots computed from the rules minus existing bookings, rendered in the visitor's browser timezone with that timezone named on screen. Picking a slot asks for name and email, then confirms with a cancellation link containing a random token. No account required. Cancellation: the tokenized link shows the booking with a cancel button, and cancelling frees the slot immediately. Expired or bogus tokens get one neutral message, no detail leaked. Admin: behind Replit Auth, a week view of confirmed bookings, blocking out time, and edits to weekly rules that affect only future slots. Race handling: two people grabbing the same slot must resolve as one success and one clear already-taken message that refreshes the grid. Prove it by firing two concurrent requests from the workspace shell and pasting both responses. Empty state: a week with no availability says when the next opening is, not just an empty grid. Done when: I book a slot in the webview, watch it vanish for a second visitor, cancel through the token link, and see it reappear.
Backend 5 prompts
Build a REST API in {{language}} on Replit, backed by {{database}}, for managing {{resource}}.
Endpoints:
- GET /api/{{resource}}: paginated list. Query params limit (default 20, max 100) and offset. Response includes items and total.
- GET /api/{{resource}}/:id
- POST /api/{{resource}}: validate the body, reject unknown fields with a 400 listing them.
- PATCH /api/{{resource}}/:id: partial update. An empty body is a 400.
- DELETE /api/{{resource}}/:id: returns 204, then 404 on repeat.
- GET /healthz: 200 plus a live database connectivity check.
Data model: id, created_at, updated_at, and the fields a {{resource}} record realistically needs. Propose the schema in chat before writing code so I can correct field names cheaply.
Error contract: every failure returns JSON shaped { "error": { "code", "message", "fields" } }. Validation failures are 400 with per-field messages. A malformed id is 400, not 500. Nothing ever leaks a stack trace to the client. Unsupported methods on known routes return 405.
Setup: read the database URL from an environment variable via Replit Secrets, never hardcode it. Add request logging middleware. Write a seed script that inserts 25 varied rows and is safe to re-run.
Verify before you stop: start the server, curl every endpoint from the shell including the failure cases above, and paste each status code with one sample body into chat. Then configure the Repl for deployment and tell me which deployment type you chose and why.Spin up a GraphQL API in this Repl with Apollo Server mounted on Express at /graphql, backed by the built-in Postgres database. Plan the schema, show it to me, then build.
Schema: a {{resource}} type with id, name, status, and created_at, plus a related comments type to force one join. Queries: single fetch by id, and a paginated list using cursor-based pagination with first and after arguments returning edges and pageInfo. Mutations: create, update, delete, each returning the affected node and a userErrors array instead of throwing on validation problems.
Resolvers: batch the comments lookup with DataLoader so listing many {{resource}} records issues two SQL queries, not one per row. Log SQL in dev so I can confirm the count from the workspace console.
Errors: malformed input returns userErrors with field and message, unknown ids return null with a NOT_FOUND extension code, unexpected failures return a generic message and log the stack server-side.
Tooling: leave the GraphQL playground enabled in dev so I can run queries straight from the webview, and add a seed script inserting twenty rows so pagination is testable immediately.
Done when: I can paste a list query with first: 10 into the playground, page through with the returned cursor, run one mutation with a deliberate validation error, and see userErrors instead of a 500. Snapshot a checkpoint before any schema change after that point.Add JWT authentication to the Express API in this Repl. Do not swap in Replit Auth, I need token auth for external API clients. Endpoints: POST /api/auth/register with email and password. POST /api/auth/login returning a short-lived access token in the JSON body and a refresh token in an httpOnly secure cookie. POST /api/auth/refresh that rotates the refresh token. POST /api/auth/logout that revokes it. Storage: a users table holding a bcrypt hash, never the raw password. A refresh_tokens table with token hash, user_id, expires_at, and revoked_at so rotation and logout are enforceable server-side. Use the built-in Postgres database. Secrets: read the signing key from Replit Secrets, generating and storing one if missing. Fail loudly at startup if it is absent in production. Middleware: requireAuth verifies the access token, attaches the user id to the request, and returns 401 with a machine-readable code of token_expired or token_invalid so clients know whether to refresh or log in again. Protect the routes I list, leave health checks open. Edge cases: duplicate registration returns 409, a reused rotated refresh token revokes that token's whole family, and clock skew tolerance stays small. Verify from the workspace shell: a curl sequence covering register, login, an authorized call, an expired-token 401, a refresh, and a post-logout refresh that fails. Paste the commands and outputs, then list every changed file.
Set up the Postgres schema for {{resource}} in this Repl using the database integration, with migrations checked into the project rather than hand-run SQL.
Tables: the core {{resource}} table plus its natural satellites, an owners reference, a status history table, and tags with a many-to-many join table. Every table gets created_at and updated_at with defaults, and updated_at maintained by one mechanism used everywhere, pick it and state it.
Constraints do the enforcement, not application code: NOT NULL on required columns, UNIQUE where a duplicate would mean corruption, foreign keys with an explicit ON DELETE choice per relation stated in a comment, and CHECK constraints on enum-like status columns.
Indexes: cover the queries the app will actually run, the list view sort, the owner filter, and the tag lookup. Name them consistently. No speculative index on every column.
Migrations: incremental files with both up and down, runnable from the workspace shell with a single documented command. Never edit an applied migration, always add a new one.
Seed: an idempotent script inserting a small realistic dataset. Running it twice must not duplicate rows.
Verify from the shell and paste the output: the migration command against a clean database, the seed run twice, a query plan for the list view showing its index being used, and one deliberate constraint violation per table proving the database rejects bad data. Finish by listing every file you created and what each contains.Build a file upload endpoint in this Repl backed by Object Storage, with the built-in Postgres database holding metadata. No files written to the repl filesystem, uploads pass straight through to the bucket. Endpoint: POST /api/files accepting multipart form data. Enforce a size cap and an allowlist of content types, images and PDF to start, detected from the file's magic bytes, not the client-supplied mime header or the extension. Reject early rather than buffering the whole body, an oversized upload should fail fast with 413. Metadata: a files table with id, storage key, original name, byte size, detected content type, uploader when the app has auth, and created_at. The storage key is a generated id plus extension, never the original filename, which is kept only as display metadata. Download: GET /api/files/:id streams from Object Storage with the correct content type and a content-disposition header built from the sanitized original name. An unknown id returns a JSON 404 matching the API's existing error shape. Failure handling: if the storage write succeeds but the database insert fails, delete the orphaned object before returning 500, and leave a code comment stating that cleanup so a refactor does not drop it. Verify from the workspace shell with curl and paste the outputs: a valid image upload returning its id, a renamed executable posing as a .png rejected with 415, an oversize file rejected with 413, a download round trip proven byte-identical with a checksum comparison, and the 404. Then list the changed files and where the size cap constant lives so I can tune it.
Features 4 prompts
Retrofit search into the existing codebase: users need to find {{resource}} records stored in {{database}} by name or description.
Backend: GET /api/search?q= returning up to 20 matches with the matched field named in each result. Matching is case-insensitive: prefix match on the name field plus substring match on description. Escape % and _ in user input so wildcards cannot be injected. If {{database}} is Postgres, use ILIKE backed by a trigram index (or tsvector if you argue it fits better), never load the whole table into application memory. Queries under two characters return an empty result without touching the database.
Frontend: a search input in the header, debounced at 300ms. A dropdown shows the top matches with arrow-key navigation, enter to open, escape to close. Submitting jumps to a full results page at /search?q= with the same matching rules.
Race handling: tag each request with an incrementing id and discard responses that arrive out of order, so fast typing never renders stale results. Abort the in-flight request when a newer one starts.
States: no matches shows the query echoed back with a suggestion to broaden it. A failed search request shows a retry link in the dropdown, not a silent empty list.
Verify: seed rows where a term appears only in the name, only in the description, and not at all. Run all three searches plus one containing %, paste the JSON responses, and confirm keyboard navigation works in the webview. If you added an index, paste the query plan for one search to show it is being used.Add an LLM chatbot to the app already in this Repl without touching unrelated screens. Before writing code, tell me which provider connector you plan to use. If none is configured, ask me for an API key and store it in Secrets, never in source. Backend: a POST /api/chat route that streams tokens to the client as they arrive, plus conversations and messages tables in the existing Postgres database so a refresh restores history. Keep the system prompt in one server-side file I can edit, and never send it to the browser. UI: a chat panel with message bubbles, a streaming indicator while tokens arrive, a stop button that aborts the in-flight request, and an input that disables during generation. Render model output as markdown with code blocks. Guardrails: cap each request's history at the last twenty messages, truncating oldest first. Return a clear inline error when the key is missing or the provider rejects the call, with a retry button. Do not auto-retry on the server. Empty state: a short explainer of what the bot can do, with three clickable example questions. Done when: I can ask a question in the webview, watch the reply stream in, hit stop mid-response, refresh and see the full conversation restored from Postgres, and see a readable error card after I temporarily remove the key from Secrets.
Add transactional email notifications to the app in this Repl. Tell me which email provider integration you intend to use before building. Whichever it is, the API key lives in Secrets and the sender address is one config value, not a literal scattered through the code. Events to cover: a welcome message on signup, a confirmation when credentials change, and a weekly digest, each with its own template. Architecture: do not send inline from request handlers. Write an outbox table in the built-in Postgres database with event type, recipient, payload, status, attempt count, and last_error. A worker loop drains it, retries failures with backoff up to three attempts, then marks the row dead so nothing loops forever. Templates: plain HTML with a text fallback, shared header and footer partials, and an unsubscribe link on anything non-essential that flips a flag on the user row. The digest respects that flag, credential notices ignore it deliberately. Dev behavior: when no provider key is present, log the rendered email to the console and mark the outbox row sent_dev so I can build without an account. Never attempt a real send in that mode. Failure UX: sending problems never surface as signup errors, the account gets created regardless and the outbox owns delivery. Done when: I trigger a signup in the webview, watch the outbox row move from pending to sent in the database pane, see the rendered text fallback in the console in dev mode, and can walk the unsubscribe flow end to end.
Add CSV import and export for {{resource}} records to the app in this Repl. Both directions, processed server-side, no client-side parsing of large files.
Import flow: an upload dropzone that accepts .csv only, sends the file to POST /api/import, and lands on a mapping step. The mapping step shows the first five parsed rows and lets me match CSV headers to fields, with automatic matches preselected when names align. Confirming starts the real import.
Validation: process row by row, collect failures instead of aborting, and enforce the same rules as the normal create form. On completion show imported, skipped, and failed counts, plus a downloadable failures.csv containing each original row with an added error column. Dedupe on a natural key I confirm during mapping, duplicates count as skipped.
Big files: stream the parse, never hold the whole file in memory, and run anything beyond a few hundred rows as a background job the page polls for progress. Park the uploaded file in Object Storage until the job completes.
Export: a button on the list view that exports the currently applied filters, not the whole table, streaming the response with a date-stamped filename. Escape cells that spreadsheets would treat as formulas so nothing executes on open.
Empty and error states: an empty export still returns a valid header-only file. A malformed CSV fails at the mapping step and reports the line number of the first unparseable row.
Done when: I import a file containing two bad rows, get failures.csv back holding exactly those rows, and a filtered export reopens cleanly in a spreadsheet.Frontend 4 prompts
Build a dashboard UI on Replit for tracking {{resource}} activity, with data stored in {{database}}.
Layout: fixed sidebar with nav links (Overview, {{resource}}, Settings), top bar with a date range picker and a status dropdown, main area on a 12-column grid.
Overview content:
- Four stat cards: total {{resource}}, created this week, active count, and percent change versus the previous period. Every number must be computed by a query at request time, never hardcoded.
- A line chart of {{resource}} created per day across the selected range.
- A table of the most recent 50 rows: sortable columns, a text filter box, pagination at 10 rows per page.
The date range picker and status dropdown filter cards, chart, and table together from one place, not per widget.
Data: create the schema in {{database}} plus a seed script covering 60 days with uneven daily volume so the chart has visible shape.
States: filters matching zero rows show a message and a clear-filters button while cards render zeros. Fetch failures show an inline retry in the affected panel, never a blank page. Use loading skeletons, not spinners.
Styling: light theme, single accent color, system font stack, generous whitespace. Below tablet width the sidebar collapses to icons and cards stack in one column.
Verify: run the app, open the webview at desktop and phone widths, change a seed row and confirm the affected card changes, test column sorting, and list every component file you created.Build a one-page marketing site for {{resource}} in this Repl. Frontend plus one small API route for the waitlist. That route needs a running server, so plan to ship this as an Autoscale deployment; a Static deployment only works if you cut the form and point the CTA at a mailto link instead.
Sections in order: hero with a single headline, one subline, and one primary CTA button. A three-item problem and solution row. A product screenshot block using a placeholder image with explicit width and height so nothing shifts while it loads. A how-it-works list with three numbered steps. A social proof placeholder. A closing CTA repeating the hero action. Footer with contact and privacy links.
Waitlist: the CTA scrolls to an email form that POSTs to /api/waitlist and writes to a waitlist table in the built-in Postgres database. Validate the email server-side, dedupe on the address, and respond to repeats with a friendly already-subscribed message instead of an error.
States: success swaps the form for a confirmation line. Network failure keeps the typed email in the field and shows a retry message under the input. No alert dialogs anywhere.
Styling: one display font for headlines, system font for body, a single accent color used only on CTAs, constrained content width, and real spacing between sections rather than divider lines.
Done when: the page reads correctly at phone width in the webview, submitting the same email twice shows the dedupe message, and a hard refresh produces no visible layout shift in the hero.Build a pricing page for {{resource}} in this Repl as a route on the existing site, not a separate app.
Layout: three tiers side by side on desktop, stacked on mobile with the recommended tier first. Each card shows the plan name, price, a one-line summary, five feature bullets, and a CTA. The middle tier gets a subtle highlighted border and a small recommended tag, nothing animated.
Billing toggle: a monthly or annual switch above the cards. Annual shows the discounted monthly-equivalent price with billed-yearly fine print. The toggle updates prices in place with no route change, and the selected mode is written to the URL query so a shared link opens in the same state.
Comparison table: below the cards, a full feature matrix with a sticky first column on horizontal scroll for narrow screens, rows grouped by feature area with subheadings.
CTA wiring: the free tier goes to signup, paid tiers go to /checkout with the plan and billing period as query params, and the checkout route can be a stub page for now that echoes the chosen plan back. Keep all prices in one server-provided JSON object so edits happen in one place.
Edge cases: with JavaScript disabled the page still shows monthly prices and working CTA links. An unknown plan param on the stub shows a plain message and a link back.
Done when: toggling billing updates all three cards and the table together, a shared URL restores the toggle state, and the layout holds at phone width in the webview with no horizontal scroll.Build a sortable data table for the existing app in this Repl. Sorting happens on the server, the client never re-sorts a page of rows locally. API: extend the list endpoint with sort and dir query params, whitelist sortable columns server-side, and reject unknown values with 400 rather than silently ignoring them. Keep pagination as it is and reset to page one whenever the sort changes. Header behavior: clicking a sortable column cycles ascending, descending, off, with exactly one active sort at a time. The active column shows a direction arrow and carries aria-sort so the state is announced. Non-sortable columns get plain text headers, not dead buttons. URL state: sort, direction, and page live in the query string. A pasted link reproduces the exact view and browser back steps through sort changes. Loading: keep current rows visible under a subtle overlay while the next sort loads, no full-table skeleton flash after first paint. Sorts fire immediately on click. Table mechanics while you are in there: a sticky header on scroll, right-aligned numeric columns, fixed table layout so column widths do not jump between pages, and dates sorted by value while displayed formatted. Empty and error states: zero results keeps the header row and shows a message in the body. A failed fetch keeps the previous rows and offers an inline retry. Done when: I sort by each column in the webview, watch the network panel issue one request per click, refresh and land on the identical view, and get a 400 from a hand-edited bogus sort param.
Refactor 1 prompt
Run an accessibility pass on the app already in this workspace. Do not redesign anything, keep the visual language, fix the mechanics. Audit first: crawl every route, then give me a numbered list of concrete problems grouped by page before changing any code. Wait for my go-ahead. Then fix, in this order: keyboard access for every interactive element, including any div or span carrying an onClick, which should become a button or link. Visible focus rings that survive the existing CSS reset. A skip-to-content link as the first focusable element. Form inputs paired with real label elements, not placeholder text as the only hint. Images get alt text, decorative ones get empty alt. Color contrast on body text and button labels, adjusted by shade, not by replacing the palette. Heading levels made sequential per page. Any custom dropdown, modal, or tab widget gets matching keyboard behavior: Escape closes modals, focus is trapped while open and returns to the trigger on close. Announce dynamic changes: toasts and inline validation messages go into a polite live region so they are read aloud. Verify: tab through the main flow start to finish in the webview and list every focus stop in order so I can review the sequence. List each file you changed with a one-line reason. Create a checkpoint before the first edit so I can roll the whole pass back if anything regresses visually.
Testing 1 prompt
Audit the app in this Repl and retrofit error handling at every layer. Do not change the shape of successful responses.
Server: wrap route handlers so thrown errors become a JSON envelope { "error": { "code", "message", "requestId" } }, with the stack logged server-side and never sent to the client. Distinguish operational failures (validation, not found, upstream timeout) from bugs, and map each to a correct status code. Outbound HTTP calls get a timeout and one retry with backoff. If the database is unreachable at startup, retry the connection with a delay instead of crashing.
Client: add a fetch wrapper that converts non-2xx responses into typed errors. Every screen that loads data needs a visible failure state with a retry action, no blank sections, no infinite spinners. Form submissions display the server's per-field validation messages next to the inputs.
Process: install handlers for unhandledRejection and uncaughtException that log and exit cleanly so the Replit runtime restarts the app.
Prove it, do not just claim it: temporarily set the database URL to a wrong value and show what users see, then restore it. Submit a form with invalid data and screenshot the field messages. Point one outbound call at an unreachable host and show the timeout path. When finished, paste the final error envelope shape, give a route-by-route rundown of the failure handling each endpoint gained, and justify any dependency you added in one line. Prefer zero new dependencies.Prompting patterns that work in Replit Agent
Milestone-first spec
Describe the whole product briefly, then ask the agent to build only milestone one, such as schema plus API plus seed data. Verify it in the webview, then request the next layer. Each milestone lands on a checkpoint you can return to.
Name the platform services
Say Replit Auth, the built-in database, and Secrets by name in the prompt. Left to choose, the agent may hand-roll login or hardcode configuration, and unwinding that costs more than the original build.
Proof-of-done checklist
End the spec with checks the agent must run and report before stopping: endpoints curled with status codes pasted, a seed row edited to confirm the UI reacts. The agent has a shell and a webview, so make it use them.
States before styling
Specify empty, loading, and failure states in the first prompt, not as cleanup. Generated apps default to happy-path screens, and retrofitting states means reopening components the agent considered finished.
Common mistakes
The whole product in one prompt
A ten-feature request produces a build you cannot verify piece by piece, and effort goes into features you will change anyway. Cut it into milestones and confirm each one works before asking for the next.
Vague data model
If the spec says "store users and orders" without fields, the agent invents a schema, and correcting it after seed data and queries exist means a migration. List field names, or ask the agent to propose the schema in chat before it writes code.
Secrets pasted in chat
Keys dropped into the conversation tend to end up committed in code. Add them through the Secrets pane, give the agent only the variable name, and check the diff for hardcoded values before deploying.