11 KiB
TPV-DEV-IN-PROD — Diseño técnico
Arquitecto: design · Estado: ready for build
1 · Diagnóstico (root cause)
El host de producción tpv-mv.rikrdo.com está sirviendo el TPV con next dev
(Turbopack) en lugar de next start. La app funciona superficialmente, pero
expone internals de desarrollo y rompe la experiencia PWA.
1.1 Evidencia
| Fuente | Hallazgo |
|---|---|
Consola del navegador (tpv-mv.rikrdo.com) |
[HMR] connected repetido, web-socket.ts:50 WebSocket … /_next/hmr fallando, "Download the React DevTools". Solo next dev emite HMR. |
project/.runtime/dev/tpv.log |
▲ Next.js 16.3.1 (Turbopack) ← banner exclusivo de next dev. 324 líneas con tráfico real. |
project/.runtime/prod/tpv.log |
▲ Next.js 16.3.1 (sin Turbopack) → arranque correcto de next start, 5 líneas, sin PID asociado, sin tráfico posterior. Proceso prod nunca se mantuvo. |
project/apps/pos/.next/BUILD_ID |
Existe (UjpwEJ1_CRR9xKXBHOcBu, 26 ago 23:25). El build de prod existe; el problema es de arranque, no de build. |
project/apps/pos/package.json |
dev: next dev --port 3002, start: next start --port 3002. Definición correcta. |
project/scripts/monolith.sh |
Distingue dev:tpv (Turbopack) vs prod:tpv (no Turbopack). Lógica correcta. |
project/apps/pos/next.config.ts |
Tiene turbopack: { root: __dirname } y allowedDevOrigins (incluye tpv-mv.rikrdo.com). Estas opciones son dev-only; su presencia confirma que la config se diseñó pensando en dev. |
git log reciente |
No hay commits que toquen monolith.sh, next.config.ts o el arranque. El bug no vino de un cambio de código. |
1.2 Cadena causal
- Operador del host remoto ejecutó
monolith.sh dev start(o equivalente:cd project/apps/pos && npm run dev) en lugar demonolith.sh prod start. - El proceso
next devse ató al puerto3002(único en el host). - Traefik enruta
tpv-mv.rikrdo.com → :3002→ recibe la app de TPV en modo dev. - El proceso
next start(prod) que pudo haber existido antes fue desplazado, o nunca se arrancó, o murió silenciosamente. - Resultado: prod domain sirve dev internals; HMR, React DevTools y WebSocket
a
/_next/hmrquedan expuestos al público.
1.3 Por qué pasó
- El script
monolith.shy la doc (HOWTO-monolith.md) ya dicen "prod =next start" y "dev =next dev". El conocimiento está correcto. - No existe un smoke test post-deploy que verifique que el proceso activo es
next start, nonext dev. Un operador que arranca condevpor confusión o Shortcut no recibe señal hasta que aparece un bug raro (HMR, favicon, manifest). - No hay una advertencia explícita que diga "dev mode en host público = expones dev internals y rompes PWA". Las consecuencias están implícitas.
2 · Enfoque del fix
Refuerzo de la guardarraíl operacional en 3 capas, todas centradas en detectar el caso "dev corriendo donde debería ir prod" en el momento del despliegue, no horas después en la consola del cliente.
Capa A — Smoke test obligatorio post monolith.sh prod start
Añadir una función smoke_check_no_dev_markers() que, tras wait_http
para los 4 servicios Next.js (admin, tpv, frontend, storefront — backend no usa
Next), ejecute:
curl -fs http://127.0.0.1:$PORT/ | grep -F ...— grep negativo sobre la respuesta HTML inicial. Falla si encuentra cualquiera de:/__next_hmrreact-refreshDownload the React DevToolswebpack-hmr__webpack_require__/_next/static/chunks/_devPagesManifest
curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:$PORT/_next/hmr— esperar HTTP 404 o 426 en el endpoint HMR. Cualquier otro código (200, 101, 405) indica que un dev server está escuchando.grep -F '(Turbopack)' "$log"— el log de arranque del proceso no debe contener el sufijo(Turbopack), exclusivo denext dev.
La función se invoca desde start_all después del loop spawn_service solo
cuando MODE == "prod". Si cualquiera falla: exit 1, mensaje claro
"DEV MODE DETECTED ON PROD START — refusing to continue", y se eliminan los
PIDs que monolith.sh acaba de registrar (deja el sistema en estado limpio).
Por qué aquí: captura el bug en el momento del deploy, antes de que Traefik apunte tráfico de usuarios a la instancia incorrecta.
Capa B — Documentación endurecida
docs/HOWTO-monolith.md: añadir sección "§3.1 Errores comunes — jamás arranquesdeven un host de producción" con:- Por qué
next devno es un atajo válido: expone HMR, React DevTools, source maps y rutas internas; consume ~10× memoria; no compila para prod; rompe PWA manifest y cookies Secure assumptions. - Comando correcto:
./scripts/monolith.sh prod start(oprod restart). - Señales de que estás en dev por accidente: el log dirá
(Turbopack)y verás intentos de WebSocket a/_next/hmren la consola del navegador.
- Por qué
docs/pos/POS_OPERATIONS.md§1.3 (POS app deployment): añadir una línea inline "Nuncanpm run devnimonolith.sh deven el host público — usar siempremonolith.sh prod."docs/HOWTO-monolith.md§6 (Validación antes/después del deploy): añadir el smoke test al checklist post-deploy.
Capa C — Runbook de remediación (operacional, no código)
Añadir a docs/HOWTO-monolith.md un mini-runbook "Si ya estás sirviendo dev
en prod, recuperación rápida":
# 1) Identificar el proceso dev en :3002
lsof -nP -iTCP:3002 -sTCP:LISTEN
# 2) Detenerlo
kill -TERM <PID>
# 3) Limpiar runtime stale (incluye dev huérfano)
rm -f project/.runtime/dev/*.pid
# 4) Arrancar prod
cd project && ./scripts/monolith.sh prod start
# 5) Confirmar: el log dirá "▲ Next.js 16.3.1" SIN "(Turbopack)"
# y el smoke test post-start debe pasar.
tail project/.runtime/prod/tpv.log
3 · Alcance (scope)
Dentro:
project/scripts/monolith.sh— añadirsmoke_check_no_dev_markers+ invocación enstart_allcuandoMODE == "prod".docs/HOWTO-monolith.md— §3.1 + actualización de §6.docs/pos/POS_OPERATIONS.md— nota inline en §1.3.work/artifacts/TPV-DEV-IN-PROD/implementer.mdcon evidencia de los comandos y outputs.
Fuera (deliberado):
- No se modifica
next.config.ts:turbopack.rootyallowedDevOriginsson opciones dev-only quenext startignora sin warning. Tocarlas añade ruido sin valor; el fix correcto es operacional. - No se modifica
package.json(dev/start scripts ya correctos). - No se toca Authelia / Traefik (config fuera del repo). La capa C deja claro que la recuperación es operacional.
- No se mete un check en CI: el smoke test es post-deploy; verificar en CI no garantiza el comportamiento del host real.
4 · Decisiones de diseño y por qué
- Smoke test basado en
grep -Fsobre HTML, no en introspección del proceso. Es black-box: no depende de flags internas de Next. Funciona aunque la versión de Next cambie. Si Next 17 reorganiza los markers HMR, hay que actualizar el patrón — pero el coste de un grep es despreciable y el acierto es evidente. - Test cubre los 4 servicios Next, no solo TPV. El bug es sistémico: cualquier servicio Next del repo es susceptible. Limitar al TPV dejaría una bomba de tiempo en admin/frontend/storefront.
- El smoke test falla ruidosamente (
exit 1) en lugar de warn. Warn se ignora; un fail del deploy obliga a reaccionar. El runbook de remediación está a 1 scroll. - No se automatiza el "kill dev before prod" más allá de lo que ya hace
clear_stale_deployments. Automatizar un kill "inteligente" de procesos ajenos es peligroso (false positives en puertos compartidos). Mejor detectar y abortar. - Capa C (runbook) es código-en-documentos, no automatización. Mantenerlo como doc evita código que pueda fallar cuando el operador lo necesita.
5 · Acceptance criteria
bash -n project/scripts/monolith.sh→ exit 0 (sintaxis válida)../scripts/monolith.sh dev startarranca en dev y no ejecuta el smoke test (es dev, HMR es esperado)../scripts/monolith.sh prod startarranca en prod y ejecuta el smoke test; pasa si los 4 servicios Next se sirvieron connext start.- Forzar
npm run dev(en lugar del script) en el host y luego intentarmonolith.sh prod start→ el smoke test debe fallar con mensaje claro. docs/HOWTO-monolith.mdcontiene §3.1 con la sección de error común y el runbook de recuperación.docs/pos/POS_OPERATIONS.md§1.3 contiene la advertencia inline../scripts/verify.sh→ exit 0.work/artifacts/TPV-DEV-IN-PROD/implementer.mddocumenta los comandos ejecutados y los outputs observados.
6 · Verificación esperada
bash -n project/scripts/monolith.sh→ exit 0.- Búsqueda de los markers HMR en el HTML renderizado por un dev local:
curl -fs http://127.0.0.1:3002/ | grep -E '/__next_hmr|react-refresh'→ matches (sanity check del test). - Smoke test sobre prod local (si hay tiempo):
monolith.sh prod start→ grep -F en/→ no matches → PASS. ./scripts/verify.sh→ exit 0.
7 · Riesgos y mitigaciones
| Riesgo | Mitigación |
|---|---|
| Next 17 reorganiza markers HMR y rompe el grep | Comentario explícito en el script listando los markers y la fuente. Si cambia, hay que actualizar el patrón — pero el cambio será visible en cualquier deploy. |
| El smoke test genera falsos positivos si el HTML inicial aún no incluye el HMR client (timing) | Reintento: si el primer curl no encuentra el marker pero el proceso es next dev, el segundo marker (/_next/hmr 404/426) lo detecta. Doble cobertura. |
Operador preocupado por tiempo extra en monolith.sh prod start |
El smoke test son 4 curls + 4 greps = <500ms. Despreciable comparado con el build. |
Cambio en monolith.sh rompe otros features que dependen de su output |
Smoke test solo afecta a exit code; los logs siguen imprimiendo lo mismo. monolith.sh status/stop/logs/urls no se tocan. |
8 · Resumen para el implementer
- Editar
project/scripts/monolith.sh:- Añadir función
smoke_check_no_dev_markers()antes destart_all. - Definir array
NEXT_SERVICES=(admin tpv frontend storefront)y arrayHMR_MARKERS=('/__next_hmr' 'react-refresh' 'Download the React DevTools' 'webpack-hmr' '__webpack_require__' '_devPagesManifest'). - En
start_all, tras el loopspawn_service, agregar:[[ "$MODE" == "prod" ]] && smoke_check_no_dev_markers. - Si falla:
echo "[FAIL] ..." >&2; stop_all; exit 1.
- Añadir función
- Editar
docs/HOWTO-monolith.md:- Añadir §3.1 "Errores comunes — jamás arranques
deven un host de producción" + runbook de recuperación. - En §6, añadir bullet "Smoke test post-start verde (incluye verificación HMR-free)".
- Añadir §3.1 "Errores comunes — jamás arranques
- Editar
docs/pos/POS_OPERATIONS.md§1.3: añadir una línea de advertencia. - Ejecutar
bash -n project/scripts/monolith.sh(sintaxis) y./scripts/verify.sh(full). - Documentar todo en
work/artifacts/TPV-DEV-IN-PROD/implementer.md.
9 · Próximo stage
→ build (implementer): ejecutar §8.