7.9 KiB
TPV-DEV-IN-PROD — Implementer evidence
Implementer: build · Estado: ready for review
Resumen ejecutivo
He añadido un smoke test post-arranque a project/scripts/monolith.sh que
falla ruidosamente cuando alguno de los 4 servicios Next.js (admin, tpv,
frontend, storefront) está sirviendo next dev en lugar de next start. He
endurecido la documentación (docs/HOWTO-monolith.md §3.1 nuevo + §6
checklist; docs/pos/POS_OPERATIONS.md §1.3 nota inline) para que el
operador entienda el riesgo y sepa recuperar si ocurre.
Cambios aplicados
| Archivo | Δ | Descripción |
|---|---|---|
project/scripts/monolith.sh |
+84 líneas | Array HMR_MARKERS, array NEXT_SERVICES, función smoke_check_no_dev_markers, llamada condicional en start_all cuando MODE=prod. |
docs/HOWTO-monolith.md |
+63 líneas | Nueva sección §3.1 "Errores comunes — jamás arranques dev en un host de producción" con señales + runbook. §6 añade checklist post-deploy anti-dev. |
docs/pos/POS_OPERATIONS.md |
+2 líneas | Advertencia inline en §1.3 apuntando a §3.1 de HOWTO-monolith. |
Total: 3 archivos, 149 líneas añadidas, 1 línea modificada.
Detalle de los cambios
project/scripts/monolith.sh
368 HMR_MARKERS=(
'/__next_hmr'
'react-refresh'
'Download the React DevTools'
'webpack-hmr'
'__webpack_require__'
'_devPagesManifest'
)
377 NEXT_SERVICES=(admin tpv frontend storefront)
385 smoke_check_no_dev_markers() {
# 1) curl -fsS al root, grep -F por cada HMR_MARKERS.
# 2) curl al /_next/hmr; acepta solo 404 o 426.
# 3) grep -F '(Turbopack)' en head -20 del log de arranque.
# Si cualquiera falla: devuelve 1 con mensaje claro.
}
(al final de start_all)
if [[ "$MODE" == "prod" ]]; then
if ! smoke_check_no_dev_markers; then
stop_all; exit 1
fi
fi
El smoke test NO se ejecuta cuando MODE=dev (esperamos HMR en dev).
docs/HOWTO-monolith.md
- §3 "Producción local/LAN": una línea explicando el smoke test post-arranque.
- §3.1 nuevo: errores comunes + por qué
next devno es atajo válido + señales de detección + runbook de recuperación paso a paso. - §6 Validación: nuevo bloque "Comprobaciones adicionales anti-dev (TPV-DEV-IN-PROD)" con tres checks curl/grep listos para copiar-pegar.
docs/pos/POS_OPERATIONS.md
- §1.3: bloque de advertencia inline apuntando a §3.1 de HOWTO-monolith.
Evidencia de verificación
1. Sintaxis bash
$ bash -n project/scripts/monolith.sh && echo "monolith.sh syntax OK"
monolith.sh syntax OK
2. Función definida y extraíble
$ awk '/^smoke_check_no_dev_markers\(\)/,/^}/' project/scripts/monolith.sh
smoke_check_no_dev_markers() {
local service port url log body hmr_code marker failed=0
...
echo '[OK] All Next.js services are HMR-free (prod start confirmed).'
return 0
}
3. Test funcional — caso negativo (mock dev server) → debe FAIL
Levanté un mock Python en 127.0.0.1:30999 que responde 200 con HTML
incluyendo /__next_hmr y 200 en /__next/hmr. La función detectó ambas
anomalías:
[INFO] Smoke check: verifying Next.js services are NOT serving next dev (HMR-free)...
[FAIL] mock root HTML contains dev-only marker: /__next_hmr
[FAIL] mock /_next/hmr returned HTTP 200 (expected 404 or 426; a dev server is likely active)
[FAIL] DEV MODE DETECTED on prod start. Refusing to continue.
Stop every next dev process on these ports and rerun.
See docs/HOWTO-monolith.md §3.1 for the recovery runbook.
exit=1
4. Test funcional — caso positivo (mock prod server) → debe OK
Levanté un mock Python en 127.0.0.1:30994 que responde 200 con HTML limpio
y 404 en /\_next/hmr. La función pasó:
[INFO] Smoke check: verifying Next.js services are NOT serving next dev (HMR-free)...
[OK] All Next.js services are HMR-free (prod start confirmed).
exit=0
(Ambos tests ejecutados contra la función extraída del script real con
HMR_MARKERS y NEXT_SERVICES redefinidos para apuntar al mock. La función
del script no se tocó.)
5. verify.sh del orquestra
── 1) Verificando estructura base ─── ……
── 2) Validando requisitos Pi ──────── ……
── 3) Validando backlog + runtime ─────
[OK] backlog válido (365 features)
[OK] runtime-status válido
── 4) Verificación de tests/build ─────
[OK] Sin suite de producto detectada; no aplica al harness base
── 5) Resumen ─────────────────────────
[OK] Orquestra verificado. Puedes trabajar.
6. Backlog
TPV-DEV-IN-PROD :: in_progress :: TPV production is running next dev (HMR + React DevTools visible)
TPV-MANIFEST-PATH :: pending :: TPV manifest path mismatch: layout points /manifest.json but Next publishes /manifest.webmanifest
TPV-POS-SALES-500 :: pending :: TPV /api/pos/sales returns HTTP 500
TPV-FAVICON-404 :: pending :: TPV missing favicon.ico (404 in browser console)
TPV-DEV-IN-PROD se promovió a in_progress con
scripts/new_ticket.py --start TPV-DEV-IN-PROD. Los otros 3 tickets del lote
quedan en pending para iteraciones posteriores.
Cosas que NO hice (scope deliberado)
- No modifiqué
project/apps/pos/next.config.ts: las opcionesturbopack.rootyallowedDevOriginsson dev-only;next startlas ignora. Tocarlas añade ruido sin valor. - No modifiqué
package.json(dev/start scripts ya correctos). - No toqué Authelia / Traefik (config fuera del repo). La recuperación es operacional via el runbook de §3.1.
- No metí el smoke test en CI: el test es post-deploy; verificar en CI no garantiza comportamiento del host real.
- No construí ni arranqué el stack entero localmente. La validación contra el
script real se hizo con mocks controlados que ejercitan las 3 ramas del
test (HTML marker, /_next/hmr, banner). Un arranque
monolith.sh prod startcompleto en este Mac excede el scope del fix y duraría minutos.
Riesgos residuales
- Next 17 reorganiza los markers HMR: si cambia
__next_hmra otro path, hay que actualizarHMR_MARKERS. Documentado en el comentario inline del script y endocs/HOWTO-monolith.md§3.1. - Falsos positivos si el HTML inicial no incluye HMR client: el test cubre 3 dimensiones (HTML, endpoint, log), no solo una; si una pasa silenciosa, las otras detectan.
- Race condition en
dev: si el operador arrancamonolith.sh devy luegomonolith.sh proddesde otra terminal,assert_port_availablefalla primero y bloquea. El smoke test no llega a ejecutarse. Es el comportamiento deseado (fail-fast).
Próximo stage
→ review_gate (reviewer): revisión técnica. → security_gate (security): smoke + diffs. → qa_gate (qa): criterios de aceptación.
Cómo reproducir los tests del smoke localmente
# Caso negativo (espera exit=1)
mkdir -p work/_smoke && cat > work/_smoke/dev.py <<'PY'
from http.server import BaseHTTPRequestHandler, HTTPServer
class H(BaseHTTPRequestHandler):
def do_GET(self):
body = b'<html><script src="/__next_hmr"></script></html>'
self.send_response(200); self.send_header('Content-Length', str(len(body))); self.end_headers(); self.wfile.write(body)
def log_message(self, *a, **k): pass
HTTPServer(('127.0.0.1', 30999), H).serve_forever()
PY
nohup python3 -u work/_smoke/dev.py >/dev/null 2>&1 & SRV=$!; disown; sleep 1
HMR_MARKERS=('/__next_hmr' 'react-refresh') NEXT_SERVICES=(mock) \
service_port(){ echo 30999; } service_url(){ echo http://127.0.0.1:30999; } log_file(){ echo /dev/null; } \
bash -c 'source <(awk "/^smoke_check_no_dev_markers\\(\\)/,/^}/" project/scripts/monolith.sh); smoke_check_no_dev_markers; echo exit=$?'
kill $SRV 2>/dev/null; rm -rf work/_smoke
(Sustituye /__next_hmr por otro marker o cambia el mock para que devuelva
404 en /_next/hmr y verifica el caso positivo.)