CLAUDE.md
Context file for AI agents working in this repository. Read this first. This is a sourdough starter — fed as the project evolves, not set-and-forget. Last meaningful update: 2026-05-28.
What this is
{Your project name} — {one-sentence technical identity}. {Audience / what it does for them in one more sentence}.
Three deployable surfaces:
app/— Next.js App Router (marketing + dashboard), deployed on Vercelsupabase/— schema migrations + RLS policies, applied via Supabase CLIworkers/— Cloudflare Workers for tool execution (if applicable; delete if not)
Stack
| Layer | Tech | Notes |
|---|---|---|
| Framework | Next.js 15+ App Router + TypeScript | Strict TS, no JS files. Server components by default. |
| UI primitives | {shadcn/ui? Raw Tailwind? Custom CSS classes?} | Be specific. |
| Fonts | {Inter? JetBrains Mono?} | Loaded via next/font/google in lib/fonts.ts |
| Database + Auth | Supabase | RLS on every table. No exceptions. |
| Runtime secrets | Supabase Vault | Used for any secret that crosses a deploy. |
| Dev-time secrets | .env.local (gitignored) |
Never in committed files. |
| {Resend? Postmark?} | Configured in lib/email/* |
|
| Errors | {Sentry? Custom?} | All three surfaces if applicable. |
| Payments | {Stripe? Lemon Squeezy?} | Phase N if not yet wired. |
Standing rules (apply always)
These are non-negotiable across the codebase. The AI should treat them as constraints, not suggestions.
- No secret in any file that touches git.
.env.localis gitignored; Vault for runtime secrets. - Supabase RLS on every table. No exceptions. Even tables not yet user-facing.
- No
anytypes. Useunknownand narrow, or define the type properly. - Server components by default. Add
'use client'only when you need state, effects, or browser APIs. - {Project-specific copy rule} — e.g. "Lowercase headings in marketing copy" or "No emojis in code or copy unless I explicitly asked".
- Don't add comments that restate what the code does. Only comment WHY when the why isn't obvious.
- No new abstractions on first use. Three similar lines beats a premature helper.
- Mobile-first responsive. Every page works at 360px width.
- Test in the browser before claiming a UI task is done. Type checks aren't behaviour checks.
Folder structure
{project}/
├── app/ ← Next.js App Router
│ ├── (marketing)/ ← Public pages
│ ├── (dashboard)/ ← Authed pages
│ ├── api/ ← Route handlers
│ ├── layout.tsx ← Root layout
│ └── globals.css
├── components/
│ ├── ui/ ← Primitives (shadcn or your own)
│ └── {domain}/ ← Domain components grouped by area
├── lib/
│ ├── supabase/ ← Server + client supabase factories
│ ├── {domain}.ts ← Domain-specific utilities
│ └── utils.ts
├── supabase/
│ └── migrations/ ← SQL migrations, numeric-prefix order
└── public/
Next.js + Supabase gotchas (the ones that bite)
These are real failure modes the AI introduces by default. Pre-record them so they don't keep happening.
NEXT_PUBLIC_prefix on a Supabase service-role key exposes it to the browser. The other env vars are public; the pattern misleads. Service-role keys belong server-side only, in routes / server components / route handlers — never in client code.createClient()from@supabase/ssrvs@supabase/supabase-jsmatters. Server components need the server-side factory (passes cookies); client components need the client factory. Mixing them silently breaks auth.- RLS policies need
WITH CHECKon UPDATE/INSERT.USINGonly covers SELECT-shaped reads. The row-reassignment bug ships when WITH CHECK is missing — the AI scaffolds RLS as a checkbox and forgets WITH CHECK. auth.role() = 'authenticated'is not the same asauth.uid() = user_id. The first lets any logged-in user through; only the second scopes to the owner.- Server Actions need explicit
'use server'. The AI sometimes forgets the directive; the action then runs client-side and exposes whatever secrets it touched. - Cookie-based auth and revalidation cache interact badly. When you call
revalidatePath('/some-route')after an auth-state change, forcecache: 'no-store'on any fetch that reads the cookie session, or you'll serve stale auth state.
Naming + conventions
| Thing | Convention | Example |
|---|---|---|
| Component files | kebab-case .tsx |
tool-card.tsx, nav.tsx |
| Component names | PascalCase | ToolCard, Nav |
| SQL tables | snake_case, plural | profiles, api_keys, tool_runs |
| Env vars | UPPER_SNAKE_CASE | STRIPE_SECRET_KEY, NEXT_PUBLIC_SUPABASE_URL |
| Migrations | NNN_kebab-name.sql |
001_initial_schema.sql |
| Tool MCP names (if applicable) | {project}__action_object |
fizzgig__rls_checker |
Don't do this
- Don't introduce
lib/services/orlib/repositories/layers. Premature abstraction. - Don't disable RLS as a workaround. Fix the policy.
- Don't trust input from the client because it was validated client-side. Server validates separately.
- Don't recreate the marketing pages — they're done. Edit existing files.
- Don't commit secrets, even in test fixtures. Use
.env.local(gitignored) or Vault.
Where to look when
| If you need to… | Go to |
|---|---|
| Understand WHY a decision was made | docs/adr/ (architecture decision records) |
| Understand WHAT we're building this week | {wherever your sprint plan lives} |
| Understand the schema | latest migration in supabase/migrations/ |
| Understand the design system | app/globals.css + components/ui/ |
| Understand security posture | [starters/security-posture](https://fizzgig.ai/starters/security-posture) |
Feed log
Notable updates to this CLAUDE.md. Most recent first. Each entry: what changed, why.
- 2026-05-28 — Initial fork from fizzgig.ai/starters/claude-md-nextjs-supabase.
This is a sourdough starter — fed as the project evolves. If you're an agent reading this and the project has clearly moved past what this file describes, flag it (the file is stale and needs feeding).