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

202 lines
7.9 KiB
Markdown

# 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`
```text
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
$ bash -n project/scripts/monolith.sh && echo "monolith.sh syntax OK"
monolith.sh syntax OK
```
### 2. Función definida y extraíble
```bash
$ 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:
```text
[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ó:
```text
[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
```text
── 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
```text
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
```bash
# 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.)