{
 "view": {
  "w": 1177.0,
  "h": 720
 },
 "modes": {
  "label": "Environment",
  "options": [
   {
    "id": "prod",
    "label": "Production",
    "icon": "dot"
   },
   {
    "id": "preview",
    "label": "Preview",
    "icon": "dot-test"
   }
  ]
 },
 "stages": [
  {
   "n": 1,
   "title": "Put it online",
   "checks": [
    {
     "id": "SHIP-1",
     "title": "Save the code",
     "tool": "GitHub",
     "level": "toy",
     "done": "The code is in a private GitHub repo, and .env files are ignored.",
     "verify": "gh repo view shows your latest commit."
    },
    {
     "id": "SHIP-2",
     "title": "Keep secrets out of the code",
     "tool": "Vercel environment variables",
     "level": "toy",
     "done": "No keys or passwords in the code or its history. Each one lives in Vercel, marked Secret.",
     "verify": "Codex searches the repo and its history and finds no secrets."
    },
    {
     "id": "SHIP-3",
     "title": "Deploy on every push",
     "tool": "Vercel",
     "level": "toy",
     "done": "Pushing to main updates production, and every branch gets a preview link.",
     "verify": "Push a one-line change and watch it go live."
    },
    {
     "id": "SHIP-4",
     "title": "Get a real address",
     "tool": "Vercel Domains",
     "level": "v1",
     "done": "Your domain loads over HTTPS, and www redirects to it.",
     "verify": "curl -sI https://yourdomain returns 200."
    }
   ]
  },
  {
   "n": 2,
   "title": "Let people in",
   "checks": [
    {
     "id": "PEOPLE-1",
     "title": "Store data for real",
     "tool": "Neon",
     "level": "v1",
     "done": "All app data lives in Neon Postgres tables, created by migrations in the repo.",
     "verify": "Something saved on your phone shows up on your laptop."
    },
    {
     "id": "PEOPLE-2",
     "title": "Lock every table",
     "tool": "Row-level security",
     "level": "v1",
     "done": "Row-level security is on for every table.",
     "verify": "Signed in as one test user, you can't read another user's data."
    },
    {
     "id": "PEOPLE-3",
     "title": "Real accounts",
     "tool": "Neon Auth",
     "level": "v1",
     "done": "Sign-up, login, logout, and password reset work, and private pages check the session on the server.",
     "verify": "A Playwright test runs the whole flow on a preview link."
    },
    {
     "id": "PEOPLE-4",
     "title": "Emails that arrive",
     "tool": "Resend",
     "level": "v1",
     "done": "Resend is verified for your domain and sends the sign-in emails.",
     "verify": "A brand-new address gets the sign-up email within a minute."
    },
    {
     "id": "PEOPLE-5",
     "title": "Files and uploads",
     "tool": "Neon Object Storage",
     "level": "v1",
     "done": "Uploads go to private object storage, and the server hands out short-lived links only to their owner.",
     "verify": "One test user can't list or download another user's files."
    }
   ]
  },
  {
   "n": 3,
   "title": "Take money",
   "checks": [
    {
     "id": "MONEY-1",
     "title": "Charge with Checkout",
     "tool": "Stripe Checkout",
     "level": "business",
     "done": "The buy button opens Stripe Checkout in test mode, and prices live in Stripe.",
     "verify": "Test card 4242 4242 4242 4242 completes a purchase."
    },
    {
     "id": "MONEY-2",
     "title": "Record payments from Stripe",
     "tool": "Stripe webhooks",
     "level": "business",
     "done": "A payment is recorded only when Stripe's signed webhook arrives.",
     "verify": "Sending the same webhook twice still records one payment."
    },
    {
     "id": "MONEY-4",
     "title": "Switch to live payments",
     "tool": "Stripe",
     "level": "business",
     "done": "The business is verified, and live keys are set for production only.",
     "verify": "A real $1 purchase, then a refund."
    }
   ]
  },
  {
   "n": 4,
   "title": "Add AI features",
   "checks": [
    {
     "id": "AI-1",
     "title": "Route AI through one gateway",
     "tool": "Vercel AI Gateway",
     "level": "toy",
     "done": "Every AI call goes through the gateway, with a project budget as a safety net.",
     "verify": "Your test call shows up in the gateway's usage view, with its cost."
    },
    {
     "id": "AI-2",
     "title": "Give each user an AI allowance",
     "tool": "Neon",
     "level": "v1",
     "done": "Each user's AI usage is recorded on the server, and calls stop when they run out.",
     "verify": "A test user who runs out gets a friendly message instead of another AI call."
    },
    {
     "id": "AI-3",
     "title": "Charge for AI",
     "tool": "Stripe",
     "level": "business",
     "done": "A subscription or credit pack adds allowance when Stripe confirms the payment.",
     "verify": "A test purchase raises the test user's allowance."
    },
    {
     "id": "AI-4",
     "title": "Let users sign in with ChatGPT (beta)",
     "tool": "Sign in with ChatGPT",
     "level": "business",
     "done": "People with ChatGPT Plus or Pro can connect their plan, and their AI use draws on it instead of your budget.",
     "verify": "A test user signed in with ChatGPT uses an AI feature, and nothing new shows up in your AI Gateway."
    }
   ]
  },
  {
   "n": 5,
   "title": "Don't break it",
   "checks": [
    {
     "id": "STEADY-1",
     "title": "Test the core flow",
     "tool": "Playwright",
     "level": "v1",
     "done": "One end-to-end test goes through the whole core flow the way a person would.",
     "verify": "npx playwright test passes."
    },
    {
     "id": "STEADY-2",
     "title": "Check every change",
     "tool": "GitHub Actions",
     "level": "v1",
     "done": "Tests run on every push, and a failing change can't merge into main.",
     "verify": "A deliberately broken branch shows a failing check."
    },
    {
     "id": "STEADY-3",
     "title": "Preview before production",
     "tool": "Vercel previews",
     "level": "toy",
     "done": "You try every change on its preview link before merging.",
     "verify": "Every pull request has a preview link."
    },
    {
     "id": "STEADY-4",
     "title": "Write it down",
     "tool": "README · AGENTS.md · STACK.md",
     "level": "v1",
     "done": "README for people, AGENTS.md for agents, STACK.md for every service.",
     "verify": "A fresh Codex session can explain how to run, test, and deploy the app."
    }
   ]
  },
  {
   "n": 6,
   "title": "Watch it",
   "checks": [
    {
     "id": "WATCH-1",
     "title": "See what people do",
     "tool": "PostHog",
     "level": "v1",
     "done": "Five to eight named events describe the core flow, with one funnel dashboard.",
     "verify": "Your own test run shows up in the funnel."
    },
    {
     "id": "WATCH-2",
     "title": "Hear when it breaks",
     "tool": "PostHog error tracking",
     "level": "v1",
     "done": "Errors are captured and point to real lines of code.",
     "verify": "A test error shows the right file and line."
    },
    {
     "id": "WATCH-3",
     "title": "Know when it's down",
     "tool": "Checkly",
     "level": "business",
     "done": "An uptime check and a check of the core flow email you when they fail.",
     "verify": "Break the URL once and the alert arrives."
    },
    {
     "id": "WATCH-4",
     "title": "Ask what people think",
     "tool": "PostHog surveys",
     "level": "v1",
     "done": "A one-question survey appears after the core flow.",
     "verify": "Your test answer shows up in PostHog."
    },
    {
     "id": "WATCH-5",
     "title": "Read your logs",
     "tool": "Vercel logs",
     "level": "v1",
     "done": "Codex can pull recent server logs and find what caused an error.",
     "verify": "Ask Codex for the last hour of errors."
    }
   ]
  },
  {
   "n": 7,
   "title": "Protect it",
   "checks": [
    {
     "id": "PROTECT-1",
     "title": "Slow down abuse",
     "tool": "Vercel Firewall · BotID",
     "level": "v1",
     "done": "A rate limit on /api, and BotID on sign-up and anything that costs money.",
     "verify": "200 fast requests get blocked; a normal visitor doesn't."
    },
    {
     "id": "PROTECT-2",
     "title": "Keep dependencies patched",
     "tool": "Dependabot",
     "level": "v1",
     "done": "Dependabot opens weekly update pull requests.",
     "verify": "GitHub shows Dependabot as active."
    },
    {
     "id": "PROTECT-3",
     "title": "Make data recoverable",
     "tool": "Neon",
     "level": "business",
     "done": "Restore points, and a recovery you've actually tried.",
     "verify": "Restore yesterday's data into a test branch."
    },
    {
     "id": "PROTECT-4",
     "title": "Say what you do with data",
     "tool": "Privacy policy · terms",
     "level": "v1",
     "done": "/privacy and /terms name every service in STACK.md that receives user data.",
     "verify": "Every service in STACK.md appears in /privacy."
    }
   ]
  },
  {
   "n": 8,
   "title": "Launch it",
   "checks": [
    {
     "id": "LAUNCH-1",
     "title": "Look good when shared",
     "tool": "Next.js metadata · Search Console",
     "level": "v1",
     "done": "Titles, descriptions, share images, a favicon, and a sitemap; Search Console verified.",
     "verify": "A link-preview validator shows your image."
    },
    {
     "id": "LAUNCH-2",
     "title": "Make a promo video",
     "tool": "Remotion",
     "level": "v1",
     "done": "A short video built from real screens, in the app's style.",
     "verify": "It still makes sense on a phone with the sound off."
    },
    {
     "id": "LAUNCH-3",
     "title": "Announce it",
     "tool": "Typefully",
     "level": "v1",
     "done": "Launch posts drafted and approved.",
     "verify": "The posts are scheduled."
    },
    {
     "id": "LAUNCH-4",
     "title": "Let other software in",
     "tool": "API · MCP server",
     "level": "business",
     "done": "Documented API routes, plus an MCP server at /api/mcp.",
     "verify": "Add it with codex mcp add, then ask Codex to use it."
    },
    {
     "id": "LAUNCH-5",
     "title": "Put it on phones",
     "tool": "PWA, then Expo",
     "level": "business",
     "done": "The app installs to a phone's home screen, with an icon.",
     "verify": "Add to Home Screen works on an iPhone."
    },
    {
     "id": "LAUNCH-6",
     "title": "Run a professional launch audit",
     "tool": "Go Launch Checklist",
     "level": "business",
     "done": "An evidence-backed GO, NO-GO, or NEEDS EVIDENCE call on every check, with blockers named.",
     "verify": "Every row cites evidence you could rerun, not a claim.",
     "link": {
      "href": "https://github.com/fractal-nyc/software-engineering-go-launch-checklist",
      "label": "Advanced: the full Go Launch Checklist"
     }
    }
   ]
  }
 ],
 "nodes": [
  {
   "id": "search",
   "label": "Search & social",
   "tool": "Google",
   "logos": [
    "google"
   ],
   "glyph": null,
   "x": 96.3,
   "y": 70,
   "w": 160.5,
   "h": 58,
   "checks": [
    "LAUNCH-1",
    "LAUNCH-2",
    "LAUNCH-3"
   ],
   "desc": "How strangers find you: search results and link previews.",
   "env": [
    "off",
    "hidden from search"
   ],
   "prompts": [
    "seo",
    "launch-kit"
   ]
  },
  {
   "id": "people",
   "label": "People",
   "tool": "",
   "logos": [],
   "glyph": "person",
   "x": 96.3,
   "y": 190,
   "w": 160.5,
   "h": 58,
   "checks": [
    "PROTECT-4",
    "LAUNCH-5"
   ],
   "desc": "Everyone who uses the app. Every other box exists to serve them.",
   "env": [
    "test",
    "you and testers"
   ],
   "prompts": [
    "privacy"
   ]
  },
  {
   "id": "agents",
   "label": "API/MCPs",
   "tool": "",
   "logos": [
    "modelcontextprotocol"
   ],
   "glyph": null,
   "x": 96.3,
   "y": 310,
   "w": 160.5,
   "h": 58,
   "checks": [
    "LAUNCH-4"
   ],
   "desc": "Other apps and AI agents that use your product through its API or MCP server.",
   "env": [
    "same",
    ""
   ],
   "prompts": [
    "api"
   ]
  },
  {
   "id": "domain",
   "label": "Domain",
   "tool": "Vercel",
   "logos": [
    "vercel"
   ],
   "glyph": null,
   "x": 288.9,
   "y": 190,
   "w": 149.8,
   "h": 58,
   "checks": [
    "SHIP-4"
   ],
   "desc": "Your address. It points browsers, and email providers, to the right place.",
   "env": [
    "test",
    "preview links"
   ],
   "prompts": [
    "domain"
   ]
  },
  {
   "id": "edge",
   "label": "Firewall",
   "tool": "Vercel",
   "logos": [
    "vercel"
   ],
   "glyph": null,
   "x": 470.8,
   "y": 190,
   "w": 149.8,
   "h": 58,
   "checks": [
    "PROTECT-1"
   ],
   "desc": "Turns away bots and floods of traffic before they reach your app or your bill.",
   "env": [
    "same",
    ""
   ],
   "prompts": [
    "firewall",
    "protect-internet"
   ]
  },
  {
   "id": "app",
   "label": "Your app",
   "tool": "Next.js · Vercel",
   "logos": [
    "nextdotjs",
    "vercel"
   ],
   "glyph": null,
   "x": 668.8,
   "y": 190,
   "w": 192.6,
   "h": 110,
   "checks": [
    "SHIP-3",
    "LAUNCH-6"
   ],
   "desc": "The code Codex wrote, running on Vercel.",
   "env": [
    "test",
    "preview deployment"
   ],
   "prompts": [
    "deploy",
    "launch-audit"
   ]
  },
  {
   "id": "secrets",
   "label": "Secrets",
   "tool": "Vercel",
   "logos": [
    "vercel"
   ],
   "glyph": null,
   "x": 668.8,
   "y": 320,
   "w": 149.8,
   "h": 58,
   "checks": [
    "SHIP-2"
   ],
   "desc": "API keys and passwords, kept out of the code. Each environment gets its own.",
   "env": [
    "test",
    "test keys"
   ],
   "prompts": [
    "secrets",
    "people-audit"
   ]
  },
  {
   "id": "auth",
   "label": "Accounts",
   "tool": "Neon",
   "logos": [],
   "glyph": "shield",
   "x": 888.1,
   "y": 90,
   "w": 160.5,
   "h": 58,
   "checks": [
    "PEOPLE-3"
   ],
   "desc": "Knows who each person is. Logging in proves who you are; it doesn't decide what you can see.",
   "env": [
    "test",
    "test users"
   ],
   "prompts": [
    "people-auth",
    "people-audit"
   ]
  },
  {
   "id": "db",
   "label": "Data + files",
   "tool": "Neon",
   "logos": [
    "postgresql"
   ],
   "glyph": null,
   "x": 888.1,
   "y": 190,
   "w": 160.5,
   "h": 58,
   "checks": [
    "PEOPLE-1",
    "PEOPLE-2",
    "PEOPLE-5",
    "PROTECT-3"
   ],
   "desc": "Where everything is kept, with a lock on each person's rows and files.",
   "env": [
    "test",
    "Neon branch"
   ],
   "prompts": [
    "people-model",
    "people-audit"
   ],
   "link": {
    "href": "../organizing-your-data/index.html",
    "zoom": "in"
   }
  },
  {
   "id": "stripe",
   "label": "Payments",
   "tool": "Stripe",
   "logos": [
    "stripe"
   ],
   "glyph": null,
   "x": 1080.7,
   "y": 90,
   "w": 160.5,
   "h": 58,
   "checks": [
    "MONEY-1",
    "MONEY-2",
    "MONEY-4",
    "AI-3"
   ],
   "desc": "Takes the card, then tells your app by webhook that the payment went through.",
   "env": [
    "test",
    "test mode"
   ],
   "prompts": [
    "money-checkout",
    "ai-billing"
   ]
  },
  {
   "id": "email",
   "label": "Email",
   "tool": "Resend",
   "logos": [
    "resend"
   ],
   "glyph": null,
   "x": 1080.7,
   "y": 190,
   "w": 160.5,
   "h": 58,
   "checks": [
    "PEOPLE-4"
   ],
   "desc": "Sign-in links, receipts, and confirmations, sent from your own domain.",
   "env": [
    "test",
    "test inbox"
   ],
   "prompts": [
    "email"
   ]
  },
  {
   "id": "ai",
   "label": "AI models",
   "tool": "Vercel",
   "logos": [
    "vercel",
    "anthropic"
   ],
   "glyph": null,
   "x": 1080.7,
   "y": 290,
   "w": 160.5,
   "h": 58,
   "checks": [
    "AI-1",
    "AI-2",
    "AI-3",
    "AI-4"
   ],
   "desc": "The models behind the app's AI features. Every call goes through one gateway, so you can see and limit what it costs, and who's using it.",
   "env": [
    "test",
    "test key"
   ],
   "prompts": [
    "ai-gateway",
    "ai-allowance",
    "ai-billing",
    "ai-siwc"
   ],
   "link": {
    "href": "../supporting-ai-features/index.html",
    "zoom": "in"
   }
  },
  {
   "id": "you",
   "label": "You",
   "tool": "",
   "logos": [],
   "glyph": "person",
   "x": 96.3,
   "y": 470,
   "w": 137.0,
   "h": 58,
   "checks": [],
   "desc": "You decide what changes. Alerts and insights come back to you.",
   "env": [
    "same",
    ""
   ],
   "prompts": []
  },
  {
   "id": "codex",
   "label": "Your agent",
   "tool": "Codex",
   "logos": [
    "openai"
   ],
   "glyph": "terminal",
   "x": 278.2,
   "y": 470,
   "w": 137.0,
   "h": 58,
   "checks": [],
   "desc": "Your coding agent. It makes the change and records what it set up in STACK.md.",
   "env": [
    "same",
    ""
   ],
   "prompts": [],
   "link": {
    "href": "../fractal-ai-accelerator/index.html",
    "zoom": "out"
   }
  },
  {
   "id": "github",
   "label": "GitHub",
   "tool": "",
   "logos": [
    "github"
   ],
   "glyph": null,
   "x": 460.1,
   "y": 470,
   "w": 137.0,
   "h": 58,
   "checks": [
    "SHIP-1",
    "STEADY-4",
    "PROTECT-2"
   ],
   "desc": "The one true copy of the code, the docs, and the list of every service.",
   "env": [
    "same",
    ""
   ],
   "prompts": [
    "github"
   ]
  },
  {
   "id": "ci",
   "label": "Checks",
   "tool": "Playwright",
   "logos": [
    "githubactions"
   ],
   "glyph": null,
   "x": 642.0,
   "y": 470,
   "w": 137.0,
   "h": 58,
   "checks": [
    "STEADY-1",
    "STEADY-2"
   ],
   "desc": "Every change has to pass the core-flow tests before it can merge.",
   "env": [
    "same",
    ""
   ],
   "prompts": [
    "steady-lock"
   ]
  },
  {
   "id": "preview",
   "label": "Preview",
   "tool": "Vercel",
   "logos": [
    "vercel"
   ],
   "glyph": null,
   "x": 823.9,
   "y": 470,
   "w": 137.0,
   "h": 58,
   "checks": [
    "STEADY-3"
   ],
   "desc": "Every change gets its own private link to try before it reaches anyone.",
   "env": [
    "same",
    ""
   ],
   "prompts": [
    "preview"
   ]
  },
  {
   "id": "analytics",
   "label": "Analytics",
   "tool": "PostHog",
   "logos": [
    "posthog"
   ],
   "glyph": null,
   "x": 1080.7,
   "y": 410,
   "w": 160.5,
   "h": 58,
   "checks": [
    "WATCH-1",
    "WATCH-2",
    "WATCH-4"
   ],
   "desc": "What people did, where they gave up, what broke, and what they said.",
   "env": [
    "test",
    "test project"
   ],
   "prompts": [
    "watch-mornings"
   ]
  },
  {
   "id": "logs",
   "label": "Logs",
   "tool": "Vercel",
   "logos": [
    "vercel"
   ],
   "glyph": null,
   "x": 1080.7,
   "y": 500,
   "w": 160.5,
   "h": 58,
   "checks": [
    "WATCH-5"
   ],
   "desc": "A running record of what the server did. It's the first thing Codex reads when something breaks.",
   "env": [
    "same",
    ""
   ],
   "prompts": [
    "logs"
   ]
  },
  {
   "id": "uptime",
   "label": "Uptime",
   "tool": "Checkly",
   "logos": [],
   "glyph": "pulse",
   "x": 1080.7,
   "y": 590,
   "w": 160.5,
   "h": 58,
   "checks": [
    "WATCH-3"
   ],
   "desc": "Visits your app like a person, day and night, and tells you when it fails.",
   "env": [
    "off",
    "production only"
   ],
   "prompts": [
    "uptime"
   ]
  }
 ],
 "edges": [
  {
   "id": "search-people",
   "from": "search",
   "to": "people",
   "path": "ctx",
   "pts": [
    [
     96.3,
     99
    ],
    [
     96.3,
     161
    ]
   ],
   "label": "finds you",
   "side": "right"
  },
  {
   "id": "people-domain",
   "from": "people",
   "to": "domain",
   "path": "req",
   "pts": [
    [
     176.6,
     190
    ],
    [
     214.0,
     190
    ]
   ],
   "label": "visits"
  },
  {
   "id": "domain-edge",
   "from": "domain",
   "to": "edge",
   "path": "req",
   "pts": [
    [
     363.8,
     190
    ],
    [
     395.9,
     190
    ]
   ]
  },
  {
   "id": "edge-app",
   "from": "edge",
   "to": "app",
   "path": "req",
   "pts": [
    [
     545.7,
     190
    ],
    [
     572.5,
     190
    ]
   ]
  },
  {
   "id": "agents-edge",
   "from": "agents",
   "to": "edge",
   "path": "req",
   "pts": [
    [
     176.6,
     310
    ],
    [
     470.8,
     310
    ],
    [
     470.8,
     219
    ]
   ],
   "label": "API",
   "labelAt": [
    321.0,
    310
   ]
  },
  {
   "id": "app-auth",
   "from": "app",
   "to": "auth",
   "path": "req",
   "pts": [
    [
     765.1,
     190
    ],
    [
     786.5,
     190
    ],
    [
     786.5,
     90
    ],
    [
     807.9,
     90
    ]
   ]
  },
  {
   "id": "app-db",
   "from": "app",
   "to": "db",
   "path": "req",
   "pts": [
    [
     765.1,
     190
    ],
    [
     807.9,
     190
    ]
   ]
  },
  {
   "id": "app-stripe",
   "from": "app",
   "to": "stripe",
   "path": "req",
   "pts": [
    [
     765.1,
     190
    ],
    [
     786.5,
     190
    ],
    [
     786.5,
     40
    ],
    [
     984.4,
     40
    ],
    [
     984.4,
     90
    ],
    [
     1000.5,
     90
    ]
   ]
  },
  {
   "id": "app-email",
   "from": "app",
   "to": "email",
   "path": "req",
   "pts": [
    [
     765.1,
     190
    ],
    [
     786.5,
     190
    ],
    [
     786.5,
     40
    ],
    [
     984.4,
     40
    ],
    [
     984.4,
     190
    ],
    [
     1000.5,
     190
    ]
   ]
  },
  {
   "id": "app-ai",
   "from": "app",
   "to": "ai",
   "path": "req",
   "pts": [
    [
     765.1,
     190
    ],
    [
     786.5,
     190
    ],
    [
     786.5,
     40
    ],
    [
     984.4,
     40
    ],
    [
     984.4,
     290
    ],
    [
     1000.5,
     290
    ]
   ]
  },
  {
   "id": "secrets-app",
   "from": "secrets",
   "to": "app",
   "path": "req",
   "pts": [
    [
     668.8,
     291
    ],
    [
     668.8,
     245
    ]
   ],
   "label": "keys",
   "side": "right"
  },
  {
   "id": "you-codex",
   "from": "you",
   "to": "codex",
   "path": "dep",
   "pts": [
    [
     164.8,
     470
    ],
    [
     209.7,
     470
    ]
   ],
   "label": "asks"
  },
  {
   "id": "codex-github",
   "from": "codex",
   "to": "github",
   "path": "dep",
   "pts": [
    [
     346.7,
     470
    ],
    [
     391.6,
     470
    ]
   ],
   "label": "pushes"
  },
  {
   "id": "github-ci",
   "from": "github",
   "to": "ci",
   "path": "dep",
   "pts": [
    [
     528.6,
     470
    ],
    [
     573.5,
     470
    ]
   ],
   "label": "tests"
  },
  {
   "id": "ci-preview",
   "from": "ci",
   "to": "preview",
   "path": "dep",
   "pts": [
    [
     710.5,
     470
    ],
    [
     755.4,
     470
    ]
   ],
   "label": "builds"
  },
  {
   "id": "preview-app",
   "from": "preview",
   "to": "app",
   "path": "dep",
   "pts": [
    [
     761.8,
     441
    ],
    [
     761.8,
     245
    ]
   ],
   "label": "go live",
   "side": "left",
   "labelAt": [
    761.8,
    400
   ]
  },
  {
   "id": "app-analytics",
   "from": "app",
   "to": "analytics",
   "path": "fb",
   "pts": [
    [
     765.1,
     190
    ],
    [
     786.5,
     190
    ],
    [
     786.5,
     355
    ],
    [
     963.0,
     355
    ],
    [
     963.0,
     410
    ],
    [
     1000.5,
     410
    ]
   ],
   "label": "events",
   "labelAt": [
    888.1,
    355
   ],
   "below": true
  },
  {
   "id": "app-logs",
   "from": "app",
   "to": "logs",
   "path": "fb",
   "pts": [
    [
     765.1,
     190
    ],
    [
     786.5,
     190
    ],
    [
     786.5,
     355
    ],
    [
     963.0,
     355
    ],
    [
     963.0,
     500
    ],
    [
     1000.5,
     500
    ]
   ]
  },
  {
   "id": "analytics-you",
   "from": "analytics",
   "to": "you",
   "path": "fb",
   "pts": [
    [
     1161.0,
     410
    ],
    [
     1170.6,
     410
    ],
    [
     1170.6,
     652
    ],
    [
     96.3,
     652
    ],
    [
     96.3,
     499
    ]
   ]
  },
  {
   "id": "logs-you",
   "from": "logs",
   "to": "you",
   "path": "fb",
   "pts": [
    [
     1161.0,
     500
    ],
    [
     1170.6,
     500
    ],
    [
     1170.6,
     652
    ],
    [
     96.3,
     652
    ],
    [
     96.3,
     499
    ]
   ]
  },
  {
   "id": "uptime-you",
   "from": "uptime",
   "to": "you",
   "path": "fb",
   "pts": [
    [
     1161.0,
     590
    ],
    [
     1170.6,
     590
    ],
    [
     1170.6,
     652
    ],
    [
     96.3,
     652
    ],
    [
     96.3,
     499
    ]
   ],
   "label": "alerts come back to you",
   "labelAt": [
    599.2,
    652
   ]
  }
 ],
 "zones": [
  {
   "id": "backend",
   "label": "backend services",
   "x": 797.2,
   "y": 20,
   "w": 368.1,
   "h": 316,
   "labelRight": true
  },
  {
   "id": "change",
   "label": "change management",
   "x": 17.1,
   "y": 425,
   "w": 883.8,
   "h": 92
  },
  {
   "id": "monitor",
   "label": "monitoring",
   "x": 989.8,
   "y": 366,
   "w": 175.5,
   "h": 256
  }
 ],
 "tours": {
  "req": {
   "label": "a request",
   "steps": [
    {
     "edge": "people-domain",
     "text": "Maya opens the app's address on her phone."
    },
    {
     "edge": "domain-edge",
     "text": "The firewall checks she's a person, not a bot."
    },
    {
     "edge": "edge-app",
     "text": "Your app, running on Vercel, answers."
    },
    {
     "edge": "app-auth",
     "text": "She logs in, and Neon Auth confirms it's really her."
    },
    {
     "edge": "app-db",
     "text": "Her booking is saved where only she can see it."
    },
    {
     "edge": "app-stripe",
     "text": "She pays through Stripe Checkout."
    },
    {
     "edge": "app-email",
     "text": "Resend emails her a confirmation."
    },
    {
     "edge": "app-analytics",
     "text": "PostHog counts one completed booking."
    }
   ]
  },
  "dep": {
   "label": "a change",
   "steps": [
    {
     "edge": "you-codex",
     "text": "You ask Codex to add a waitlist."
    },
    {
     "edge": "codex-github",
     "text": "Codex pushes the change to a branch."
    },
    {
     "edge": "github-ci",
     "text": "The tests check that people can still book and pay."
    },
    {
     "edge": "ci-preview",
     "text": "A preview link lets you try it on your phone."
    },
    {
     "edge": "preview-app",
     "text": "You merge, and it's live in about a minute."
    }
   ]
  },
  "fb": {
   "label": "a problem",
   "steps": [
    {
     "edge": "uptime-you",
     "text": "3:02am: Checkly's test booking fails, and you get an email."
    },
    {
     "edge": "app-analytics",
     "text": "PostHog shows the error and who it affected."
    },
    {
     "edge": "app-logs",
     "text": "The logs show what the server was doing when it failed."
    },
    {
     "edge": "you-codex",
     "text": "You hand the error to Codex: fix it, and add a test."
    },
    {
     "edge": "preview-app",
     "text": "The fix goes through the same checks, and it's healthy by 3:40."
    }
   ]
  }
 },
 "prompts": {
  "seo": {
   "id": "seo",
   "title": "Set up search and share previews",
   "stage": 8,
   "checks": [
    "LAUNCH-1"
   ],
   "body": "Make this app easy to find in search and good-looking when someone shares a link: page titles and descriptions, share images, a favicon, a sitemap, and the site verified in Google Search Console.\n\nCheck what already exists first. Most frameworks have built-in ways to produce metadata, share images, and sitemaps, and some may already be done. The classic mistakes are every page sharing one generic title, share images that break in previews, and private pages showing up in search.\n\nResearch the current best practice for this framework, look at what the app has, and choose the simplest approach that works. Before changing anything significant, show me your plan, what you rejected, and any risks. Verifying Search Console may need a DNS record or a sign-in from me, so tell me exactly what to do.\n\nTry it on a preview deployment first. Then show me how shared links look, the sitemap, and the Search Console status, and record what you chose in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "launch-kit": {
   "id": "launch-kit",
   "title": "Make a launch video and posts",
   "stage": 8,
   "checks": [
    "LAUNCH-1",
    "LAUNCH-2",
    "LAUNCH-3"
   ],
   "body": "Get this app ready to launch: search and link previews that look good, a short promo video built from real screens, and launch posts for me to approve.\n\nPage titles, descriptions, share images, a favicon, a sitemap, and Google Search Console verification may already be done, so check and only fill gaps. For the video, a good default is recording the real app with a browser tool like Playwright and assembling it in Remotion, in the app's own look, with no stock footage. A viewer should understand what the app does within seconds. Typefully is a good default for drafting posts. The classic failure is a video that looks generic or is unreadable on a phone.\n\nResearch what works now for launches like this and choose the simplest approach. Before rendering, show me a short storyboard, the angle for the posts, and what you rejected. Record with test accounts, never real users' data.\n\nNever post or schedule anything without my approval. When it's ready, show me the video, previews, and drafts, and note where the source files live in the project's documentation.",
   "primary": false
  },
  "privacy": {
   "id": "privacy",
   "title": "Write a privacy policy and terms",
   "stage": 7,
   "checks": [
    "PROTECT-4"
   ],
   "body": "Write a privacy policy and terms of service for this app, on pages like /privacy and /terms, that honestly name every outside service receiving our users' data and what each receives.\n\nFirst find out which services those are. Our tech stack notes, probably in a file like STACK.md or the README, may list them, but don't rely on that alone. Check the code and configuration for anything that sees personal data: hosting, analytics, email, payments, AI providers, and the rest. The classic failure is a copied template that promises things the app doesn't do, or leaves out a service that does see data.\n\nResearch what a small app like ours needs to cover, look at what pages exist, and keep the language plain. Before writing, show me the services and data you found and anything uncertain. Flag anything a lawyer should review instead of guessing.\n\nDraft the pages on a preview deployment, link them where people expect, and show me. Then note in the project's documentation that these pages must change whenever a service is added.",
   "primary": true
  },
  "api": {
   "id": "api",
   "title": "Add an API and MCP server",
   "stage": 8,
   "checks": [
    "LAUNCH-4"
   ],
   "body": "Give other software and AI agents a safe way to use this app: a few documented API routes, and a remote MCP server that agents like Codex or Claude can connect to.\n\nLook at what the app does and which routes already exist, and propose the few actions people would actually want. Hosting the MCP server inside the app, at a path like /api/mcp, is a good default on Vercel. The classic failure is an API that skips the checks the app's own pages make, so anyone can reach other people's data. Every route must check who is calling and what they may touch.\n\nResearch current best practice for how agents sign in, look at what the app has, and choose the simplest approach. Before building, show me the plan, what you rejected, and any risks.\n\nTry it on a preview deployment with test accounts. Then show me real calls and results, including refused ones, and record the API in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "domain": {
   "id": "domain",
   "title": "Connect a custom domain",
   "stage": 1,
   "checks": [
    "SHIP-4"
   ],
   "body": "Give this app its own domain, loading over HTTPS, with www redirecting to it.\n\nCheck first whether I already own a domain for this and where its DNS is managed. If not, Vercel Domains is a simple default. Suggest a few names that fit, with prices, wait for me to choose, and ask before spending any money. The classic failures are DNS changes that break existing email, a missing www redirect, and pointing the domain at the wrong project.\n\nResearch the current recommended setup, look at how the project is hosted, and choose the simplest approach. Domains and DNS are production settings, so before changing anything, show me exactly which records you'll add or change, what they replace, and how to undo it. If we'll send email from this domain, tell me what records that will need.\n\nThen show me the domain loading over HTTPS with a valid certificate, and www redirecting. Record the domain, registrar, and renewal date in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "firewall": {
   "id": "firewall",
   "title": "Set up the firewall",
   "stage": 7,
   "checks": [
    "PROTECT-1"
   ],
   "body": "Protect this app from bots and floods of requests: a rate limit on the API, and bot protection on sign-up and anything that costs us money per request, like AI calls, email, or checkout.\n\nVercel Firewall rate limits and Vercel BotID are good defaults on Vercel. If the app already has protection, keep what works and explain it. The classic failures are limits so tight that a real person moving quickly gets blocked, and protecting a page while the API behind it stays open.\n\nResearch current best practice, find which routes actually cost money or create accounts, and choose the simplest setup. Firewall rules affect production, so show me the plan first: which routes, what limits, what you rejected, and how to roll back. Where possible, try rules in a log-only mode or on a preview before enforcing them.\n\nThen show me excess requests blocked, normal use still working, and bot protection on the right routes. Record the rules and why in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "protect-internet": {
   "id": "protect-internet",
   "title": "Get ready for real users",
   "stage": 7,
   "checks": [
    "PROTECT-1",
    "PROTECT-2",
    "PROTECT-3",
    "PROTECT-4"
   ],
   "body": "Get this app ready for strangers, bots, and bad days: protection from abuse, dependencies kept up to date, backups we can restore, and honest privacy and terms pages.\n\nEarlier steps may have done some of this, so check first. Good defaults are Vercel Firewall rate limits and BotID on sign-up and anything that costs money per request, Dependabot for weekly update pull requests, database restore points (from Neon, if that's ours), and legal pages naming every service that receives user data. The classic failures are blocking real people, and backups nobody has ever restored.\n\nResearch current best practice and look at what the project has. Before changing production settings like firewall rules or the database plan, show me the plan, cost, and risks. Try rules in a safe mode first, and rehearse a restore into a separate test project, never over real data. Flag legal wording a lawyer should review.\n\nShow me evidence for each part, and record what you chose in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": false
  },
  "deploy": {
   "id": "deploy",
   "title": "Deploy your app on every push",
   "stage": 1,
   "checks": [
    "SHIP-3"
   ],
   "body": "Put this app online so every push to the main branch updates production and every other branch gets its own preview link.\n\nVercel connected to a private GitHub repository is our default. Check where things stand: the code may or may not be on GitHub, and the project may already be hosted somewhere. If something is missing, tell me rather than inventing a setup. Before anything is pushed, make sure environment files and other secrets are ignored and never committed. The classic failure is a build that passes on my laptop but fails online because of a missing environment variable or a different version. Fix the cause; never turn checks off.\n\nResearch the recommended setup for this framework and choose the simplest approach. Before linking or changing anything, show me your plan, what you rejected, and any risks.\n\nThen show me the production URL, a preview URL from a branch, and a small change going live after a push. Record the services in our tech stack notes, probably in a file like STACK.md or the README, creating them only if none exist.",
   "primary": true
  },
  "launch-audit": {
   "id": "launch-audit",
   "title": "Run a professional launch audit",
   "stage": 8,
   "checks": [
    "LAUNCH-6"
   ],
   "body": "Before we launch this app to real customers, give it the review a professional engineering team would: is it actually ready, and what would stop it?\n\nUse the Software Engineering Go Launch Checklist at github.com/fractal-nyc/software-engineering-go-launch-checklist. Read the whole thing first and follow its instructions for the reviewing agent. Start by establishing the scope with me: who this launch is for, the journeys that must work, the data we hold, and the services we depend on. Our tech stack notes, probably in a file like STACK.md or the README, may help, but ask about anything you can't find. Many checks won't apply to an app our size; mark those not applicable with a reason rather than inventing work. The classic failure is a confident audit built on claims: a test file existing, or a vendor's promise, is not evidence that the deployed app works.\n\nThis is a review only. Don't change code, deploy, touch production, or send anything to real people. Check behavior on a preview deployment or with test accounts.\n\nDeliver the report the checklist asks for, with evidence for every result, the blockers, and the decisions only I can make. Then help me turn the blockers into a short, ordered fix list, and save the report with the project's documentation.",
   "primary": false
  },
  "secrets": {
   "id": "secrets",
   "title": "Move your secrets out of the code",
   "stage": 1,
   "checks": [
    "SHIP-2"
   ],
   "body": "Find every key, password, and token this app uses and move them out of the code into the hosting platform's environment settings, marked secret, with separate values for development, previews, and production.\n\nSearch the code, config files, and commit history, since a deleted key still lives in the history. Vercel environment variables are our default. If the app also runs elsewhere, like another host, a script on my laptop, or GitHub Actions, tell me and suggest a shared secrets manager such as Infisical. Use test keys everywhere but production. Local development should read from a local environment file that git ignores. The hard lines: never print a secret's value, and never put a secret key in browser code.\n\nResearch current best practice and choose the simplest approach. Before moving anything, show me the plan and every key that was ever committed, so I can replace them.\n\nCheck that the app still works locally and on a preview deployment afterward. Record each variable's name and purpose, never its value, in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "people-audit": {
   "id": "people-audit",
   "title": "Run a security audit",
   "stage": 2,
   "checks": [
    "PEOPLE-2",
    "PEOPLE-5"
   ],
   "body": "Act as a security reviewer for this app. Try to reach data you shouldn't be able to see or change, then fix what you find.\n\nLook at what the project actually uses, whether Neon reached from server code or something else. The classic holes are queries that skip the owner check, tables with missing or wide-open access rules, secret keys in browser code or commit history, server routes that trust a user ID sent from the browser, private data hidden in the page instead of withheld by the server, and file storage anyone can list or overwrite. Uploads need the same owner-only rules as the data.\n\nResearch current attack patterns for this stack and decide how to probe it. Work only against a preview deployment with test accounts you create, never production or real users' data.\n\nReport before you fix: for each problem, what an attacker could do, how you proved it, and how urgent it is, in plain words. After fixing, show me the attacks now fail, keep them as automated checks, and record the findings in the project's documentation.",
   "primary": false
  },
  "people-auth": {
   "id": "people-auth",
   "title": "Add sign-up and login",
   "stage": 2,
   "checks": [
    "PEOPLE-3",
    "PEOPLE-4"
   ],
   "body": "Let people sign up, log in, log out, and reset their password, with every private page and route checking on the server who is signed in.\n\nNeon Auth, added with Neon through the Vercel Marketplace, is our default, with its emails sent from our own domain through Resend, not the provider's shared mail server. Check what exists, including whether our domain is verified in Resend, and don't write your own password or session code. The classic failures are hiding private content behind a button instead of checking the session on the server, redirects that break on previews, and emails landing in spam.\n\nResearch current best practice and choose the simplest approach; email magic links are often a good start. Before building, show me the plan, what you rejected, and any risks. DNS is a production setting, so show me any records you'd add before adding them. Match the app's style.\n\nTry the whole flow on a preview deployment with test addresses. Then show me evidence, including a private page refusing a logged-out visitor, and record what you chose in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "people-model": {
   "id": "people-model",
   "title": "Set up a live database",
   "stage": 2,
   "checks": [
    "PEOPLE-1",
    "PEOPLE-2"
   ],
   "body": "Move this app's data into a real database where each person can only see and change their own data, and public things stay public.\n\nFind all the data the app keeps today, including browser storage and hard-coded data. Neon Postgres, added through the Vercel Marketplace, is our default, with tables created by migration files in the repository rather than dashboard clicks, and every query run from server code and scoped to the signed-in person. The hard lines: never put the database connection string or any secret key in browser code, and never let one person's request read another person's rows, even briefly.\n\nResearch current best practice, look at what exists, and choose the simplest design that fits. Before creating anything, show me each table in one plain sentence, the access rules in plain English, what you rejected, and any risks.\n\nBuild against a development or preview database first, not production. Then show me that one test user cannot read or change another's data, and fix any warnings from the database's security checks. Record the design in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "money-checkout": {
   "id": "money-checkout",
   "title": "Set up payments",
   "stage": 3,
   "checks": [
    "MONEY-1",
    "MONEY-2"
   ],
   "body": "Let people pay through Stripe Checkout, and make sure our database always agrees with Stripe about who has paid.\n\nWork out what the app sells, asking me if it's unclear. Keep prices in Stripe, not hard-coded, and use Stripe's hosted checkout rather than building a card form. The classic failure is recording a payment when someone reaches the thank-you page. Record it only when Stripe's signed webhook arrives, check the signature, and make sure a repeated webhook never records two payments. Handle refunds, declines, and abandoned checkouts.\n\nResearch current best practice, look at what the app and database have, and choose the simplest approach. Before building, show me the plan, what you rejected, and any risks.\n\nWork only in Stripe test mode on a preview, and never switch to live payments yourself. Show me what the database records for a payment, a decline, a repeated webhook, and a refund. List what I must do to go live, and record the setup in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "email": {
   "id": "email",
   "title": "Send email from your domain",
   "stage": 2,
   "checks": [
    "PEOPLE-4"
   ],
   "body": "Make it possible for this app to send email from our own domain, so sign-up, login, and other messages reach real inboxes instead of spam.\n\nResend is our default. Check first whether we have a domain, where its DNS is managed, and whether email already runs on it, like a company inbox. The classic failures are records that clash with existing email, and login emails still coming from the auth provider's shared server. If the app uses Neon Auth, its emails should go through Resend too.\n\nResearch the current recommended setup, look at what exists, and choose the simplest approach. DNS is a production setting, so before adding or changing records, show me exactly which ones and what they affect. Tell me when a step needs me, like creating an account.\n\nThen show me a test email from our domain arriving in a real inbox, not spam, and the domain verified. Record the setup in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "github": {
   "id": "github",
   "title": "Save your code to GitHub",
   "stage": 1,
   "checks": [
    "SHIP-1"
   ],
   "body": "Put this app's code in a private GitHub repository, so it's backed up and can connect to hosting and automated checks.\n\nCheck what exists first: the folder may already be a git repository, with or without a remote. The classic failure is pushing secrets, like an environment file full of API keys, into the history, where they stay even after deletion. Before the first push, make sure environment files and other private or bulky files are ignored, and scan what would be committed for keys and passwords. If any are already in the history, tell me.\n\nResearch current best practice and choose the simplest approach; GitHub's own command-line tool is a good default. Before creating or pushing anything, show me the plan and any risks, and tell me when a step needs me, like signing in.\n\nThen show me the private repository with the code in it, and evidence that no environment files were pushed. Record the repository in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "steady-lock": {
   "id": "steady-lock",
   "title": "Set up automated tests",
   "stage": 5,
   "checks": [
    "STEADY-1",
    "STEADY-2",
    "STEADY-4"
   ],
   "body": "Make sure no future change can quietly break this app's core flow.\n\nWork out the core flow from the code, like sign up, do the main thing, pay, and ask me if it's unclear. A good default is one end-to-end Playwright test that clicks and types like a person, run by GitHub Actions on every push against the preview deployment, with merging into main blocked when it fails. Some of this may exist, so check. The classic failures are tests that skip steps by editing the database, tests weakened until they pass, and flaky runs everyone ignores. If the app is wrong, fix the app and tell me.\n\nResearch current best practice, look at the project, and choose the simplest approach. Before building, show me the plan, what you rejected, and the exact repository settings you'll change.\n\nThen show me evidence it works, like a deliberately broken change blocked on a branch. Make sure the project has a README for people, an AGENTS.md for agents, and tech stack notes listing every service, adding only what's missing.",
   "primary": true
  },
  "preview": {
   "id": "preview",
   "title": "Set up preview links",
   "stage": 5,
   "checks": [
    "STEADY-3"
   ],
   "body": "Make sure every change to this app can be tried on its own preview link before it reaches real users, and that previews use test keys and test data, never production.\n\nVercel creates a preview for each branch once the project is connected to GitHub, but check where things stand: previews may be off, or quietly using live keys or the production database. That's the classic failure, a preview that charges real cards, emails real users, or writes real data. Separate settings per environment, a test database, and Stripe test mode are the usual answers. Login redirects and webhooks often need extra setup on previews.\n\nResearch current best practice, look at what the project has, and choose the simplest setup. Before changing environment settings, show me what each environment uses today, what you'd change, and any risks.\n\nThen show me a preview link from a branch, and evidence that it uses test services and doesn't touch production. Record how previews are configured in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "watch-mornings": {
   "id": "watch-mornings",
   "title": "Set up analytics and error tracking",
   "stage": 6,
   "checks": [
    "WATCH-1",
    "WATCH-2",
    "WATCH-4"
   ],
   "body": "Set this app up so every morning I can answer three questions: how many people finished the core flow, where did the others give up, and what broke?\n\nPostHog is our default for all of this. Check what the app already tracks. A good setup has five to eight named events for the core flow, one funnel dashboard labeled in plain words, errors that point to real lines of code, and a one-question survey after someone finishes. The hard lines: never record what people type into password or payment fields, and identify people by account ID, not email. The classic failure is tracking everything and learning nothing.\n\nResearch current best practice and choose the simplest approach. Before adding anything, show me the event list and any risks.\n\nTry it on a preview with a test account first. Then show me events arriving, the funnel, and a test error pointing to the right file. Record the setup and the data it collects in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "logs": {
   "id": "logs",
   "title": "Set up logging",
   "stage": 6,
   "checks": [
    "WATCH-5"
   ],
   "body": "Make sure you, the agent, can read this app's recent server logs and use them to work out what caused an error.\n\nCheck where the app runs and how its logs can be reached. On Vercel, recent logs are available through its command line and agent tools, but our plan may keep them only briefly, so tell me if we need longer. The classic failures are logs that say nothing useful, logs that leak secrets or personal data, and errors swallowed silently. Where it helps, add clear log messages at important server steps, never recording secrets or private content.\n\nResearch current best practice for this hosting, look at what the project has, and choose the simplest approach. Before changing logging code, show me the plan and any risks.\n\nThen show me evidence: pull recent logs, find a real error or one you trigger on a preview deployment, and explain its cause. Record how to read the logs in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "uptime": {
   "id": "uptime",
   "title": "Set up uptime checks",
   "stage": 6,
   "checks": [
    "WATCH-3"
   ],
   "body": "Set up checks that watch this app around the clock and email me when it goes down or its core flow stops working.\n\nCheckly is our default because it can check both that the homepage loads and that a real browser gets through the core flow. The project may already have monitoring, or end-to-end tests the flow check can reuse. The classic failures are checks that only ping the homepage while the core flow is broken, and alerts so noisy everyone ignores them. The flow check should use a dedicated test account and never touch real users, money, or data.\n\nResearch current best practice, look at the project, and choose the simplest approach. Before setting anything up, show me what will be checked, how often, who gets alerted, the cost, and what you rejected.\n\nTry the checks against a preview deployment first if you can. Then show me them passing and a deliberate failure sending an alert, and record them in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "ai-gateway": {
   "id": "ai-gateway",
   "title": "Route AI calls through AI Gateway",
   "stage": 4,
   "checks": [
    "AI-1"
   ],
   "body": "Route every AI model call in this app through one gateway, so we can see and limit what AI costs us.\n\nVercel AI Gateway with the AI SDK is our default. Find every place the app calls an AI model; some may use a provider's library directly. The hard line: never put a model provider's key in browser code. The classic failures are one forgotten call that keeps billing directly, and no budget, so a bug runs up the bill. Set a project budget as a safety net, and tag calls with the user so we can see usage per person.\n\nResearch current best practice and models, look at what the app has, and choose the simplest approach. Before switching anything, show me each AI call, the model you'd use, its rough cost, and what happens when the budget runs out.\n\nTry it on a preview deployment first. Then show me calls arriving in the gateway's usage view with their cost, and record the gateway, models, and budget in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "ai-allowance": {
   "id": "ai-allowance",
   "title": "Give each user an AI allowance",
   "stage": 4,
   "checks": [
    "AI-2"
   ],
   "body": "Give every user an AI allowance, so one person, or one bug, can't run up our bill.\n\nFind the app's AI features first; if there are none, stop. The usual approach is to record each AI call's user, feature, model, and cost in the app's database (Neon, if that's ours), and check the remaining allowance on the server before every call. When someone runs out, skip the call and show a friendly message about how to get more. Every AI route needs login and a rate limit. The classic failure is a limit that only exists in the browser, which doesn't count.\n\nAsk me how big the free allowance should be. Research current best practice and choose the simplest approach. Before building, show me the plan, what you rejected, and any risks.\n\nTry it on a preview with test users. Then show me a test user using up a tiny allowance and the next request refused without calling the AI. Record the design in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "ai-billing": {
   "id": "ai-billing",
   "title": "Charge for AI with Stripe",
   "stage": 4,
   "checks": [
    "AI-3"
   ],
   "body": "Let people pay for more AI usage with Stripe, so what they pay comfortably covers what the AI costs us.\n\nCheck whether the app already has Stripe checkout and a per-user AI allowance; if there's no allowance yet, tell me first. Ask me which fits better, a monthly subscription that includes an allowance or prepaid credit packs. The classic failures are adding credit when someone reaches the thank-you page instead of when Stripe's signed webhook confirms payment, and a repeated webhook adding credit twice. Keep prices in Stripe, and show people their remaining allowance and a way to get more.\n\nResearch current best practice, look at what the app has, and choose the simplest approach. Before building, show me the plan, the pricing math against our AI costs, and any risks.\n\nWork only in Stripe test mode on a preview deployment. Then show me a test purchase raising a test user's allowance and use bringing it down. Record the setup in our tech stack notes, probably in a file like STACK.md or the README.",
   "primary": true
  },
  "ai-siwc": {
   "id": "ai-siwc",
   "title": "Let users sign in with ChatGPT (beta)",
   "stage": 4,
   "checks": [
    "AI-4"
   ],
   "body": "Let people who pay for ChatGPT Plus or Pro connect their plan, so their AI use here draws on it, not our budget. OpenAI's Sign in with ChatGPT is in beta, so check first whether we can use it.\n\nRead OpenAI's current documentation, including the beta's limits: hosted or paid apps may need OpenAI's approval; if we lack it, stop, tell me how to apply, and change nothing. If we can, the usual shape is a Continue with ChatGPT option beside our normal login, with plan usage as a separate permission explained plainly. Tokens stay encrypted on our server, never in the browser, and people can disconnect anytime. Everyone else keeps our gateway and allowance. When someone hits their plan's limit, tell them, and use our allowance only if they choose. The hard lines: use only OpenAI's official sign-in, never ask anyone for passwords or tokens, never copy tokens out of another app.\n\nBefore building, show me the plan and risks. Try it on a preview with a test ChatGPT account, show me AI use drawing on that account rather than our gateway, and record the setup in the project's documentation.",
   "primary": false
  }
 }
}