—— Start here

Three products. One stack. Agents write the code.

I build with AI coding agents now, mostly Devin, mostly from my phone. This is the stack and the workflow I use across three products, pulled from the actual repos. The skills at the bottom are copy-paste ready, so you can give the same workflow to your own agent.

Whole page as MarkdownUpdated

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

Every change, in every repo, goes through the same six steps. Most of them happen without me.

  1. 01

    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. 02

    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. 03

    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. 04

    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. 05

    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. 06

    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.

The four things I'd pick again

The engineering team

Devin

  • 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.

The backend

Convex

  • 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.

Web hosting and AI plumbing

Vercel

  • 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.

Every native surface

Expo

  • 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 whole stack

Tags show which products use each piece today. Deb has no native app, so it skips the mobile group.

Foundation

  • pnpm workspacesPinned version, frozen lockfile in CIFan appDebSlowTV
  • TurborepoOne lint, typecheck and test gate across appsFan appDebSlowTV
  • TypeScriptEverywhere, strictFan appDebSlowTV
  • ZodValidation at the edgesFan appDebSlowTV

Web

Backend and AI

  • ConvexDatabase, functions, crons, HTTP, file storageFan appDebSlowTV
  • Better AuthAuth via @convex-dev/better-auth, @better-auth/expo on mobileFan appDebSlowTV
  • Vercel AI SDKModel calls, tools, streamingFan appDebSlowTV
  • @convex-dev/agentPersistent agent threads in ConvexFan appSlowTV
  • convex-helpersCustom functions, relationshipsFan appSlowTV
  • ResendTransactional emailFan appSlowTV
  • Cloudflare Workers + R2Media pipelines and edge jobs Convex shouldn't runFan appDeb

Mobile and TV

Quality

  • Vitest + convex-testUnit and backend tests, no live deployFan appDebSlowTV
  • PlaywrightWeb E2E against Convex previewsFan appSlowTV
  • ESLint 9 + @shadcn/lintDesign-system lint: no raw colors or off-scale valuesFan appDeb
  • react-doctorBlocking React perf and correctness check in CIFan app
  • RevylCloud iOS and Android devices for agent-driven testsFan appSlowTV

Observe

  • PostHogAnalytics, flags, A/B variants, session replayFan appDebSlowTV
  • SentryErrors on web, mobile and backendFan appDebSlowTV
  • Expo ObserveReal-device performanceFan appSlowTV

House rules

—— Copy this

Give your agent our workflow

Save each file at the path shown. Devin, Claude Code, Codex and Cursor all read AGENTS.md and .agents/skills, so the knowledge lives in the repo, not in one tool.

AGENTS.mdAGENTS.md
AGENTS.md
# 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.
Convex.agents/skills/convex/SKILL.md
.agents/skills/convex/SKILL.md
---
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.
Mobile UI.agents/skills/mobile-ui/SKILL.md
.agents/skills/mobile-ui/SKILL.md
---
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.
App versioning.agents/skills/app-versioning/SKILL.md
.agents/skills/app-versioning/SKILL.md
---
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.
Ship.agents/skills/ship/SKILL.md
.agents/skills/ship/SKILL.md
---
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 we install

Official skills from the teams that make the tools. Add -a claude-code (or your agent's name) to target one tool.

Or hand it all over at once

Paste this into Devin or any coding agent pointed at your repo.

prompt
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.
/start.md