# 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 1. Operador del host remoto ejecutó `monolith.sh dev start` (o equivalente: `cd project/apps/pos && npm run dev`) en lugar de `monolith.sh prod start`. 2. El proceso `next dev` se ató al puerto `3002` (único en el host). 3. Traefik enruta `tpv-mv.rikrdo.com → :3002` → recibe la app de TPV en modo dev. 4. El proceso `next start` (prod) que pudo haber existido antes fue desplazado, o nunca se arrancó, o murió silenciosamente. 5. Resultado: prod domain sirve dev internals; HMR, React DevTools y WebSocket a `/_next/hmr` quedan expuestos al público. ### 1.3 Por qué pasó - El script `monolith.sh` y 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`, no `next dev`. Un operador que arranca con `dev` por 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: 1. `curl -fs http://127.0.0.1:$PORT/ | grep -F ...` — grep **negativo** sobre la respuesta HTML inicial. Falla si encuentra cualquiera de: - `/__next_hmr` - `react-refresh` - `Download the React DevTools` - `webpack-hmr` - `__webpack_require__` - `/_next/static/chunks/_devPagesManifest` 2. `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. 3. `grep -F '(Turbopack)' "$log"` — el log de arranque del proceso no debe contener el sufijo `(Turbopack)`, exclusivo de `next 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 arranques `dev` en un host de producción"** con: - Por qué `next dev` no 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` (o `prod restart`). - Señales de que estás en dev por accidente: el log dirá `(Turbopack)` y verás intentos de WebSocket a `/_next/hmr` en la consola del navegador. - `docs/pos/POS_OPERATIONS.md` §1.3 (POS app deployment): añadir una línea inline "**Nunca** `npm run dev` ni `monolith.sh dev` en el host público — usar siempre `monolith.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"**: ```bash # 1) Identificar el proceso dev en :3002 lsof -nP -iTCP:3002 -sTCP:LISTEN # 2) Detenerlo kill -TERM # 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ñadir `smoke_check_no_dev_markers` + invocación en `start_all` cuando `MODE == "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.md` con evidencia de los comandos y outputs. **Fuera (deliberado):** - No se modifica `next.config.ts`: `turbopack.root` y `allowedDevOrigins` son opciones dev-only que `next start` ignora 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 -F` sobre 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 1. `bash -n project/scripts/monolith.sh` → exit 0 (sintaxis válida). 2. `./scripts/monolith.sh dev start` arranca en dev y **no** ejecuta el smoke test (es dev, HMR es esperado). 3. `./scripts/monolith.sh prod start` arranca en prod y **ejecuta** el smoke test; pasa si los 4 servicios Next se sirvieron con `next start`. 4. Forzar `npm run dev` (en lugar del script) en el host y luego intentar `monolith.sh prod start` → el smoke test debe **fallar** con mensaje claro. 5. `docs/HOWTO-monolith.md` contiene §3.1 con la sección de error común y el runbook de recuperación. 6. `docs/pos/POS_OPERATIONS.md` §1.3 contiene la advertencia inline. 7. `./scripts/verify.sh` → exit 0. 8. `work/artifacts/TPV-DEV-IN-PROD/implementer.md` documenta 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 1. Editar `project/scripts/monolith.sh`: - Añadir función `smoke_check_no_dev_markers()` antes de `start_all`. - Definir array `NEXT_SERVICES=(admin tpv frontend storefront)` y array `HMR_MARKERS=('/__next_hmr' 'react-refresh' 'Download the React DevTools' 'webpack-hmr' '__webpack_require__' '_devPagesManifest')`. - En `start_all`, tras el loop `spawn_service`, agregar: `[[ "$MODE" == "prod" ]] && smoke_check_no_dev_markers`. - Si falla: `echo "[FAIL] ..." >&2; stop_all; exit 1`. 2. Editar `docs/HOWTO-monolith.md`: - Añadir §3.1 "Errores comunes — jamás arranques `dev` en un host de producción" + runbook de recuperación. - En §6, añadir bullet "Smoke test post-start verde (incluye verificación HMR-free)". 3. Editar `docs/pos/POS_OPERATIONS.md` §1.3: añadir una línea de advertencia. 4. Ejecutar `bash -n project/scripts/monolith.sh` (sintaxis) y `./scripts/verify.sh` (full). 5. Documentar todo en `work/artifacts/TPV-DEV-IN-PROD/implementer.md`. ## 9 · Próximo stage → **build (implementer)**: ejecutar §8.