✅ Escaneo completo (FULL) — project-doc.md y AGENTS.md generados correctamente. Estado actualizado en
state.json → checkpoints.scan = "completed"
⚡
Tech Stack
Runtime
Node.js
v20.11 LTS
Lenguaje
TypeScript
5.4.x (strict)
Framework
Next.js
14 App Router
Base de datos
PostgreSQL
via Prisma 5.x
Estilos
Tailwind CSS
3.4 + shadcn/ui
Estado global
Zustand
4.5.x
Data fetching
React Query
TanStack v5
Autenticación
NextAuth.js
v5 (multitenant)
Pagos
Stripe
webhooks + checkout
Email
Resend
transaccional
Tests unitarios
Vitest
2.x
Tests E2E
Playwright
1.44.x
🏛️
Patrón de Arquitectura
🔧 Feature-Based + Repository Pattern (multitenant)
inferido del código
El proyecto sigue una arquitectura Feature-Based combinada con el patrón Repository. La lógica de negocio reside exclusivamente en /lib/services/, separada en dominios. Los componentes de UI no acceden a la base de datos directamente. Todo flujo de autenticación y aislamiento de datos está condicionado por tenantId.
Cliente
Browser
React + shadcn/ui
→
App Router
Next.js 14
RSC + API Routes
→
Services
lib/services/
Business logic
→
ORM
Prisma
Data access layer
→
DB
PostgreSQL
RLS por tenant
📁
Estructura de Carpetas
nutritrack-app/
├── app/ # App Router — páginas y layouts
│ ├── (auth)/ # Rutas protegidas con middleware NextAuth
│ ├── (dashboard)/ # Panel principal por clínica (tenantId)
│ │ ├── patients/ # CRUD pacientes
│ │ ├── diet-plans/ # Planes nutricionales
│ │ └── reports/ # Informes y analytics
│ ├── api/ # API Routes (solo orquestación, sin lógica)
│ └── layout.tsx
├── components/
│ ├── ui/ # shadcn/ui — componentes base sin lógica de negocio
│ ├── patients/ # Componentes dominio pacientes
│ ├── diet-plans/ # Componentes dominio planes
│ └── shared/ # Componentes transversales (DataTable, Charts)
├── lib/
│ ├── services/ # ⭐ Toda la lógica de negocio aquí
│ │ ├── patients/
│ │ ├── diet-plans/
│ │ └── reports/ # ⚠️ N+1 conocido en nutrition-summary.ts
│ ├── db/ # Prisma client singleton (lib/db/index.ts)
│ └── auth/ # NextAuth config — ZONA CRÍTICA
├── types/ # [domain].ts — tipos TypeScript por dominio
├── hooks/ # Custom React hooks (use[Domain]Query)
├── prisma/
│ └── schema.prisma # Esquema con RLS por tenantId
├── __tests__/ # Vitest — tests unitarios de services
└── e2e/ # Playwright — flujos críticos E2E
📏
Convenciones de Código
📋 Patrones inferidos del código fuente
| Aspecto | Convención detectada | Ejemplo |
|---|---|---|
| Exports | Named exports (default exports: 0 encontrados) | export function getPatientsService() |
| Nombre de archivos | kebab-case para todo | nutrition-summary.ts |
| Nombre de hooks | use[Dominio][Acción] | usePatientsQuery |
| Tipos | Un archivo por dominio en /types/ |
types/patient.ts |
| Imports | Alias @/ para rutas internas |
import { db } from "@/lib/db" |
| Async/await | Siempre async/await (sin .then/.catch raw) | const patients = await getPatients(tenantId) |
| Validación | Zod schemas en entrada de API | patientSchema.parse(body) |
🗄️
Arquitectura de Datos
🔑 Entidades principales (Prisma schema)
multitenant por tenantId
// prisma/schema.prisma — relaciones clave detectadas
model Clinic { // Tenant root
id String @id @default(cuid())
patients Patient[]
dietPlans DietPlan[]
subscription Subscription?
}
model Patient {
id String @id
clinicId String // → tenantId isolation
clinic Clinic @relation(...)
dietPlans DietPlan[]
logs NutritionLog[]
}
model DietPlan {
id String @id
patientId String
clinicId String // redundante por RLS
meals Meal[]
@@index([clinicId, patientId])
}
⚠️ N+1 detectado: lib/services/reports/nutrition-summary.ts
→ Carga NutritionLog por paciente dentro de un loop
→ Corrección sugerida: incluir via Prisma .include() o batch query
🧪
Cobertura de Tests
📊 Vitest + Playwright — Estado actual
⚠ 62% — por debajo del objetivo 80%
Cobertura global
62%
Objetivo mínimo
80%
| Módulo | Cobertura | Estado |
|---|---|---|
lib/services/patients/ |
84% | ✓ OK |
lib/services/diet-plans/ |
78% | ⚠ Cerca |
lib/services/reports/ |
31% | ✗ Crítico |
lib/auth/ |
72% | ⚠ Zona crítica |
app/api/ (handlers) |
45% | ✗ Bajo |
e2e/ (Playwright) |
checkout, login, patient CRUD | ✓ Flujos clave |
🤖
AGENTS.md — Reglas para agentes IA
GENERADO
ℹ️ El archivo
AGENTS.md se escribe en la raíz del repositorio. Todos los agentes del pipeline lo leen antes de comenzar cualquier tarea. NO copiar de project-doc.md — está reescrito como instrucciones directas.
📐 Stack Context
Next.js 14 App Router + Prisma 5 + PostgreSQL + Tailwind + shadcn/ui + Vitest + Playwright
Multitenant SaaS (tenantId = clinicId) · Auth: NextAuth.js v5 · Pagos: Stripe webhooks
✅ / 🚫 Reglas de Código
- DO usar named exports en todos los archivos. Los default exports están prohibidos en este proyecto.
- DON'T añadir lógica de negocio en API route handlers. Delegar siempre a /lib/services/[dominio]/
- DON'T consultar la DB directamente desde componentes o páginas. El acceso a datos es exclusivo de /lib/services/
- DO incluir tenantId (clinicId) en TODAS las queries a la DB. Sin excepción. Filtrado obligatorio en capa de servicio.
- DO usar alias @/ para imports internos. Nunca usar rutas relativas que suban más de 1 nivel (../../).
-
N+1 conocido en lib/services/reports/nutrition-summary.ts — no agravar. Usar
.include()de Prisma o batch queries al modificar este módulo. - DON'T usar .then()/.catch() raw. Este codebase usa exclusivamente async/await con try/catch.
👥
Instrucciones por Agente
🎯
Orchestrator
- ¿El cambio toca el payment flow (Stripe)? Si sí → requiere revisión humana obligatoria.
- ¿El cambio afecta a
/lib/auth/? → marcar automáticamente como 🔴 Crítico. - ¿Existe tenantId en todas las queries del PR? → validar antes de pasar al Developer.
- ¿El PR añade nuevas rutas API? → verificar que no contienen lógica de negocio inline.
🏗️
Architect
- N+1 conocido en
reports/nutrition-summary.ts— evitar patrones similares en nuevas queries. - No crear directorios fuera del patrón feature-based sin aprobación humana.
- PostgreSQL RLS como segunda capa de tenant isolation — no saltarla con queries raw.
- Zustand solo para estado UI efímero. Datos de servidor → React Query únicamente.
💻
Developer
- Usar
dayjspara fechas.momentestá baneado (no está en package.json). - Validación de entrada obligatoria con Zod en todas las API routes.
- Emails transaccionales via Resend SDK. No fetch directo a la API.
- React Query para todo data fetching desde cliente. No usar fetch() raw en componentes.
🔍
PR Reviewer
- 🔴 Crítico: cualquier cambio en
/lib/auth/— requiere aprobación humana explícita. - 🔴 Crítico: webhook handler de Stripe sin verificación de firma (
stripe.webhooks.constructEvent). - 🟡 Should Fix: queries sin
tenantIdfilter. - 🟡 Should Fix: componentes que importan desde
@/lib/dbdirectamente.
🧪
QA Agent
- Cobertura actual: 62%. Todo código nuevo debe incluir tests. Objetivo mínimo por módulo: 80%.
- Flujos críticos E2E siempre: login multitenant → gestión paciente → generación informe → pago Stripe.
- Probar siempre: estado vacío (0 pacientes), estado de carga, error de red, y sesión expirada.
- Módulo
lib/services/reports/con cobertura 31% → prioridad máxima en nuevos tests. - Tests de integración usan DB real (Postgres test container) — no usar mocks de DB.
🔒 Reglas de Seguridad — Todos los agentes
OBLIGATORIO
-
Nunca hardcodear secrets, tokens o credenciales. Solo variables de entorno (
process.env.*). - Nunca exponer el modelo Prisma o errores de DB directamente en respuestas de API.
- Todo código que toque auth o payments → flagear inmediatamente para revisión humana.
-
Verificar firma de webhooks Stripe con
stripe.webhooks.constructEvent()antes de procesar cualquier evento.