# Start here: how we ship with AI agents

> The stack, rules and agent skills behind three products built by Steve Martocci with AI coding agents. Updated Oct 12, 2026. Canonical: https://stevemartocci.com/start.md

## The products

- **A fan app for a touring band**: iOS, Android, Apple TV, Android TV, Roku, CarPlay and web, with live video, an audio archive and an admin CMS.
- **Deb**: A money agent you text. An SMS and iMessage thread with secure web links for sensitive steps. No app to download.
- **SlowTV**: A calm, curated TV for kids: channel-based video on a kiosk-mode phone or TV, configured by a parent from the web.

## The loop

1. **Prompt.** An idea becomes a Devin session, usually typed from my phone. No ticket, no spec doc. The repo's AGENTS.md and skills carry the context.
2. **Build.** Devin plans, writes the code and its tests, and runs the repo gate (pnpm lint, typecheck, test through Turborepo) before it opens a PR.
3. **Preview.** Every PR gets its own Convex preview deployment, a Vercel preview wired to it, and an EAS Update preview I can open on my phone.
4. **Prove.** Devin drives the change like a user: Playwright for web, Android emulator, iOS and Apple TV simulators and cloud devices for mobile. It sends back a screen recording.
5. **Ship.** Merge to main. Convex deploys from CI, Vercel ships the web apps and EAS Update pushes JS over the air. Native changes go through EAS Build to TestFlight.
6. **Watch.** New UX ships behind PostHog flags (on in dev, off in prod) with the old version kept as a variant. Sentry and Expo Observe catch what breaks.

## Devin: The engineering team

https://devin.ai

- Writes nearly every line across all three products. Each idea is a session, and many run in parallel.
- Blueprints snapshot each repo's environment (dependencies, Android emulator, iOS and tvOS simulators on Mac machines), so a session boots ready to build and test.
- Reads the same AGENTS.md and .agents/skills that Claude Code, Codex and Cursor read, so the knowledge lives in the repo.
- A testing agent drives the running app and records video. Devin Review comments on the PR.
- MCP servers for Convex, Vercel, Expo, PostHog, Sentry, Linear, Figma and Mobbin let it check real data and real deploys.
- Scheduled automations run unattended overnight. I merge in the morning.

## Convex: The backend

https://convex.dev

- One reactive TypeScript backend per product: schema, queries, mutations, actions, crons and HTTP routes. No REST layer, no ORM, no SQL migrations.
- Agents work well with it because everything is typed TypeScript in one folder, with validators on every public function.
- convex-test plus Vitest runs the whole backend suite in CI with no live deployment.
- CI creates a preview deployment per PR, deploys production from main, and cleans previews up when the PR closes.
- Better Auth runs inside Convex through @convex-dev/better-auth. @convex-dev/agent handles AI threads.

## Vercel: Web hosting and AI plumbing

https://vercel.com

- Hosts the Next.js apps: marketing sites, web apps and admin consoles.
- Every PR gets a preview URL pointed at that PR's Convex preview, so the whole stack is testable before merge.
- An ignore-build script skips deploys for apps a PR didn't touch, which matters in a monorepo.
- The AI SDK (ai) and AI Gateway are how every product talks to models.
- The skills CLI (npx skills) installs and pins third-party agent skills.

## Expo: Every native surface

https://expo.dev

- One TypeScript codebase for iPhone, Android, Apple TV and Android TV (via react-native-tvos), plus CarPlay and Live Activities through config plugins and local Expo modules.
- Latest SDK, React Compiler on, and Expo Router for navigation.
- EAS Update ships JS over the air and publishes a preview per PR. EAS Build and Workflows handle simulator builds, TestFlight and the stores.
- Expo Observe and Sentry report performance and crashes from real devices.
- Native folders are generated by prebuild and never committed.

## The stack

### Foundation

- [pnpm workspaces](https://pnpm.io): Pinned version, frozen lockfile in CI (Fan app, Deb, SlowTV)
- [Turborepo](https://turborepo.com): One lint, typecheck and test gate across apps (Fan app, Deb, SlowTV)
- [TypeScript](https://www.typescriptlang.org): Everywhere, strict (Fan app, Deb, SlowTV)
- [Zod](https://zod.dev): Validation at the edges (Fan app, Deb, SlowTV)

### Web

- [Next.js 16 + React 19](https://nextjs.org): Sites, web apps, admin consoles (Fan app, Deb, SlowTV)
- [Tailwind CSS v4](https://tailwindcss.com): Theme tokens shared with mobile (Fan app, Deb, SlowTV)
- [shadcn/ui pattern](https://ui.shadcn.com): cva + clsx + tailwind-merge primitives in a shared ui package (Fan app, Deb, SlowTV)
- [Lucide](https://lucide.dev): Icons (Fan app, Deb, SlowTV)

### Backend and AI

- [Convex](https://convex.dev): Database, functions, crons, HTTP, file storage (Fan app, Deb, SlowTV)
- [Better Auth](https://better-auth.com): Auth via @convex-dev/better-auth, @better-auth/expo on mobile (Fan app, Deb, SlowTV)
- [Vercel AI SDK](https://ai-sdk.dev): Model calls, tools, streaming (Fan app, Deb, SlowTV)
- [@convex-dev/agent](https://www.convex.dev/components/agent): Persistent agent threads in Convex (Fan app, SlowTV)
- [convex-helpers](https://github.com/get-convex/convex-helpers): Custom functions, relationships (Fan app, SlowTV)
- [Resend](https://resend.com): Transactional email (Fan app, SlowTV)
- [Cloudflare Workers + R2](https://developers.cloudflare.com/workers/): Media pipelines and edge jobs Convex shouldn't run (Fan app, Deb)

### Mobile and TV

- [Expo SDK + Expo Router](https://docs.expo.dev): Latest SDK, file-based routes (Fan app, SlowTV)
- [React Compiler](https://react.dev/learn/react-compiler): On by default, no manual memo (Fan app, SlowTV)
- [Reanimated 4 + Worklets](https://docs.swmansion.com/react-native-reanimated/): All animation on the UI thread (Fan app, SlowTV)
- [React Native Skia](https://shopify.github.io/react-native-skia/): Custom drawing and shaders (Fan app, SlowTV)
- [expo-video + expo-updates](https://docs.expo.dev/versions/latest/sdk/video/): Playback, OTA updates (Fan app, SlowTV)
- [react-native-tvos](https://github.com/react-native-tvos/react-native-tvos): Apple TV and Android TV from the same code (Fan app, SlowTV)
- [Uniwind (Pro)](https://uniwind.dev): Tailwind v4 classNames in React Native (Fan app)
- [Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/): Native gestures (Fan app)
- [FlashList](https://shopify.github.io/flash-list/): Every long list (Fan app)
- [Keyboard Controller](https://kirillzyusko.github.io/react-native-keyboard-controller/): Keyboard-aware sheets and forms (Fan app)
- [expo-haptics](https://docs.expo.dev/versions/latest/sdk/haptics/): Behind one shared haptics helper (Fan app, SlowTV)

### Quality

- [Vitest + convex-test](https://vitest.dev): Unit and backend tests, no live deploy (Fan app, Deb, SlowTV)
- [Playwright](https://playwright.dev): Web E2E against Convex previews (Fan app, SlowTV)
- [ESLint 9 + @shadcn/lint](https://ui.shadcn.com): Design-system lint: no raw colors or off-scale values (Fan app, Deb)
- [react-doctor](https://github.com/millionco/react-doctor): Blocking React perf and correctness check in CI (Fan app)
- [Revyl](https://revyl.ai): Cloud iOS and Android devices for agent-driven tests (Fan app, SlowTV)

### Observe

- [PostHog](https://posthog.com): Analytics, flags, A/B variants, session replay (Fan app, Deb, SlowTV)
- [Sentry](https://sentry.io): Errors on web, mobile and backend (Fan app, Deb, SlowTV)
- [Expo Observe](https://docs.expo.dev): Real-device performance (Fan app, SlowTV)

## House rules

- Every repo has a short root AGENTS.md: what lives where, how to run it, how to validate it, what never to do. Deep knowledge goes in skills.
- Product invariants are written down as hard rules (for example, "never take an action without explicit approval"), so the agent can say no to me.
- New non-trivial logic ships with unit tests in the same PR. Bugs go red, then green.
- Shared rules live in a pure packages/domain module that the web, mobile and backend all import.
- Lint enforces the design system: theme tokens only, one haptics helper, reduced-motion checks.
- Mobile clients can run old code against a new backend, so a contract test lists every function the app calls, and breaking changes need a shim or a forced update.
- No secrets in code, no PII in analytics, never point local commands at production.

## Skills to copy

Save each block at the path shown. Any agent that reads AGENTS.md and .agents/skills (Devin, Claude Code, Codex, Cursor) picks them up.

### AGENTS.md

````markdown
# AGENTS.md

Repo-wide policy for coding agents. Keep it short. Deep guidance lives in
`.agents/skills/*/SKILL.md`.

## Precedence
1. This file. 2. The nearest nested AGENTS.md. 3. The relevant skill.
4. Task instructions. The more specific file wins.

## Layout
pnpm workspaces + Turborepo.
- apps/web       Next.js (latest) + React 19 + Tailwind v4
- apps/mobile    Expo (latest SDK) + Expo Router, iOS / Android / TV
- apps/convex    Convex backend (schema, functions, crons, HTTP)
- packages/domain  Pure shared logic + types. Change rules here, once.
- packages/ui    Shared shadcn-style primitives. Check here before writing raw controls.

## Product invariants
<List the rules the product must never break. Agents should refuse
changes that violate them.>

## Validate before you finish
`pnpm install --frozen-lockfile && pnpm lint && pnpm typecheck && pnpm test`
Run the narrowest package filter while iterating, the full gate before a PR.

## Change rules
- Non-trivial logic ships with unit tests in the same PR. Extract it from
  components into plain modules and test those.
- Bugs: reproduce, add a failing test, fix, re-run.
- UI changes: verify in a real browser or simulator and attach screenshots
  of every state (loading, empty, error).
- New user journeys leave a repeatable E2E test behind.
- New UX ships behind a PostHog flag: on in dev, off in prod, old version
  kept as a variant.

## Deploys
- Convex deploys to production from CI on push to main. Every PR gets a
  Convex preview deployment, a Vercel preview and an EAS Update preview.
- Never run local commands against production. Never seed production.

## Never
- Commit secrets, .env files or generated native folders (ios/, android/).
- Put email, phone numbers, OTPs or other PII in analytics or logs.
````

### .agents/skills/convex/SKILL.md

````markdown
---
name: convex
description: Convex backend rules. Use for any work in apps/convex or code that
  calls it - schema, queries, mutations, actions, indexes, validators, crons,
  HTTP routes, auth, convex-test tests, codegen and deploys.
---

# Convex

## Mental model
- Queries are reactive TypeScript functions running in the database.
- Each mutation is one ACID transaction.
- Actions do side effects (fetch, Node libs) and have no ctx.db. They use
  ctx.runQuery / ctx.runMutation.
- Schema is enforced on deploy. There are no SQL migrations: widen, backfill,
  then tighten.

## Deploy-breaking mistakes
1. Never make a schema change that existing documents don't satisfy. Add new
   fields as v.optional, backfill, then tighten.
2. Never pass undefined into a write. Omit the key.
3. Never edit convex/_generated. Run codegen.
4. "use node" files export actions only, no queries or mutations.
5. Every public function has args (and ideally returns) validators.
6. New args on an existing public function deploy before any client sends
   them. Ship the validator change in its own PR.
7. Query indexes in field order. Use .withIndex(), not .filter(), for reads.
8. Await every ctx.db.* and ctx.scheduler.* call.
9. Use internal.* for scheduled and server-to-server calls.
10. Shared helpers live in convex/lib/ and are not functions.

## Clients
Import a typed api from a shared types-only package (for example
packages/convex-api re-exporting convex/_generated/api) so web and mobile
get full types.

## Validate
`pnpm --filter <convex-pkg> codegen && pnpm --filter <convex-pkg> typecheck \
  && pnpm --filter <convex-pkg> test && pnpm --filter <convex-pkg> lint`
Tests use convex-test + Vitest and need no live deployment.

## Previews
CI creates a Convex preview deployment per PR, seeds it with demo data, and
deletes it when the PR closes. Production deploys only from main.
````

### .agents/skills/mobile-ui/SKILL.md

````markdown
---
name: mobile-ui
description: Conventions for the Expo app. Use when building or changing
  screens, styling, animation, gestures, lists, keyboard handling or haptics
  in apps/mobile.
---

# Mobile UI

## Styling
- Uniwind (Tailwind v4 for React Native) is the default. Use className on
  core components and keep class strings static.
- Use theme tokens from global.css (bg-surface, text-text-secondary...), never
  raw palette colors or arbitrary values that have an on-scale equivalent.
- useResolveClassNames for APIs that only take style objects. withUniwind only
  for third-party components without className.

## Motion
- Reanimated only (never Animated from react-native), and keep work on the UI
  thread with worklets.
- Every entering/exiting animation respects useReducedMotion().
- Every Pressable has a visible pressed state (active: class or pressed style).

## Lists, keyboard, haptics
- FlashList for any list that can grow.
- react-native-keyboard-controller: KeyboardAwareScrollView with
  keyboardShouldPersistTaps="handled", never KeyboardAvoidingView.
- Haptics only through src/lib/haptics.ts (selection, tap, press, destructive,
  confirm, warn, error), never expo-haptics directly.
- No hardcoded bottom padding. Use safe-area utilities or useSafeAreaInsets.

## Platform
- React Compiler is on. Don't add useMemo/useCallback by reflex.
- ios/ and android/ come from expo prebuild and are never committed. Native
  code lives in local Expo modules and config plugins.

## Verify
Run it on a simulator or emulator, screenshot every state (loading, empty,
error, long text), and check reduced motion and dark mode.

Enforce the rules above with ESLint (no-restricted-imports plus a small
custom plugin) so the agent gets told the fix, not just the error.
````

### .agents/skills/app-versioning/SKILL.md

````markdown
---
name: app-versioning
description: Keep installed mobile and TV apps working against a newer
  backend. Use when removing, renaming or tightening a backend function the
  app calls, or when making any native change.
---

# App versioning and forced updates

Installed apps run old JS against your newest backend ("drift").

## Two layers
1. OTA (JS-only): expo-updates checks on launch and on foreground, then
   reloads at a safe moment (never mid-playback or mid-form). Additive
   changes heal themselves.
2. Min-version gate (native): a reactive query compares the app's native
   version to a staff-set minimum and shows a blocking "Update" screen
   below it. It fails open, so missing config never bricks the app.

## Breaking changes
- Removing or renaming a function the app calls.
- Tightening an arg or return validator, or changing what a function means.
- Anything that bumps the native runtimeVersion.

## When you must break
1. Prefer a back-compat shim: keep the old name or shape working.
2. Otherwise bump the app version and ship a native build.
3. Raise MIN_SUPPORTED_APP_VERSION in packages/domain and prune the contract.
4. After the build is live, raise the floor in the admin console.

## Guardrail
Keep a MOBILE_BACKEND_CONTRACT list of every function the app calls. A CI test
fails if the app calls something not in the list, if a listed function no
longer exists, or if the minimum version exceeds the shipped version. It
can't catch semantic changes, so use judgment too.
````

### .agents/skills/ship/SKILL.md

````markdown
---
name: ship
description: How a change gets from prompt to production. Use before opening
  or updating a PR, and when asked to test, deploy or verify a change.
---

# Ship

1. Branch from latest main. One PR per change. Consolidate parallel work into
   one integration PR with conflicts resolved.
2. Run the full root gate before opening the PR:
   `pnpm install --frozen-lockfile && pnpm lint && pnpm typecheck && pnpm test`
3. Open the PR with: what changed, why, tests run, risks, and screenshots or a
   recording for anything visual.
4. Wait for previews: Convex preview deployment, Vercel preview URL, EAS Update
   preview. Test against those, not localhost, when you can.
5. Prove the user outcome on the real target:
   - Web: Playwright against the preview.
   - Mobile: emulator, simulator or cloud device. Record it.
   - Replay a new E2E test 3 times in a row before calling it stable.
6. Keep the PR mergeable. When main moves, merge it in and resolve conflicts
   so both sides survive.
7. After merge, confirm the deploy (Convex from CI, Vercel, EAS Update) and
   check Sentry/PostHog for regressions.

Report status plainly: what is merged, what is deployed, what is live, and
what the human still has to do.
````

## Public skills

- `npx skills add get-convex/agent-skills`: Convex's official skills
- `npx skills add expo/skills`: Expo's official skills: EAS Update, Router, native UI, upgrades
- `npx skills add vercel-labs/agent-skills`: Vercel's skills: React and React Native best practices, deploys
- `npx skills add uni-stack/uniwind --skill uniwind`: Uniwind styling for React Native
- `npx skills add shadcn/ui --skill shadcn`: shadcn components and composition
- `npx skills add pbakaus/impeccable`: Design critique and polish for any frontend
- `npx skills add cloudflare/skills --skill wrangler --skill workers-best-practices`: Cloudflare Workers, if you run edge jobs
- `npx skills add safaiyeh/app-store-review-skill`: Check an app against Apple's review guidelines before you submit

## Hand this to your agent

````text
Read https://stevemartocci.com/start.md and set this repo up the same way.

1. Compare our stack to it and list the gaps. Don't change frameworks
   without asking me.
2. Write a root AGENTS.md from the template, filled in with our real
   layout, commands and product invariants.
3. Add the skills from that page under .agents/skills/, adapted to our
   paths and package names.
4. Install the public skills that match our stack with `npx skills add`.
5. Make sure CI runs pnpm lint, typecheck and test, and tell me which
   preview environments (Convex, Vercel, EAS Update) are missing.
Open one PR with all of it.
````
