Los 14 documentos que tu IA necesita para programar tu app
Que la IA escriba código es lo fácil. Lo difícil es explicarle cómo tiene que funcionar tu producto, cómo se construye y cómo saber que funciona. Estos son los documentos que lo resuelven, con un ejemplo completo de cada uno.
Los prompts para crear la carpeta docs/ entera y mantenerla al día.
Si programas con IA, conseguir que escriba código es lo fácil. Lo difícil es darle el contexto suficiente para que entienda cómo tiene que funcionar tu producto, cómo se construye y cómo saber que de verdad funciona. Lo que no le cuentas, se lo inventa.
Para eso sirven estos documentos. Son catorce, cada uno responde una pregunta que no responde ningún otro, y todos cuentan el mismo proyecto inventado para que veas cómo encajan: Turnio, una app de reservas para peluquerías.
Cómo se organizan
AGENTS.md va en la raíz, porque es lo que los agentes buscan al abrir un proyecto. El resto vive en docs/. Van en tres grupos: qué construyes, cómo se construye y cómo se trabaja.
turnio/
├── AGENTS.md ← el agente empieza aquí
├── CLAUDE.md ← una línea: @AGENTS.md
└── docs/
├── PRD.md qué y para quién
├── APP_FLOW.md por dónde se mueve cada usuario
├── DESIGN_SYSTEM.md cómo se ve
├── TRD.md cómo funciona por dentro
├── DATABASE.md dónde viven los datos
├── API.md cómo se hablan las piezas
├── SECURITY.md qué no se puede romper
├── CODE_STYLE.md cómo se escribe
├── IMPLEMENTATION_PLAN.md en qué orden
├── TESTING.md cómo sabes que funciona
├── DEPLOYMENT.md cómo llega a producción
├── DECISIONS.md por qué es así
└── PROGRESS.md dónde lo dejamos| Documento | Responde a | Se escribe |
|---|---|---|
| PRD.md | ¿Qué construimos y para quién? | Antes de nada |
| APP_FLOW.md | ¿Qué hace el usuario, pantalla a pantalla? | Después del PRD |
| DESIGN_SYSTEM.md | ¿Cómo se ve y se mueve? | Antes de la primera pantalla |
| TRD.md | ¿Con qué y cómo se construye? | Antes de la primera línea |
| DATABASE.md | ¿Dónde viven los datos y quién los ve? | Antes de la primera tabla |
| API.md | ¿Cómo se piden y se devuelven? | Antes del primer endpoint |
| SECURITY.md | ¿Qué no se puede romper nunca? | Con el TRD |
| CODE_STYLE.md | ¿Cómo se escribe aquí? | Con el primer código |
| AGENTS.md | ¿Por dónde empiezo y qué no toco? | Con el primer código |
| IMPLEMENTATION_PLAN.md | ¿En qué orden? | Antes de la primera tarea |
| TESTING.md | ¿Cómo sé que funciona? | Con la primera tarea |
| DEPLOYMENT.md | ¿Cómo llega a producción? | Antes del primer despliegue |
| DECISIONS.md | ¿Por qué es así? | Cada vez que se decide algo |
| PROGRESS.md | ¿Dónde lo dejamos? | Al cerrar cada sesión |
No hace falta tener los catorce el primer día. Una landing se apaña con PRD, DESIGN_SYSTEM y AGENTS. Un MVP suma APP_FLOW, TRD, DATABASE, IMPLEMENTATION_PLAN y PROGRESS. Los demás llegan cuando hay usuarios y dinero de por medio. Lo importante es que la IA tenga un contexto claro y ordenado en lugar de adivinar.
En cada documento del árbol de ficheros puedes pulsar cualquier otro para saltar a él.
1. PRD.md: qué construyes y para quién
Es el documento del que salen todos los demás. Le dice a la IA qué problema resuelve el producto, para quién, qué entra en la primera versión y, casi más importante, qué no entra.
Documento de producto
Qué construimos, para quién y cómo sabremos que ha funcionado. El resto de documentos cuelga de este.
01Resumen
- Producto
- Turnio
- En una frase
- Reservas online para peluquerías, sin llamadas ni WhatsApp.
- Qué es
- Una web donde el cliente reserva cita en 30 segundos y el negocio ve su agenda llena sin contestar un solo mensaje.
- Versión
- MVP 1.0
- Estado
- En construcción · lanzamiento previsto el 15 de noviembre
02Problema
Las peluquerías pequeñas gestionan las citas por WhatsApp y por teléfono. Pierden unas 6 horas a la semana contestando mensajes, y el 15 % de las citas acaban en un «no vino» sin avisar.
03Objetivo
Que un negocio reciba su primera reserva online en menos de 10 minutos desde que se registra.
04Usuarios
- Dueña del negocio: configura servicios, horarios y cobra la señal.
- Empleado: ve su agenda del día y marca quién ha venido.
- Cliente final: reserva desde el móvil, sin crear cuenta.
05Necesidad principal
«Quiero que mis clientas reserven solas y que, si no vienen, por lo menos me quede la señal.»
Marta, peluquería de 3 sillas en Sabadell
06Funcionalidades del MVP
- Página públicaturnio.app/tu-negocio
- Reserva onlineServicio, hueco y listo
- AgendaDía y semana por empleado
- SeñalCobro con tarjeta al reservar
- RecordatoriosEmail 24 h antes
- MétricasReservas y no-shows
07Prioridades
MoSCoW: lo que entra sí o sí, lo que entra si da tiempo y lo que no entra.
| Prioridad | Qué | Por qué |
|---|---|---|
| Imprescindible | Reserva, agenda, horarios, señal | Sin esto no hay producto |
| Importante | Recordatorios, cancelar y cambiar cita | Es lo que baja los no-shows |
| Deseable | Varios empleados, métricas | Lo pide 1 de cada 3 negocios |
| Fuera | App nativa, fidelización, TPV | Después de validar el MVP |
08Fuera de alcance
Lo que la IA no debe construir aunque parezca buena idea.
- App nativa para iOS o Android
- Varios idiomas
- Sincronización con Google Calendar
- Venta de productos o bonos
- Chat entre cliente y negocio
09Métricas de éxito
- 50 negocios activos a los 3 meses
- El 40 % de sus citas llegan por Turnio
- No-shows por debajo del 5 %
- Primera reserva en menos de 10 minutos
10Supuestos y riesgos
- Supuesto: la clienta acepta pagar 5 € de señal
- Supuesto: el negocio comparte el enlace en Instagram
- Riesgo: Booksy y Fresha ya existen; competimos en precio y sencillez
- Riesgo: la dueña no tiene tiempo para configurar nada
El error más común es no escribir «Fuera de alcance». Sin esa lista, la IA añade lo que le parece buena idea, y en una semana tienes un chat, un programa de puntos y ninguna reserva funcionando.
2. APP_FLOW.md: por dónde se mueve cada usuario
Convierte el PRD en recorridos: qué pantallas hay, en qué orden se pasa por ellas y qué ve el usuario en cada una. Evita que la IA se invente pantallas o se deje el camino de vuelta.
Flujo de la app
Cómo se mueve cada usuario por el producto: por qué pantallas pasa, qué ve en cada una y qué ocurre cuando algo sale mal.
01Resumen
- Plataforma
- Web responsive, pensada primero para móvil
- Roles
- Cliente (sin cuenta) · Dueña · Empleado
- Entrada del cliente
- El enlace del negocio en su bio de Instagram
- Entrada del negocio
- turnio.app → Crear cuenta
02Flujo del cliente
- 1NegocioAbre la página desde Instagram
- 2ServicioElige corte, color o peinado
- 3HuecoDía y hora libres
- 4DatosNombre, móvil y señal de 5 €
- 5ConfirmadaEmail con la cita
03Pantallas clave
- Peluquería MartaSabadell · ★ 4,9Corte mujer · 25 €Color · 45 €1NegocioFoto, dirección y servicios
- ¿Qué te hacemos?Corte mujer · 45 minCorte y color · 2 hPeinado · 30 min2ServicioPrecio y duración a la vista
- Martes 1410:0011:3017:003HuecoSolo se ve lo que está libre
- Tus datosNombreMóvilTarjeta · señal 5 €4DatosSin cuenta ni contraseña
- ¡Hecho, Laura!Martes 14 · 11:30Corte mujer con Marta5ConfirmadaCambiar o cancelar, a un clic
04Flujo del negocio
- 1RegistroEmail o Google
- 2ServiciosNombre, precio y duración
- 3HorarioDías, horas y descansos
- 4ComparteEnlace para la bio
- 5AgendaLas reservas entran solas
05Mapa de pantallas
Cada ruta de la app y lo que hay en ella.
/ landing /[negocio] página pública /[negocio]/reservar servicio y hueco /reserva/[id] cambiar o cancelar /entrar /registro /panel agenda de hoy /panel/servicios /panel/horario /panel/clientes /panel/ajustes pagos y equipo
06Estados de cada pantalla
Vacío, cargando y error, escritos antes de programarlos.
| Pantalla | Vacío | Error |
|---|---|---|
| Agenda | «Comparte tu enlace para recibir la primera» | Reintentar sin perder el día elegido |
| Huecos | «No quedan huecos esta semana» + la siguiente | Mensaje y volver al servicio |
| Clientes | «Aparecerán con su primera reserva» | Reintentar |
07Caminos alternativos
- Otro cliente coge el hueco mientras pagas: vuelves al calendario con un aviso
- El pago falla: el hueco queda retenido 10 minutos
- Cancelas con menos de 24 h: pierdes la señal y se te dice antes
- El negocio cierra un día con citas: se avisa a cada cliente
08Avisos que salen
| Cuándo | A quién | Canal |
|---|---|---|
| Reserva hecha | Cliente y negocio | |
| 24 h antes | Cliente | |
| Cancelación | Negocio | Email y panel |
| No-show | Negocio | Panel |
Lo que casi nadie escribe son los estados vacío y error y los caminos alternativos. Son justo lo que la IA no se imagina sola, y lo primero que se encuentra un usuario de verdad.
3. DESIGN_SYSTEM.md: cómo se ve y cómo se mueve
Colores, tipografía, espacios, componentes y movimiento, con valores exactos. Con él, cada pantalla nueva se parece a las anteriores; sin él, cada una estrena un gris y un radio distintos.
Sistema de diseño
Los colores, tipos, espacios, componentes y movimientos que existen. Si no está aquí, la IA no se lo inventa.
01Identidad
- Personalidad
- Cercana, clara y tranquila
- Se parece a
- Una agenda de papel bien hecha
- Nunca
- Degradados morados, emojis en la interfaz, sombras duras
02Colores
Solo estos. Nada de hexadecimales sueltos en el código.
- Tinta#16151A
- Coral#FF6B4A
- Coral suave#FFE8E2
- Fondo#FAFAF7
- Borde#E7E5E0
- Éxito#2F9E55
- Aviso#E6A100
- Error#D93C3C
03Tipografía
Inter en toda la app. Nada por debajo de 12 px.
| Estilo | Tamaño | Línea | Peso |
|---|---|---|---|
| Título 1 | 40 | 44 | 700 |
| Título 2 | 28 | 34 | 600 |
| Título 3 | 20 | 28 | 600 |
| Cuerpo | 16 | 24 | 400 |
| Pequeño | 14 | 20 | 400 |
| Etiqueta | 12 | 16 | 500 |
04Espaciado
Escala de 4 px. Si un hueco no está aquí, está mal.
- 4
- 8
- 12
- 16
- 24
- 32
- 48
05Componentes
Sale de shadcn/ui y se adapta aquí, no en cada pantalla.
| Componente | Variantes | Regla |
|---|---|---|
| Botón | Primario, secundario, fantasma, peligro | Un solo primario por pantalla |
| Campo | Normal, error, deshabilitado | Etiqueta siempre visible, nunca solo placeholder |
| Selector de hueco | Libre, elegido, ocupado | El ocupado no se enseña, no se tacha |
| Tarjeta de cita | Pendiente, confirmada, no vino | El estado con texto, no solo con color |
| Modal | Confirmar, formulario | Solo para lo que no se puede deshacer |
| Aviso | Éxito, error | Arriba en escritorio, abajo en móvil, 4 s |
06Radios y sombras
| Token | Valor | Dónde |
|---|---|---|
| radius-sm | 6 px | Campos, etiquetas |
| radius-md | 10 px | Botones, tarjetas |
| radius-lg | 16 px | Modales, hojas |
| shadow-card | 0 1px 2px / 6 % | Tarjetas |
| shadow-pop | 0 12px 32px / 12 % | Menús y modales |
07Movimiento
- Rápido · 150 ms
- Hover, pulsar, cambiar un icono
- Normal · 250 ms
- Menús, pestañas, abrir un modal
- Lento · 400 ms
- Paneles y entrar en una pantalla
- Curva
- cubic-bezier(.22, 1, .36, 1), sin rebotes
- Siempre
- Respetar prefers-reduced-motion
08Accesibilidad
- Contraste mínimo 4,5:1 en texto
- Foco visible en todo lo que se pulsa
- Zonas táctiles de 44 × 44 px
- Cada campo con su etiqueta
- Nada se comunica solo con color
09Iconos y pantallas
- Iconos
- Lucide, 20 px, trazo 1,5
- Móvil
- Hasta 640 px · se diseña primero
- Tableta
- 640 – 1024 px
- Escritorio
- Desde 1024 px · contenido a 1200 px máximo
No basta con la paleta: escribe la regla de cada componente («un solo botón primario por pantalla») y cuánto duran las animaciones. Y si además quieres que no parezca hecha con IA, aquí tienes seis cosas que lo arreglan.
4. TRD.md: cómo funciona por dentro
El documento técnico: stack, arquitectura, estructura de carpetas y requisitos con cifras. Si el PRD dice qué, el TRD dice con qué y cómo.
Documento técnico
Cómo se construye: el stack, cómo encajan las piezas, los servicios externos y los límites técnicos que no se negocian.
01Stack
- Next.js 15Web y API
- TypeScriptEstricto
- TailwindCon shadcn/ui
- SupabasePostgres, login y ficheros
- StripeSeñal y suscripción
- ResendCorreos
- VercelHosting y cron
- PostHogAnalítica
- SentryErrores
02Arquitectura
Un solo proyecto de Next.js. Sin servidor aparte hasta que haga falta.
- 1NavegadorMóvil o escritorio
- 2Next.jsPáginas en servidor y rutas /api
- 3SupabaseDatos con RLS y login
- 4StripePago y webhook de vuelta
- 5ResendConfirmación y recordatorio
03Estructura
turnio/ ├─ app/ │ ├─ (publico)/[negocio]/ # página y reserva │ ├─ (panel)/panel/ # zona privada │ └─ api/v1/ # rutas de la API ├─ components/ │ ├─ ui/ # shadcn adaptado │ └─ booking/ # piezas de la reserva ├─ lib/ # lógica, sin React ├─ supabase/migrations/ └─ docs/
04Una reserva, por dentro
- La página pide los huecos libres a /api/v1/slots
- Al elegir, el hueco se retiene 10 minutos en la base de datos
- Stripe Checkout cobra la señal
- El webhook de Stripe confirma la reserva: nunca la página
- Resend manda el correo y un cron diario, los recordatorios
05Requisitos no funcionales
| Requisito | Objetivo | Cómo se mide |
|---|---|---|
| Carga en móvil | LCP por debajo de 2,5 s en 4G | Vercel Speed Insights |
| Reservar | Menos de 60 s de principio a fin | Embudo de PostHog |
| Disponibilidad | 99,5 % al mes | Monitor externo cada minuto |
| Escala | 500 negocios y 20.000 reservas al mes sin tocar la arquitectura | Prueba de carga antes del lanzamiento |
| Datos | Todo en la UE (Frankfurt) | Región de Supabase y Vercel |
06Si un servicio se cae
| Servicio | Qué pasa |
|---|---|
| Stripe | No se puede reservar con señal; se avisa en la página |
| Resend | El correo va a una cola y se reintenta |
| PostHog | Nada: nunca bloquea la app |
| Supabase | Página de mantenimiento |
07Restricciones
- Sin servidor aparte ni microservicios
- Sin ORM: supabase-js y tipos generados
- Infraestructura por debajo de 50 € al mes
- Nada que obligue a un plan de pago antes de tener 20 negocios
Escribe las restricciones. «Sin servidor aparte» o «menos de 50 € al mes» son las frases que impiden que la IA te monte microservicios para una app de reservas.
5. DATABASE.md: dónde viven los datos
Las tablas, cómo se relacionan, quién puede leer y escribir cada una y cómo se cambia el esquema. Es el que más caro sale no tener: un error en la base de datos no se arregla con un despliegue.
Base de datos
Las tablas, cómo se relacionan, quién puede leer qué y cómo se cambia el esquema sin romper producción.
01Motor
- Base de datos
- Postgres 16 en Supabase, región Frankfurt
- Acceso
- supabase-js con tipos generados del esquema
- Migraciones
- SQL plano en supabase/migrations
- Fechas
- Siempre timestamptz en UTC; se enseñan en la hora del negocio
- Dinero
- Enteros en céntimos, nunca decimales
02Tablas
| Tabla | Campos clave | Relación |
|---|---|---|
| businesses | slug, name, timezone, deposit_cents | Tiene servicios, equipo y reservas |
| staff | business_id, user_id, role | Pertenece a un negocio |
| services | business_id, name, minutes, price_cents | Pertenece a un negocio |
| availability | staff_id, weekday, starts_at, ends_at | Horario de cada empleado |
| customers | business_id, name, phone, email | Uno por negocio, sin cuenta |
| bookings | service_id, staff_id, customer_id, period, status | El centro de todo |
| payments | booking_id, stripe_session_id, amount_cents | Uno por reserva |
03Relaciones
businesses 1──n services businesses 1──n staff 1──n availability businesses 1──n customers 1──n bookings services 1──n bookings n──1 staff bookings 1──1 payments
04Reglas que pone la base, no el código
- Un empleado no puede tener dos reservas solapadas (restricción de exclusión sobre period)
- status es un enum: held, confirmed, cancelled, no_show
- Precios y señales mayores o iguales que 0
- created_at y updated_at en todas las tablas
- Las reservas no se borran: se cancelan
05Row Level Security
Activa en todas las tablas. Sin política, nadie ve nada.
| Tabla | Quién lee | Quién escribe |
|---|---|---|
| businesses | Todo el mundo (solo slug, nombre y dirección) | Su dueña |
| services | Todo el mundo | Staff del negocio |
| bookings | Staff del negocio | Solo el servidor |
| customers | Staff del negocio | Solo el servidor |
| payments | Dueña del negocio | Solo el webhook |
06Convenciones
- Tablas en inglés, plural y snake_case
- Claves primarias uuid; foráneas como tabla_id
- Índice en cada columna por la que se filtra
- Nada de select('*') en producción
07Migraciones
- Una migración por cambio, con nombre que lo diga
- Nunca se edita una migración ya aplicada
- Se prueba con supabase db reset antes de subir
- Borrar o renombrar, en dos pasos: añadir, migrar datos, quitar
08Copias
- Recuperación
- A cualquier punto de los últimos 7 días
- Exportación
- Semanal, fuera de Supabase
- Prueba
- Se restaura una copia el primer lunes de cada mes
09Comandos
# Base de datos local con los datos de prueba supabase start && supabase db reset # Nueva migración supabase migration new add_no_show_to_bookings # Tipos de TypeScript a partir del esquema supabase gen types typescript --local > lib/database.types.ts
Pon en la base las reglas que no se pueden romper, como que un empleado no tenga dos citas a la vez. Una regla que solo vive en el código se la salta el primer endpoint que se olvida de ella.
6. API.md: el contrato entre las piezas
Cada endpoint con su método, quién puede llamarlo, qué recibe, qué devuelve y cómo falla. Con él, la IA escribe el frontend y el backend sin que se contradigan.
API
Cada endpoint: qué recibe, qué devuelve, quién puede llamarlo y cómo falla. Todos siguen las mismas reglas.
01Base
- URL
- https://turnio.app/api/v1
- Formato
- JSON en la entrada y en la salida
- Sesión
- Cookie de Supabase; las rutas públicas no la piden
- Validación
- Un esquema de Zod por ruta, antes de tocar nada
- Versiones
- /v1 en la URL; lo que rompe algo va a /v2
02Endpoints
| Método | Ruta | Quién | Qué hace |
|---|---|---|---|
| GET | /businesses/:slug | Público | Datos y servicios del negocio |
| GET | /slots?service=&date= | Público | Huecos libres de un día |
| POST | /bookings | Público | Retiene el hueco y abre el pago |
| PATCH | /bookings/:id | Cliente con enlace o staff | Cambia la hora |
| POST | /bookings/:id/cancel | Cliente con enlace o staff | Cancela |
| GET | /panel/bookings?date= | Staff | Agenda de un día |
| POST | /panel/services | Dueña | Crea un servicio |
| POST | /webhooks/stripe | Stripe (firma) | Confirma el pago |
| GET | /cron/reminders | Vercel Cron (secreto) | Manda los recordatorios |
03Petición
POST /api/v1/bookings
{ "serviceId": "3f2a…", "staffId": "9b1c…", "startsAt": "2026-10-14T09:30:00Z", "customer": { "name": "Laura Gil", "phone": "+34600111222", "email": "laura@correo.com" } }
04Respuesta
201 Created
{ "data": { "bookingId": "c71d…", "status": "held", "heldUntil": "2026-10-01T10:10:00Z", "checkoutUrl": "https://checkout.stripe.com/…" } }
05Errores
Siempre { error: { code, message } }. El mensaje se puede enseñar tal cual.
| Código | HTTP | Cuándo |
|---|---|---|
| VALIDATION_ERROR | 422 | El cuerpo no cumple el esquema |
| UNAUTHORIZED | 401 | Falta sesión |
| FORBIDDEN | 403 | Hay sesión, pero no es tu negocio |
| NOT_FOUND | 404 | No existe o no puedes verlo |
| SLOT_TAKEN | 409 | Alguien ha cogido el hueco antes |
| RATE_LIMITED | 429 | Demasiadas peticiones |
| INTERNAL | 500 | Fallo nuestro; va a Sentry con su id |
06Reglas
- POST /bookings acepta Idempotency-Key: el doble clic no crea dos reservas
- Listas paginadas con cursor, 50 como máximo
- Fechas en ISO 8601 y UTC; dinero en céntimos
- Nunca se devuelven ids de Stripe ni campos internos
07Límites
- Rutas públicas
- 60 peticiones por minuto e IP
- Crear reserva
- 5 por hora e IP
- Panel
- 300 por minuto y usuario
08Webhooks
- Se verifica la firma antes de leer nada
- Se responde 200 enseguida y se procesa después
- Cada event.id se guarda: si llega dos veces, se ignora
- Eventos: checkout.session.completed y charge.refunded
Define los errores una vez y para todos. Si no, cada endpoint falla a su manera y el frontend se llena de casos especiales.
7. SECURITY.md: lo que no se puede romper
Las reglas de seguridad que el código cumple siempre, lo escriba quien lo escriba. La IA no escribe código inseguro por maldad: lo escribe porque nadie le ha dicho qué proteger.
Seguridad
Qué se protege, de quién y las reglas que cumple el código siempre, lo haya escrito una persona o un agente.
01Qué protegemos
- ClientesNombre, móvil y email
- PagosLas tarjetas nunca pasan por nosotros
- NegociosSu agenda y sus clientes
- SecretosClaves de Stripe, Supabase y Resend
02Autenticación
- Supabase Auth: enlace mágico y Google
- Sesión en cookie httpOnly, nunca en localStorage
- Límite de intentos en login y en enlaces
- El cliente gestiona su cita con un enlace firmado que caduca
- Doble factor para las dueñas en la v1.1
03Autorización
- RLS en todas las tablas, cerrada por defecto
- El business_id sale de la sesión, nunca del cuerpo de la petición
- La clave service_role solo en webhooks y cron
- Cada ruta del panel comprueba que el recurso es de tu negocio
04Secretos
- .env fuera de git; .env.example sin valores
- NEXT_PUBLIC_ solo para lo que puede ver cualquiera
- Claves distintas en preview y en producción
- Si una clave se filtra, se rota ese mismo día
05Entradas y salidas
- Todo lo que llega se valida con Zod en el servidor
- Nunca se pinta HTML que venga de un usuario
- Subidas: solo imágenes, 2 MB, tipo comprobado
- Errores genéricos hacia fuera, detalle solo en Sentry
06Cabeceras
En next.config.ts, para todas las rutas.
Content-Security-Policy: default-src 'self'; frame-src https://checkout.stripe.com Strict-Transport-Security: max-age=63072000; includeSubDomains X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=()
07Datos personales
- Solo se pide lo necesario para la cita
- Borrar un cliente a petición en menos de 30 días
- Nada de datos personales en logs ni en analítica
- Servidores en la UE, contrato de encargo con cada proveedor
08Dependencias
- npm audit en cada PR; alto o crítico, no entra
- Dependabot activado
- Antes de instalar algo: descargas, último commit y licencia
09Si pasa algo
- 1DetectarAlerta de Sentry o aviso
- 2ContenerCortar el acceso
- 3RotarTodas las claves afectadas
- 4AvisarA afectados en 72 h
- 5AprenderQué falló y qué cambia
Escríbelas como reglas que se pueden comprobar («el business_id sale de la sesión, nunca del cuerpo»), no como deseos («la app debe ser segura»). La lista completa está en la guía para que no te hackeen.
8. CODE_STYLE.md: una sola forma de escribir
Nombres, carpetas, reglas del lenguaje y del framework, errores y commits. Hace que el código de cinco sesiones distintas parezca escrito por la misma persona.
Estilo de código
Cómo se escribe el código aquí, para que lo que hace la IA y lo que haces tú parezca escrito por la misma persona.
01Principios
- Simple antes que ingenioso
- No se abstrae hasta el tercer uso
- Borrar antes que añadir
- Los nombres explican el qué; los comentarios, el porqué
- Antes de escribir algo, se busca si ya existe
02Nombres
| Qué | Ejemplo |
|---|---|
| Componentes | SlotPicker.tsx |
| Funciones | createBooking() |
| Booleanos | isHeld, hasDeposit |
| Constantes | HOLD_MINUTES |
| Carpetas y rutas | panel/servicios |
| Tipos | Booking, BookingStatus |
03Dónde va cada cosa
Por funcionalidad, no por tipo de archivo. El test, al lado de lo que prueba.
components/booking/SlotPicker.tsx # lo que se ve lib/bookings/create-booking.ts # lo que decide lib/bookings/create-booking.test.ts # lo que lo prueba app/api/v1/bookings/route.ts # solo valida y llama a lib/
04TypeScript
- strict activado
- Sin any: unknown y se comprueba
- Tipos de la base generados, nunca a mano
- Uniones de texto en vez de enum
05React
- Server Components por defecto
- 'use client' solo con estado o eventos
- Un componente por archivo
- Nada de useEffect para calcular lo que ya se sabe
06Errores
- lib/ devuelve { data, error }, no lanza
- Nunca un catch vacío
- Los textos de error, en un solo archivo
07Formato
- Formateo
- Prettier, al guardar
- Reglas
- ESLint de Next + import/order
- Línea
- 100 caracteres
- Imports
- Absolutos con @/
08Antes y después
// ✗ Así no const d = await fetch('/api/v1/slots?s=' + s).then(r => r.json()) as any // ✓ Así sí const { data: slots, error } = await getFreeSlots({ serviceId, date }) if (error) return <SlotsError />
09Commits
feat(bookings): retener el hueco 10 minutos mientras se paga fix(slots): los huecos salían una hora corridos con el cambio de hora docs(progress): cierre de la sesión del 29 de octubre
Un ejemplo de «así no, así sí» vale más que diez reglas. La IA imita mejor de lo que obedece.
9. AGENTS.md: por dónde empieza el agente
El único que va en la raíz y el primero que se lee. No repite a los demás: dice cuál leer según la tarea, cómo se trabaja y qué no se toca nunca. Claude Code lee CLAUDE.md y el resto de agentes, AGENTS.md; con una línea que importa uno desde el otro, sirven los dos.
Instrucciones del agente
Lo primero que lee cualquier IA que toque el proyecto: qué es, dónde está cada cosa y cómo se trabaja aquí.
01El proyecto
Turnio: reservas online para peluquerías. Next.js 15 y Supabase, desplegado en Vercel. Estamos construyendo el MVP; el estado de hoy está en docs/PROGRESS.md.
02Qué leer antes de tocar nada
No hace falta leerlo todo. Lee lo que toca según la tarea.
| Si vas a… | Lee |
|---|---|
| Empezar cualquier sesión | PROGRESS.md e IMPLEMENTATION_PLAN.md |
| Hacer o cambiar una pantalla | APP_FLOW.md y DESIGN_SYSTEM.md |
| Tocar tablas o consultas | DATABASE.md y SECURITY.md |
| Crear o cambiar un endpoint | API.md y SECURITY.md |
| Elegir una librería o un servicio | TRD.md y DECISIONS.md |
| Subir algo a producción | DEPLOYMENT.md y TESTING.md |
03Cómo trabajas
- 1SituarteLee PROGRESS.md
- 2PlanExplica qué vas a hacer y espera el sí
- 3Cambio pequeñoUna tarea, una rama
- 4ComprobarTests, lint y móvil
- 5ApuntarActualiza los docs
04Siempre
- Reutiliza lo que ya existe antes de crear nada
- Sigue CODE_STYLE.md aunque tú lo harías distinto
- Di en qué archivo y línea está lo que afirmas
- Si un doc contradice al código, avisa: no elijas tú
05Nunca
- Instalar una dependencia sin preguntar
- Editar una migración ya aplicada
- Desactivar RLS, un test o el lint para que algo pase
- Hacer push a main
- Inventarte una API o una opción que no has comprobado
06Pregunta antes de
- Borrar datos o archivos
- Cambiar el esquema de la base
- Tocar el login, los pagos o los permisos
- Añadir un servicio externo
07Comandos
npm run dev # localhost:3000 npm run lint npm run test # Vitest npm run test:e2e # Playwright supabase db reset # base local limpia
08Terminado significa
- Compila sin avisos
- Tests y lint en verde
- Probado a 375 px de ancho
- Docs actualizados en el mismo commit
- Sesión apuntada en PROGRESS.md
09CLAUDE.md
Claude Code lee CLAUDE.md; Codex, Cursor y los demás, AGENTS.md. Uno importa al otro y no se duplica nada.
# CLAUDE.md @AGENTS.md
La tentación es meterlo todo aquí. Si AGENTS.md pasa de dos pantallas, el agente lo lee en diagonal. Que dirija, no que explique.
10. IMPLEMENTATION_PLAN.md: en qué orden
El proyecto partido en fases y tareas numeradas, cada una con lo que tiene que pasar para darla por hecha. La IA hace una tarea cada vez, y tú sabes exactamente cuál.
Plan de implementación
El orden en el que se construye, en fases pequeñas que se pueden probar solas. La IA hace una fase cada vez, nunca el proyecto entero.
01Resumen
- Objetivo
- MVP en producción con 10 peluquerías de prueba
- Duración
- 6 semanas, del 6 de octubre al 15 de noviembre
- Ritmo
- Una tarea = una rama = un PR
- Equipo
- Una persona y un agente
02Fases
- 1CimientosSemana 1
- 2NegocioSemana 2
- 3ReservaSemana 3
- 4PagosSemana 4
- 5AvisosSemana 5
- 6LanzarSemana 6
03Fase 1 · Cimientos
- 1.1 Proyecto, repositorio y Vercel
- 1.2 Supabase local y en la nube
- 1.3 Tablas base y RLS
- 1.4 CI con lint y tests
- Hecho cuando: un push despliega una preview
04Fase 2 · Panel del negocio
- 2.1 Registro y login
- 2.2 Crear servicios
- 2.3 Horario y descansos
- 2.4 Agenda del día
- Hecho cuando: una dueña configura su negocio sola
05Fase 3 · Reserva del cliente
- 3.1 Página pública del negocio
- 3.2 Cálculo de huecos libres
- 3.3 Selector de hueco
- 3.4 Retener el hueco 10 minutos
- Hecho cuando: dos personas no pueden coger el mismo hueco
06Fase 4 · Pagos
- 4.1 Conectar Stripe al negocio
- 4.2 Checkout de la señal
- 4.3 Webhook que confirma
- 4.4 Reembolso al cancelar a tiempo
- Hecho cuando: el pago de prueba confirma la cita
07Fase 5 · Avisos
- 5.1 Dominio verificado en Resend
- 5.2 Correo de confirmación
- 5.3 Recordatorio 24 h antes con cron
- 5.4 Cambiar y cancelar desde el enlace
- Hecho cuando: llega el recordatorio a la hora
08Fase 6 · Lanzamiento
- 6.1 Landing y precios
- 6.2 Analítica del embudo
- 6.3 Revisión de seguridad
- 6.4 10 negocios invitados
- Hecho cuando: entra la primera reserva real
09Lo que bloquea a qué
- Sin 1.3 (RLS) no empieza nada que toque datos
- 3.4 va antes que 4.2: no se cobra un hueco que no está retenido
- 5.1 tarda hasta 48 h en verificarse: se pide en la semana 1
10Cómo se le pide a la IA
Lee AGENTS.md y PROGRESS.md. Implementa solo la tarea 3.4 del plan. Antes de escribir código, dime qué archivos vas a tocar y cómo lo vas a probar.
El error es pedir «hazme la app» en un solo mensaje. Cada fase tiene que poder probarse sola; si no, cuando algo falla no sabes en cuál de las cuarenta cosas nuevas está.
11. TESTING.md: cómo sabes que funciona
Qué tipo de test para qué, los flujos que se prueban de punta a punta, los casos límite y cuándo se ejecuta cada cosa. Convierte «parece que funciona» en algo que se puede comprobar.
Cómo se prueba
Qué se prueba, con qué, cuándo, y qué significa que algo funciona. Sin esto, «funciona» quiere decir «lo miré una vez».
01Qué tipo de test para qué
| Tipo | Herramienta | Qué cubre | Cuántos |
|---|---|---|---|
| Unitario | Vitest | Lógica pura: huecos, precios, fechas | Muchos |
| Integración | Vitest y Supabase local | Endpoints, RLS y consultas | Uno por endpoint |
| De punta a punta | Playwright | Los flujos que dan dinero | Pocos: 6 |
| Manual | Un móvil de verdad | Lo que se ve y se toca | Antes de cada versión |
02Herramientas
- VitestUnitarios e integración
- PlaywrightNavegador real
- Testing LibraryComponentes
- Supabase localBase limpia en cada test
- axeAccesibilidad
- GitHub ActionsEn cada PR
03Flujos de punta a punta
- Reservar y pagar la señal
- Cancelar a tiempo y recibir el reembolso
- Dos personas a la vez en el mismo hueco
- La dueña crea un servicio y aparece en su página
- Entrar con enlace mágico
- Cambiar la cita desde el correo
04Casos límite que siempre se prueban
- Cambio de hora de octubre y de marzo
- Una cita que acaba a medianoche
- El último hueco del día
- Doble clic en «Confirmar»
- Nombre con tildes, emojis o 80 letras
- Conexión lenta (3G)
05Casos de prueba
| ID | Caso | Resultado esperado | Tipo |
|---|---|---|---|
| T-01 | Pedir huecos de un día cerrado | Lista vacía, no error | Unitario |
| T-02 | Reservar un hueco ya retenido | 409 SLOT_TAKEN | Integración |
| T-03 | Negocio A pide reservas de B | Lista vacía por RLS | Integración |
| T-04 | Webhook de Stripe repetido | Una sola confirmación | Integración |
| T-05 | Pago rechazado | Hueco retenido y aviso | Punta a punta |
| T-06 | Cancelar con menos de 24 h | Se avisa y no se reembolsa | Punta a punta |
06Reglas
- Cada bug arreglado deja un test que lo reproduce
- Un test no se borra ni se salta para que pase
- Stripe y Resend, simulados; nunca la red de verdad
- Ningún test depende del orden de los demás
- La zona horaria del CI se fija a Europe/Madrid
07Cuándo se ejecutan
| Momento | Qué |
|---|---|
| Antes de cada commit | Lint y unitarios de lo cambiado |
| En cada PR | Todo, más punta a punta |
| Antes de publicar | Pasada manual en móvil |
| Después de publicar | Reserva de humo en producción |
08Comandos
npm run test # unitarios e integración npm run test -- --watch # mientras programas npm run test:e2e # Playwright npm run test:e2e -- --ui # viéndolo en el navegador
Nombra los casos límite de tu producto. En una app de reservas son el cambio de hora y dos personas cogiendo el mismo hueco, y la IA no los va a probar si no se los dices.
12. DEPLOYMENT.md: cómo llega a producción
No está en la mayoría de listas y es de los que más sustos ahorra: qué hay en cada entorno, qué variables hacen falta, en qué orden se sube cada cosa y cómo se deshace.
Despliegue
Cómo llega el código a producción, qué hay en cada entorno y cómo se da marcha atrás en dos minutos cuando algo sale mal.
01Entornos
| Entorno | Dónde | Cuándo se actualiza | Base de datos |
|---|---|---|---|
| Local | localhost:3000 | Al guardar | Supabase local con datos de prueba |
| Preview | turnio-*.vercel.app | En cada PR | Proyecto staging |
| Producción | turnio.app | Al fusionar en main | Proyecto de producción |
02Del commit a producción
- 1PRRama desde main
- 2CILint y tests
- 3PreviewSe prueba en su URL
- 4MigracionesAntes que el código
- 5ProducciónFusionar en main
- 6HumoUna reserva de prueba
03Variables de entorno
Los nombres viven en .env.example; los valores, solo en Vercel.
| Variable | Para qué | Pública |
|---|---|---|
| NEXT_PUBLIC_SUPABASE_URL | Conectar con Supabase | Sí |
| NEXT_PUBLIC_SUPABASE_ANON_KEY | Clave pública (la protege RLS) | Sí |
| SUPABASE_SERVICE_ROLE_KEY | Webhooks y cron | No |
| STRIPE_SECRET_KEY | Crear pagos | No |
| STRIPE_WEBHOOK_SECRET | Verificar webhooks | No |
| RESEND_API_KEY | Mandar correos | No |
| CRON_SECRET | Que nadie más lance el cron | No |
| SENTRY_DSN | Enviar errores | Sí |
04Antes de fusionar
- CI en verde
- Probado en la preview, también en móvil
- Variables nuevas creadas en producción
- Migraciones compatibles con el código de antes
- Nada a producción un viernes por la tarde
05Migraciones en producción
- Se aplican desde CI, nunca a mano desde tu ordenador
- Siempre antes del código que las necesita
- Copia de seguridad justo antes de una destructiva
06Marcha atrás
- Código: Instant Rollback en Vercel, un clic
- Funcionalidad nueva: se apaga su flag en PostHog
- Base de datos: migración nueva que deshace; nunca borrar la vieja
- Apuntarlo en PROGRESS.md con la causa
07Vigilancia
| Qué | Con qué | Avisa cuando |
|---|---|---|
| Errores | Sentry | Un error nuevo o más de 10 en 5 minutos |
| Caídas | Monitor externo | La web no responde 2 minutos |
| Pagos | Stripe | Un webhook falla |
| Recordatorios | Vercel Cron | El cron no termina |
| Velocidad | Speed Insights | LCP por encima de 2,5 s |
08Dominio y correo
- DNS
- Cloudflare, con el dominio apuntando a Vercel
- Correo
- SPF, DKIM y DMARC verificados para Resend
- HTTPS
- Automático en Vercel; HSTS activado
El plan de marcha atrás se escribe antes de necesitarlo: a las once de la noche y con la app caída, nadie lo improvisa bien. Las trece comprobaciones antes de subir a producción lo completan.
13. DECISIONS.md: por qué es así
Un registro de decisiones, cada una con su contexto, lo que se descartó y lo que cuesta. Impide que una IA sin memoria deshaga en cinco minutos algo que te costó una semana decidir.
Registro de decisiones
Por qué el proyecto es como es. Cada decisión con su contexto, lo que se descartó y lo que cuesta, para que nadie la deshaga sin saberlo. Tampoco la IA.
01Índice
| ADR | Decisión | Estado | Fecha |
|---|---|---|---|
| 001 | Next.js sin servidor aparte | Aceptada | 02/10 |
| 002 | Supabase en vez de Firebase | Aceptada | 02/10 |
| 003 | Recordatorios por SMS | Sustituida por 006 | 03/10 |
| 004 | El cliente reserva sin cuenta | Aceptada | 06/10 |
| 005 | La señal la confirma el webhook, no la página | Aceptada | 27/10 |
| 006 | Recordatorios por email; WhatsApp en la v2 | Aceptada | 28/10 |
02Plantilla
## ADR-000: Título corto Fecha · Estado: propuesta | aceptada | sustituida Contexto: qué problema había Decisión: qué se eligió Alternativas: qué se descartó y por qué Consecuencias: qué ganamos y qué pagamos
03Cuándo se escribe una
- Hay dos opciones razonables y se elige una
- Entra una dependencia o un servicio de pago
- El cambio cuesta mucho de deshacer
- Alguien preguntará «¿por qué así?»
04ADR-004 · El cliente reserva sin cuenta
- Contexto
- En las pruebas, 4 de cada 10 clientes abandonaban al ver «Crea tu cuenta».
- Decisión
- Se reserva con nombre, móvil y email. La cita se gestiona con un enlace firmado.
- Alternativas
- Cuenta obligatoria (más abandono) o login con Google (no todas lo tienen).
- Consecuencias
- Más reservas. A cambio, un cliente puede aparecer repetido: se une por teléfono.
- Revisar si
- Añadimos fidelización o historial para el cliente.
05ADR-005 · La señal la confirma el webhook
- Contexto
- Si el cliente cierra la pestaña tras pagar, la página de éxito no llega a cargarse.
- Decisión
- Solo checkout.session.completed marca la reserva como confirmada.
- Alternativas
- Confirmar al volver de Stripe: rápido, pero pierde pagos.
- Consecuencias
- Unos segundos de «confirmando…» a cambio de no perder ni una reserva pagada.
06Reglas
- Una decisión no se borra: se sustituye por otra
- Cabe en una pantalla
- La IA la redacta; tú la apruebas
- Si la IA propone deshacer una, primero la cita
No escribas una por cada cosa. Solo cuando había dos opciones razonables o cuando deshacerlo sale caro.
14. PROGRESS.md: dónde lo dejamos
La memoria entre sesiones. La IA no recuerda la conversación de ayer, pero puede leer un archivo: qué está hecho, qué falta, en qué rama estás y qué ha fallado ya.
Progreso
La memoria del proyecto entre sesiones: dónde lo dejamos, qué falta y qué ha salido mal. La IA lo lee al empezar y lo escribe al terminar.
01Estado actual
- Fase
- 3 · Reserva del cliente
- Última sesión
- 29 de octubre de 2026
- Rama
- feat/hold-slot
- Siguiente tarea
- 3.4 Retener el hueco 10 minutos mientras se paga
- Bloqueos
- Ninguno
02Hecho
- Fases 1 y 2 completas
- 3.1 Página pública del negocio
- 3.2 Cálculo de huecos, con 14 tests
- 3.3 Selector de hueco en móvil
03Pendiente
- 3.4 Retener el hueco (empezada)
- Revisar textos de error con Marta
- Probar el selector en un Android antiguo
- Deuda: el selector de hueco carga dos veces
04Trampas conocidas
Lo que ya ha fallado una vez y no tiene que volver a fallar.
| Problema | Causa | Solución |
|---|---|---|
| Huecos una hora corridos | Se calculaban en la hora del servidor | Todo en UTC y se convierte al pintar |
| La agenda salía vacía sin error | Faltaba la política select de RLS | Test de RLS por tabla |
| Una reserva confirmada dos veces | Stripe reintenta los webhooks | Guardar event.id |
| E2E en verde en local y en rojo en CI | El CI corre en UTC | TZ=Europe/Madrid en el CI |
05Últimas sesiones
| Fecha | Qué se hizo | Commit |
|---|---|---|
| 29/10 | Selector de hueco y sus estados vacío y error | a41c9e2 |
| 28/10 | Cálculo de huecos con descansos y festivos | 7be0d13 |
| 27/10 | Página pública y ADR-005 | f93a6c1 |
06Antes de cerrar la sesión
- Actualiza el estado actual y la siguiente tarea
- Mueve lo terminado a «Hecho»
- Si algo falló, apunta la trampa y su solución
- Si se decidió algo, escribe su ADR
- Commit: docs(progress): cierre de la sesión
Lo más valioso son las trampas conocidas: cada error apuntado es un error que el agente no repite. Ciérralo al final de cada sesión con el segundo prompt del artículo.
Que los lea y que sigan siendo verdad
Un documento desactualizado es peor que ninguno, porque la IA se lo cree. Seis reglas para que no pase:
- Cada dato en un solo sitio. Si el precio de la señal está en el PRD y en la base de datos, un día dirán cosas distintas. Uno lo cuenta y el otro enlaza.
- En el mismo commit. El cambio de código que contradice un documento lo corrige en el mismo commit, no «luego».
- Cortos. Una o dos pantallas cada uno. Lo que no ayuda al agente a decidir, sobra.
- Nómbralos en el prompt. «Sigue DATABASE.md» funciona mejor que esperar a que lo encuentre.
- Cifras en vez de adjetivos. «Menos de 2,5 segundos» se puede comprobar; «rápido», no.
- Revísalos cada mes. Pide al agente que compare cada documento con el código y te diga dónde mienten.
Los tres prompts: el primero te hace las preguntas y escribe los documentos, el segundo cierra cada sesión y el tercero comprueba que la documentación sigue diciendo la verdad.