Files
apps-registry/workloads/docs-portal/docs/aprendizajes/notas-sueltas.md
T
devopsandClaude Sonnet 5 fe4ed8005a
Build and Push Docs Portal / Escaneo de Secretos (Gitleaks) (push) Successful in 27s
Build and Push Docs Portal / Construir y publicar Docs Portal (push) Successful in 6m1s
docs: ADR 0011 (ambiente de Staging) + notas de la sesión 2026-09-13/14
Documenta el ambiente de Staging (staging.cruzcloud.net, promoción
manual, footer con version real), y agrega a notas-sueltas la
correccion de bodega Cali, redes sociales reales, rediseño de galeria
estilo Amazon, la leccion de min-w-0 en CSS Grid, y la remocion
definitiva de la barra flotante de carrito en mobile. De paso, suma a
la nav el ADR 0010 que existía en el repo pero nunca se había
enlazado.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV
2026-09-14 05:00:19 +00:00

8.6 KiB

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

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

CSS Grid: un scroller horizontal sin min-w-0 puede desbordar todo el grid

Encontrado en producción (2026-09-13/14) en la página de producto del storefront: un síntoma que parecía un bug de imágenes ("se ve como si abriera en tamaño real" en Chrome/Edge Android) resultó ser, en realidad, un desborde de layout. El carrusel horizontal de "También te puede gustar" (overflow-x-auto, sin flex-wrap) vivía como hijo directo de un CSS Grid. Por spec, el tamaño mínimo automático de un item de grid es el tamaño mínimo de su contenido a menos que el propio item tenga overflow distinto de visible — y como el div que envolvía al carrusel no lo tenía, el ancho mínimo intrínseco de todas las tarjetas del carrusel sin wrappear se contaba como el mínimo de esa columna, forzando todo el grid compartido (imagen, título, specs) a ~800px de ancho en un viewport real de ~380px.

Lección reusable: cualquier fila con scroll horizontal (carrusel, tira de miniaturas, chips de filtro) que sea hijo directo de un CSS Grid necesita min-w-0 en su wrapper (o overflow seteado directamente en el item de grid) — si no, su contenido sin wrappear puede reventar el ancho de todo el grid en silencio, y el síntoma visible no tiene nada que ver con imágenes ni con DPR. Diagnosticado en vivo comparando window.innerWidth (reportaba el ancho desbordado) contra el viewport real del dispositivo.

Rediseño de página de producto y correcciones de datos reales (2026-09-13/14)

Sesión de correcciones guiadas por capturas reales del usuario en navegadores reales (no reportes abstractos):

  • Bodega mal configurada: ENVIOCLICK_ORIGIN_DANE_CODE apuntaba a Bogotá (11001000) cuando la bodega real está en Santiago de Cali, Valle del Cauca (76001000) — afectaba el cálculo real de envío (EnvíoClick) en cada checkout. Corregido en el ConfigMap del frontend, en commerce-backend/medusa-config.ts (incluyendo un fallback obsoleto que también apuntaba a Bogotá) y en el registro real de la bodega en Medusa (vía admin temporal, mismo patrón que el resto del lab para ediciones puntuales en vivo).
  • Redes sociales y páginas de ayuda reales: el footer enlazaba cuentas placeholder; se verificó en vivo la cuenta real de Instagram antes de linkearla (la primera sugerida no era la correcta) y se activaron los enlaces reales de Instagram/Facebook/WhatsApp, además de las páginas /cambios y /preguntas-frecuentes con contenido real (ya existían en código, no estaban enlazadas).
  • Zoom estilo Amazon en la galería de producto: iteración en tres pasos hasta llegar a lo pedido — transform: scale() sobre una imagen ya recortada por object-cover (no funcionaba: lo recortado nunca estaba en el DOM), superposición in-place (técnicamente correcta pero no la UX pedida), y finalmente una lupa que sigue el cursor más un panel lateral que magnifica esa región — el patrón real de Amazon. El tamaño/posición del panel se calcula en JS contra window.innerWidth/innerHeight en cada mousemove (no un breakpoint CSS fijo), con fallback a la superposición in-place si no hay espacio.
  • Barra flotante de "Añadir al carrito" en mobile: removida, no reajustada. Pasó por varias iteraciones de lógica de mostrar/ocultar (con IntersectionObserver) que seguían superponiéndose a otro contenido en capturas reales del usuario. En vez de seguir ajustando el umbral, se eliminó el componente por completo y se dejó el botón inline normal, visible también en mobile — el mismo patrón que usa la mayoría de tiendas. Verificado con Playwright: cero elementos position: fixed en la página de producto.
  • Ambiente de Staging + versión real en el footer: ver ADR 0011 para el detalle completo.