📸 Epic P — Meal Photo AI: la comida entra por la cámara
Propuesta 39 · 9 de agosto de 2026 · decisión Juanjo: entra en la 0.22 («yes, 22!») · game changer, no mejora incremental · nace del feedback del piloto (NS-0033 💡) y del análisis competitivo v1.1 (pestaña 🥊)
Foto → borrador creíble → corregir en uno o dos toques → dato nutricional estructurado. Y a partir de ahí, lo estratégico: aprender qué patrones funcionan para CADA usuaria a lo largo de sus ciclos.
1 · Por qué es EL epic (y por qué ahora)
- El registro manual de comidas es la fricción nº 1 de cualquier app de nutrición. La foto la elimina.
- La competencia ya lo hace (28 desde julio, Aluna) — reconocer el plato es commodity. Nuestro hueco: convertir cada comida en dato estructurado y consciente del ciclo, y aprender del bucle recomendar → hacer → responder (informe v1.1, pestaña 🥊).
- El dato de CORRECCIÓN de la usuaria («era pollo, no atún») es el activo que nadie puede comprar.
2 · Reparto (quién trae qué)
| Quién | Trae |
| Lucía | Screenflows y diseños: captura, «analizando…», pantalla de confirmación/corrección (borrador, no veredicto), diario visual. |
| Pilar | Deontología del mapeo foto→nutrientes: qué se estima, cómo se comunica la incertidumbre, qué NUNCA se afirma (nada clínico), reglas de porciones y rangos. |
| Engineering | Toda la lógica: captura, Edge Function de análisis, casado con base nutricional (USDA ya con clave ✔ · EuroFIR pendiente de alta), cálculo determinista, patrones. |
3 · Arquitectura — el stack que ya tenemos, sin piezas nuevas
Cero servidores nuevos (principio rector). Todo Supabase + React Native + un proveedor de visión:
App RN (Expo)
├─ foto → redimensionar 1024px JPEG (compress .75, SIN EXIF)
├─ subir a Storage PRIVADO meal-images/{user_id}/{meal_id}.jpg
└─ crear meal_log (status: uploading → queued)
▼
Edge Function analyse-meal
├─ verificar sesión de la usuaria (y que el meal es SUYO)
├─ descargar imagen privada → base64
├─ visión IA → JSON ESTRICTO (json_schema, strict:true)
├─ casar alimentos contra canonical_foods (USDA/EuroFIR)
├─ macros DETERMINISTAS: per100g × gramos ÷ 100 ← jamás el LLM
└─ guardar borrador (status: needs_review)
▼
Pantalla de confirmación (borrador, no hecho)
├─ «La IA detectó esto» + confianza
├─ corrige porciones/ingredientes → meal_edits (ORO)
└─ confirmar (status: confirmed)
▼
Motor de patrones (SQL primero, LLM solo EXPLICA después)
└─ grupos + macros + fase + síntomas + energía + sueño + ejercicio
Modelos y enrutado
| Caso | Modelo |
| Comida normal (volumen alto) | gpt-5.6-luna (visión, Responses API, salida JSON estricta) |
| Confianza < 0.65 · platos mixtos · salsas · etiquetas/menús | reintento con gpt-5.6-terra |
| Sigue dudoso | UNA pregunta simple a la usuaria: «¿era pollo o atún?» |
Encaje con el Epic N (Propuesta 32): mismo régimen — router reversible, golden-set propio (fotos de comidas reales del piloto con corrección como etiqueta), cero PII en prompts: a la visión viaja SOLO la imagen, jamás el historial de ciclo o salud. El proveedor de visión es una elección revisable, no un matrimonio.
4 · Las reglas que no se negocian
- El formulario manual NUNCA desaparece (requisito Juanjo, 9-ago). «Log Today's Meal» ofrece SIEMPRE dos caminos: 📸 foto con IA (el nuevo) y ✍️ apuntarlo a mano (el sencillo de hoy) — como fallback permanente: sin cámara, sin cobertura, IA caída, o simplemente porque ella prefiera teclear. Ambos acaban en el MISMO databed (
meal_captures.origen = 'foto' | 'manual'); mientras el flujo IA madura, el manual sigue escribiendo donde siempre (meal_logs) sin tocarse. La IA profesionaliza el NutriLog; no le quita la puerta simple.
- El LLM identifica, la base de datos calcula. La IA dice «yogur griego, ~180 g, confianza 0.89»; los macros salen de
canonical_foods (USDA per-100g × gramos). Nada de calorías inventadas con tres decimales.
- Nutrientes ≠ grupos de alimentos — dos capas separadas (proteína/hidratos/grasa/fibra vs legumbres/cereales/verduras/lácteos…). Una comida pertenece a varios grupos.
- Borrador, no veredicto: todo se presenta como estimación con confianza visible y corrección en un toque. Rangos, no falsa precisión. Cero decisiones clínicas.
- La corrección se guarda separada de la estimación original (
meal_edits: before/after) — es el dataset de entrenamiento futuro. No se afina ningún modelo hasta acumular corrección limpia.
- Imágenes: análisis a 1024px JPEG temporal → miniatura 256-384px WebP/JPEG con consentimiento → el grande se borra salvo que ella elija conservar su diario visual. EXIF y localización fuera. Bucket privado, URLs firmadas cortas, borrado explícito en sus manos.
- Claves solo en el backend (Edge secrets): OpenAI, USDA, EuroFIR. Jamás en la app.
- Patrones por SQL primero; el LLM recibe la evidencia calculada y solo la explica («asociación en tus propios datos, no causa»). Comparación contra SU historia, no contra una dieta ideal.
5 · Modelo de datos (resumen — SQL completo en el anexo del chat de ingeniería)
| Tabla | Qué guarda |
meal_logs | La comida: tipo, imagen, status (uploading→queued→processing→needs_review→confirmed→failed), modelo/prompt/confianza IA, macros confirmados, snapshot de fase y día de ciclo, raw JSON de auditoría. |
meal_items | Cada alimento detectado: nombre, casado canónico (fuente+id), gramos estimados (con rango bajo/alto), confianza, preparación, macros calculados, grupos[], ingredientes visibles y ocultos posibles, confirmado o no. |
meal_edits | Cada corrección de la usuaria: before/after JSON + motivo. El oro. |
canonical_foods | La base nutricional propia: fuente (USDA/EuroFIR/OFF/propia), per-100g de energía/proteína/hidratos/grasa/fibra, aliases multiidioma (pgvector después), payload de origen. Cambiar de fuente no rompe la app. |
RLS en todo (cada usuaria solo lo suyo), bucket meal-images privado con carpeta por usuaria — el mismo patrón que feedback-shots.
6 · Fuentes nutricionales
| Fuente | Para | Estado |
| USDA FoodData Central | Ingredientes y alimentos genéricos | CLAVE ACTIVA 9-ago gratuita, en Edge secrets |
| EuroFIR AISBL | Alimentos europeos/españoles (pan, legumbres, quesos, platos locales) | Alta de membresía pendiente → pedir cuota → 🗳 Decisiones. Muestra ES ya en casa: inputs/2026-08-09-EuroFIR-ES-sample.xlsx (2.456 filas de alimentos españoles con nombre original/inglés, grupo y FoodExplorer ID — el formato exacto que casará con canonical_foods) |
| Open Food Facts | Envasados y códigos de barras (después) | Comunidad → con flag de calidad de dato |
| Recetario propio verificado | Platos NutriSync de expertas | Con la capa experta (Incremento 4) |
6b · El databed ya existe (9-ago tarde)
Hecho el mismo día: los 5 datasets oficiales de USDA descargados a datasets/usda/ (Foundation · SR Legacy · FNDDS · Branded · full CSV + el PDF de definiciones de campos, con README de cadencias y avisos), la migración completa del modelo (2026-08-09-epic-p-databed.sql: meal_logs con origen foto|manual para compatibilidad con el formulario manual de hoy, meal_items, meal_edits, canonical_foods agnóstica de fuente, RLS y bucket privado) y el primer seed real: 363 Foundation Foods con macros analíticos per-100g vía tools/usda-import-foundation.mjs (re-ejecutable en cada release semestral de USDA). Estrategia de fuentes: Foundation sembrado entero · Branded (3 GB) por API bajo demanda con caché · FNDDS para porciones estándar · EuroFIR al llegar la membresía (muestra ES ya en inputs/).
7 · Los 5 incrementos y dónde caen
| # | Incremento | Entrega | Release |
| P1 | Borrador de comida por IA: foto → detección + ingredientes + grupos + confirmación. (Cámara = dependencia nativa → build) | 📦 nativo | 0.22 |
| P2 | Cálculo nutricional: casado canónico USDA + porciones + macros/fibra deterministas | 🔄 OTA sobre 0.22 | 0.22.x |
| P3 | Patrones: agregados semanales y por ciclo, observaciones personales, dashboard | 🔄 OTA | 0.22.x / 0.23 |
| P4 | Propuestas expertas: fichas de nutricionistas gobernadas, filtros duros, ranking, registro de aceptación | 📦 + backend | 0.23 |
| P5 | Capa comunidad: recetas moderadas, matching, popularidad y respuesta personal | backend | 0.23 / GA |
Realismo de calendario: la 0.22 es el build nativo del miércoles. El Incremento P1 entra si Lucía trae los screenflows a tiempo y el piloto lo prueba como BETA (flag por cohorte). P2+ viajan por OTA encima — esa es la gracia de haber separado la cámara (nativa) del cerebro (Edge Functions).
8 · Qué mide el éxito (piloto)
- % de fotos que producen borrador útil (is_food + ≥1 item correcto) — objetivo >80%
- % de comidas confirmadas sin corrección — el 72% de referencia del informe
- Tiempo foto→confirmación < 20 segundos
- Correcciones/semana (el dataset creciendo) y comidas/usuaria/día (adopción real)
9 · Encaje en los planes
- 🗂 Sprint Planning: el epic entra como P·Increments con release asignada (SQL 2026-08-09).
- 🥊 Competencia: es la respuesta al «commodity» — la sección 5 del informe es la especificación funcional de este epic.
- Epic N (Propuesta 32): comparte router, golden-set y regla cero-PII. Este epic APORTA el mejor golden-set posible: fotos reales + correcciones reales del piloto.
- Wearables (Epic O): el ejercicio NO va por foto — jerarquía: Health/wearables → un toque → voz → foto de pantalla como último recurso.
- Compliance: imágenes = dato personal; el flujo de retención/borrado entra en el RAT y la EIPD con Audidat (J5).
Anexo técnico completo (SQL de tablas + RLS + Edge Function analyse-meal + captureMeal.ts) archivado en el chat de ingeniería del 9-ago y listo para arrancar el Incremento P1 cuando lleguen los screenflows.