# Notas sueltas / aprendizajes > Esto es un scratchpad estructurado, no un playbook — sigue creciendo > con notas cortas a medida que aparecen. Cada entrada está extraída de > un incidente o hallazgo real ya documentado en otra parte del sitio, > no es especulación. ## Gotchas de Medusa v2 - El Store API (`/store/products`, validado por `StoreGetProductsParams`, `.strict()`) **no acepta** `currency_code` ni `country_code` para resolver precios calculados — solo `region_id` explícito. Ver [playbook de precios](../playbooks/incidente-precios-medusa.md). - El campo `thumbnail` de un producto **no se autocompleta** a partir del array `images` — hay que setearlo explícito desde el Admin si el frontend lo usa como imagen hero en listados. ## MinIO / S3 provider - Medusa sube imágenes de producto a MinIO (`minio-svc:9000`, bucket `ari-products`) usando el provider S3-compatible estándar — MinIO no sabe que no es AWS real, y Medusa tampoco necesita saberlo. - La URL pública que termina viendo el navegador **no** es la interna (`minio-svc:9000`): es `S3_FILE_URL=https://media.cruzcloud.net/ari-products`, un dominio propio proxied por NPM hacia el mismo MinIO. Esto tiene una consecuencia directa que ya rompió producción una vez: si `media.cruzcloud.net` deja de rutear bien (mismo patrón de `forward_host` roto que el resto del sitio), **todas** las imágenes de producto del storefront se caen aunque MinIO y Medusa estén perfectamente sanos — porque el navegador nunca habla con MinIO directo, solo con esa URL pública. Ver [Red y exposición](../arquitectura/red-y-exposicion.md). ## Subscriber `revalidate-storefront.ts` y sus límites - Escucha 6 eventos de Medusa (`product.created/updated/deleted` + los 3 equivalentes de `product-variant`) y por cada uno hace un único `POST` a `${STOREFRONT_URL}/api/revalidate` con un header secreto (`x-revalidate-secret`) — esa ruta del frontend llama `revalidateTag("medusa-products")` de Next.js. - **Se degrada solo, no tira el evento entero**: si `STOREFRONT_URL`/`REVALIDATE_SECRET` no están configurados, o el `fetch` falla, solo loguea un `warn` y sigue — un fallo de red hacia el frontend nunca revierte ni bloquea la operación real en Medusa (crear/editar un producto). Mismo criterio de "degradar, no romper" que se repite en el resto del storefront (`searchWithFacets` opcional, `/api/search` cayendo a `{}` si Meilisearch no responde). - **Límite real, todavía no un problema**: no hay debounce ni deduplicación — si una operación dispara varios eventos juntos (ej. editar un producto con 3 variantes a la vez), son varios `POST` independientes al mismo endpoint. `revalidateTag` de Next.js es barato e idempotente, así que hoy no se nota, pero si el catálogo creciera mucho y las ediciones masivas se volvieran frecuentes, sería el primer lugar donde agregar algún tipo de agrupación. ## Qué haría distinto si volviera a montar este lab desde cero - **Fijar `TUNNEL_TRANSPORT_PROTOCOL=http2` desde el día uno** (ver [ADR 0003](../decisiones/0003-por-que-http2-sobre-quic-tunnel.md)) y **ajustar el MTU de `flannel.1` desde el primer `deploy-lab.sh`** — ambos terminaron siendo incidentes reales (504 intermitente, blackhole cross-node) que en retrospectiva eran predecibles: red doméstica con NAT agresivo + Docker-in-Docker multi-nodo son una combinación conocida para tener problemas de MTU y de transporte UDP. Hoy los dos ya están automatizados (`fix_flannel_mtu` en `deploy-lab.sh`, la variable de entorno del túnel), pero se aprendieron reactivamente, no se anticiparon. - **Usar `git merge-base --is-ancestor` para la promoción segura del CI desde el primer pipeline**, no después de ver la condición de carrera en vivo (dos pipelines corriendo en serie sobre el mismo commit, el segundo pisando o saltándose la promoción del primero). El patrón correcto no es complicado una vez que se conoce — el costo fue no anticiparlo. - **Separar siempre "chequeo de disponibilidad" de "migraciones"** en init containers distintos, y que el chequeo use el mínimo de parámetros posible (host/puerto/usuario/db) en vez de reusar una URI de conexión pensada para la app. Confirmado como decisión correcta en el [incidente de crashloop de Medusa](../playbooks/incidente-crashloop-medusa.md). - **No escribir un script que regenere credenciales completas en cada corrida** (`create-commerce-secrets.sh` rota JWT/COOKIE/passwords de Postgres/Redis/MinIO enteros cada vez que se ejecuta, incluso si solo hacía falta agregar una clave nueva) — un script de setup inicial y uno de "agregar una clave a un secret ya vivo" deberían haber sido dos herramientas distintas desde el principio, no una sola que hay que saber cuándo NO correr completa.