Files
mercadodevida/work/artifacts/TPV-DEV-IN-PROD/architect.md

214 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 <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ñ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.