Las dos próximas releases · 0.21.1 (OTA) y 0.22.0 (nativa)
10-ago-2026 · Sprint 12 · Regla Juanjo (r16-F18): la decisión se toma en el ANÁLISIS, antes de escribir una línea — no al final, cuando ya no hay marcha atrás.
Un cambio que solo toca JavaScript viaja por OTA: se publica y llega a las usuarias el mismo día, sin tiendas ni revisión. Un cambio que toca el binario exige build nativo: miércoles se construye, pasa revisión, y llega el viernes. Confundirse tiene coste real en las dos direcciones:
Cualquiera de estas cinco cosas obliga a 📦 nativo. Ninguna otra lo hace.
| Detonante | Por qué | Cómo se detecta |
|---|---|---|
| Dependencia nativa nueva (o cambio de versión) | El módulo se compila dentro del binario; el JS solo lo invoca | Cambia package.json en una dependencia con código nativo |
| Permiso nuevo (cámara, salud, ubicación…) | Vive en Info.plist / AndroidManifest, dentro del binario | Cambia app.json → infoPlist, permissions o plugins |
| Icono, splash, nombre, bundle | Recursos empaquetados | Cambia app.json → icon, splash, name |
| Subir versión de Expo SDK o RN | Cambia el runtime entero | Cambia expo o react-native |
| Configuración de arranque (deep links, esquema, notificaciones push a nivel de sistema) | Se registra en el sistema operativo al instalar | Cambia scheme, associatedDomains, canales de notificación |
Leído del código, no de memoria: repos/appdev/package.json y app.json.
| Módulo nativo instalado | Versión | Para qué |
|---|---|---|
expo-image-picker | 17.0.11 | Adjuntar captura en Feedback (galería). Incluye también la cámara del sistema. |
expo-notifications | 0.32.17 | Push (r15) |
expo-local-authentication | ~17.0.8 | Bloqueo biométrico |
expo-secure-store | ~15.0.8 | Secretos del dispositivo |
@kingstinct/react-native-healthkit | plugin | Salud (iOS) |
expo-location | plugin | Sugerir ciudad en el alta |
NO instalados (pedirlos = build nativo): expo-camera · expo-image-manipulator · expo-file-system · expo-media-library · expo-av · expo-sharing.
Proyecto managed (sin carpetas ios/ ni android/): el prebuild lo hace EAS. Runtime por política appVersion → cada versión de app es un runtime distinto y la OTA debe publicarse en todos los vivos (0.18–0.21).
El doc 39 describe el flujo: foto → 1024 px sin EXIF → Storage privado → analyse-meal → borrador. Ese «1024 px sin EXIF» es lo que decide la entrega.
| Pieza del Incremento P1 | Entrega | Razón técnica |
|---|---|---|
Edge Function analyse-meal, tablas, RLS, registro de modelos | 🔄 OTA | Backend: no viaja en el binario. Ya desplegado. |
| Pantalla de captura y de confirmación, textos, i18n | 🔄 OTA | JavaScript puro |
| Elegir foto de la galería | 📦 nativo | Corregido tras verificarlo en el móvil (ver §4b): la librería está en package.json pero NO en el binario 0.21 |
| Subir a Storage | 🔄 OTA | Patrón ya probado en Feedback: fetch(uri) → blob → storage.upload() |
| Hacer la foto con la cámara | 📦 nativo | Misma librería + NSCameraUsageDescription, que tampoco estaba configurada. Añadida ya al app.json para el build del miércoles. |
| Redimensionar a 1024 px y quitar EXIF | 📦 nativo | Exige expo-image-manipulator, que NO está instalado |
quality del propio picker, sin recorte a 1024 ni limpieza de EXIF. Llega a las testers hoy o mañana. Coste: se sube más peso del necesario y los metadatos de la foto (incluida la ubicación) viajarían al Storage — aceptable solo si se documenta en el consentimiento del piloto, y con la promesa de limpiarlo en la 0.22.launchImageLibraryAsync dentro de un try/catch que traga el error en silencio. Si al binario 0.21 le faltan las cadenas de permiso de iOS, la usuaria toca «adjuntar» y no pasa nada — sin aviso, sin registro. Prueba de 30 segundos: abrir la app 0.21 en el iPhone → Ajustes → Enviar feedback → adjuntar captura. Si aparece el selector, el permiso está y la galería es OTA. Si no aparece nada, es nativo y además hay un bug abierto que nadie ha reportado porque no hace ruido.
Diagnóstico (git, no intuición): expo-image-picker entró en el commit 5becee9 del 7-ago, cuyo propio mensaje ya avisaba «se enciende con el próximo build». El binario 0.21 se construyó antes, desde 935a72d. La librería está en package.json pero no está dentro de la app instalada: el require() falla y el JS no puede hacer nada.
Consecuencia: TODO lo fotográfico del Epic P es nativo, también la galería. No hay versión recortada que pueda salir hoy por OTA. La decisión de producto se resuelve sola: P1 completo en la 0.22, que es además lo correcto por privacidad.
package.json no es una dependencia en el binario. Lo que cuenta es lo que se compiló en el build que la usuaria tiene instalado. Para saberlo: git log -S"<paquete>" -- package.json y comparar la fecha con la del último build nativo. Yo mismo caí en esto hace media hora leyendo solo el package.json — el guardarraíl correcto verifica el ARTEFACTO, no la fuente (meta-lección r11c-3).
Bug de fontanería asociado: la pantalla de Feedback avisa con un mensaje amable cuando falta el módulo, pero el resto de fallos del picker se tragan en un catch {} vacío. Un fallo que no hace ruido cuesta días. Se arregla en la 0.22 junto con el resto.
tools/OTA NutriSync App.command, y se recibe cerrando la app dos veces.expo-image-manipulator (1024 px + sin EXIF) y, si falta, el permiso de cámara.eas build -p all --profile production. Un build de una sola plataforma es la excepción y se justifica.package.json, app.json, icono, splash o SDK? → nativo. Fin.El SQL backend-sql/2026-08-10-analisis-entrega-releases.sql saca los candidatos, los que están sin decidir y las contradicciones (un item nativo colado en una release OTA, que es el error que duele).