NUTRISYNCBuilders Hub
🏠 🛠

40 · Análisis de entrega: qué va por OTA y qué exige build nativo

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.

1 · La pregunta y por qué importa

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:

2 · Qué obliga a build nativo (regla dura)

Cualquiera de estas cinco cosas obliga a 📦 nativo. Ninguna otra lo hace.

DetonantePor 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 invocaCambia package.json en una dependencia con código nativo
Permiso nuevo (cámara, salud, ubicación…)Vive en Info.plist / AndroidManifest, dentro del binarioCambia app.jsoninfoPlist, permissions o plugins
Icono, splash, nombre, bundleRecursos empaquetadosCambia app.jsonicon, splash, name
Subir versión de Expo SDK o RNCambia el runtime enteroCambia expo o react-native
Configuración de arranque (deep links, esquema, notificaciones push a nivel de sistema)Se registra en el sistema operativo al instalarCambia scheme, associatedDomains, canales de notificación
Todo lo demás es OTA: pantallas, textos, i18n, lógica de negocio, llamadas a Supabase, Edge Functions (¡el backend no es la app!), consultas, estilos, navegación entre pantallas existentes, correcciones de crash en JS.

3 · Inventario nativo de la app HOY (0.21.0)

Leído del código, no de memoria: repos/appdev/package.json y app.json.

Módulo nativo instaladoVersiónPara qué
expo-image-picker17.0.11Adjuntar captura en Feedback (galería). Incluye también la cámara del sistema.
expo-notifications0.32.17Push (r15)
expo-local-authentication~17.0.8Bloqueo biométrico
expo-secure-store~15.0.8Secretos del dispositivo
@kingstinct/react-native-healthkitpluginSalud (iOS)
expo-locationpluginSugerir 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 appVersioncada versión de app es un runtime distinto y la OTA debe publicarse en todos los vivos (0.18–0.21).

4 · Epic P · Meal Photo AI: el análisis que decide la 0.22

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 P1EntregaRazón técnica
Edge Function analyse-meal, tablas, RLS, registro de modelos🔄 OTABackend: no viaja en el binario. Ya desplegado.
Pantalla de captura y de confirmación, textos, i18n🔄 OTAJavaScript puro
Elegir foto de la galería📦 nativoCorregido 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🔄 OTAPatrón ya probado en Feedback: fetch(uri) → blob → storage.upload()
Hacer la foto con la cámara📦 nativoMisma 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📦 nativoExige expo-image-manipulator, que NO está instalado
La decisión de producto que había que tomar — resuelta: la opción 1 no existe (ver §4b). Se queda por su valor de registro. — dos caminos legítimos:
  1. P1 por OTA esta semana: solo galería, comprimiendo con 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.
  2. P1 completo en la 0.22 nativa: cámara + 1024 px + sin EXIF, tal como manda el doc 39. Build el miércoles, usuarias el viernes. Coste: dos días.
Recomendación de ingeniería: la opción 2. La privacidad de la foto es el corazón de este epic (doc 39 §4: «cero PII al proveedor»), y subir EXIF con ubicación al Storage contradice esa promesa. Los dos días valen. Si Juanjo prefiere ver algo funcionando ya, la opción 1 es válida solo con galería y avisando.
⚠ Comprobación pendiente que puede morder — el adjuntar captura en Feedback usa 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.

4b · La comprobación de 30 segundos dio negativo — y corrige el análisis

Juanjo, 10-ago: «solo puedo adjuntar screenshots utilizando la funcionalidad nativa de TestFlight, pero nada más; no puedo capturar en Send Feedback, solo escribir».

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.

Lección grabada (r17): una dependencia en 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.

5 · Las dos releases, tal como quedan

🔄 0.21.1 — OTA · lista cuando lo esté (mismo día)

📦 0.22.0 — nativa · build miércoles 12 → usuarias viernes 14

6 · Cómo se decide cada item (dos minutos por item)

  1. ¿Toca package.json, app.json, icono, splash o SDK? → nativo. Fin.
  2. ¿Usa una capacidad del móvil? Mira el inventario del §3: si el módulo ya está en el binario, es OTA; si no, nativo.
  3. ¿Necesita un permiso que la app aún no pide? → nativo (aunque el módulo esté).
  4. En cualquier otro caso → OTA.
  5. Se anota en 🗂 Sprint Planning, columna entrega, antes de empezar. Esa columna es la que manda en 🚢.

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).