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

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 dev no 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 opciones turbopack.root y allowedDevOrigins son dev-only; next start las 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 start completo en este Mac excede el scope del fix y duraría minutos.

Riesgos residuales

  1. Next 17 reorganiza los markers HMR: si cambia __next_hmr a otro path, hay que actualizar HMR_MARKERS. Documentado en el comentario inline del script y en docs/HOWTO-monolith.md §3.1.
  2. 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.
  3. Race condition en dev: si el operador arranca monolith.sh dev y luego monolith.sh prod desde otra terminal, assert_port_available falla 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.)