Handoff bot: hallazgos reales de logs (conflict/503, persist parcial, sin trigger)
Vía SSH al VPS + docker logs: la conexión Baileys está en bucle de reconexión (conflict:replaced + 503); persistirTurno funciona (→ ok) pero solo se llama en algunos turnos y la máquina de estados se descuadra; y el bot NUNCA llama a ingesta/perfilCompleto, así que la generación de presupuesto/render/entrega no se dispara (Problema C, el que rompe el end-to-end). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -41,12 +41,19 @@ a los logs en Dokploy). La parte de la app (EPs) está verificada y no es el pro
|
||||
para ver `remoteJid`/`remoteJidAlt`, si llega algo, y el resultado del matching.
|
||||
- Subido el límite de body del worker a 30 MB (las fotos en data URI rompían el 100kb por defecto).
|
||||
|
||||
## 3. Problema A — Baileys deja de recibir tras reconectar
|
||||
## 3. Problema A — conexión Baileys inestable (bucle de reconexión)
|
||||
|
||||
**Síntoma:** justo tras un **escaneo fresco** del QR, el bot recibe mensajes (conversación OK). Pero
|
||||
tras cualquier **reconexión** (redeploy, o auto-reconexión de Baileys al caerse el socket), reporta
|
||||
`connection: "open"` pero **no recibe nada** (`/debug` → `inbound: []` aunque el cliente escriba).
|
||||
`markOnlineOnConnect: true` ayudó al primer connect pero no lo blinda contra reconexiones.
|
||||
**Evidencia en logs del contenedor (`docker logs`):** el socket se cae cada pocos minutos con
|
||||
`stream:error → conflict {type:"replaced"}` y `stream:error code 503`, y reconecta en bucle
|
||||
(`AwaitingInitialSync → Transitioning to Online → opened connection to WA → ✅ conectado`, repetido).
|
||||
El `conflict: replaced` indica que **otra sesión reclama la misma cuenta** — típico del **solapamiento
|
||||
en el deploy** (Swarm arranca el contenedor nuevo antes de matar el viejo y ambos usan la misma
|
||||
sesión del volumen). `markOnlineOnConnect: true` mejoró la recepción pero no arregla una conexión que
|
||||
se cae sola.
|
||||
|
||||
**Mitigación parcial (Baileys):** configurar el deploy del bot como **stop-first / sin solapamiento**
|
||||
(1 réplica, recreate) para evitar el `conflict`. Pero los `503` son de WhatsApp y seguirán.
|
||||
**Vía robusta (acordada): Evolution API.**
|
||||
|
||||
**Cómo reproducir/diagnosticar:**
|
||||
- `GET https://reformix-bot.dv3.com.es/debug` (Basic, contraseña `QR_TOKEN`): si `connection:open`
|
||||
@@ -61,24 +68,31 @@ conexión y entrega los mensajes por **webhook**, sin un socket Baileys en-proce
|
||||
zombi. El bot pasaría a: (1) recibir mensajes por webhook de Evolution, (2) enviar por su REST. La
|
||||
lógica de Claude + los EPs de la app se quedan igual; solo cambia la capa de transporte WhatsApp.
|
||||
|
||||
## 4. Problema B — el perfil extraído no se persiste en el lead durante la conversación
|
||||
## 4. Problema B — el perfil se persiste solo a medias + máquina de estados errática
|
||||
|
||||
**Síntoma:** la conversación avanza (Luisa pregunta espacio→tamaño→estilo→urgencia) pero el lead
|
||||
queda con `botStep` desfasado y `espacio/rangoM2/...` vacíos.
|
||||
**Verificado en vivo:** `persistirTurno` SÍ funciona cuando se llama —
|
||||
`Lead ... persistido via API: {"rangoM2":"10a20","botStep":"estilo"} → ok`. Pero en una conversación
|
||||
completa **solo apareció UN `persistido`** (rangoM2); `espacio`, `urgencia` y `presupuesto` no se
|
||||
guardaron. Y la conversación se descuadra (rechaza a 4500€ → con 8500€ vuelve a preguntar el tamaño →
|
||||
"preparo presupuesto"). Causa probable: `claudeService.llamarClaude` solo devuelve `entidad`/`nuevoEstado`
|
||||
en algunos turnos, y la lógica de estado/viabilidad no es determinista.
|
||||
|
||||
**Qué sé:** el EP `/perfil` **funciona** con el payload del bot (probado). Así que el fallo está en la
|
||||
llamada del bot. [`LeadsService.persistirTurno`](../mvp/Whatsapp-bot/src/leads/leads.service.ts#L68)
|
||||
ya loguea: `Lead X persistido via API: {...} → ok/fallo`.
|
||||
**A revisar:** que cada turno con dato extraído llame a `persistirTurno`, que los valores encajen con
|
||||
los enums de la app (`urgencia` alta|media|baja, etc., o `POST /perfil` da 422 y no guarda nada), y
|
||||
endurecer la máquina de estados (no re-preguntar lo ya respondido; viabilidad estable).
|
||||
|
||||
**Qué mirar (logs del bot en Dokploy) durante una conversación:**
|
||||
- Si aparece `→ fallo`: mira el payload logueado. Sospechosos: un valor de enum inválido (p.ej.
|
||||
`urgencia` fuera de `alta|media|baja`, o `calidadGlobal` fuera de `basica|media|premium`) hace que
|
||||
TODO el `POST /perfil` dé `422` y no persista nada de ese turno. Mapea los valores que extrae Claude
|
||||
a los enums de la app antes de enviar.
|
||||
- Si no aparece la línea: `persistirTurno` no se está llamando → revisa que `claudeService.llamarClaude`
|
||||
devuelva `entidad`/`nuevoEstado`.
|
||||
- Ojo también a si el bot se **reinició a media conversación** (Problema A): tras reiniciar deja de
|
||||
recibir y el flujo se corta.
|
||||
## 4bis. Problema C (el que rompe el end-to-end) — el bot NUNCA dispara la generación
|
||||
|
||||
**Verificado en logs:** Luisa termina diciendo *"en un momento recibes tu presupuesto"* pero **no hay
|
||||
ninguna llamada a `ingesta` / `perfilCompleto`** en toda la sesión (grep vacío). Es decir, al cerrar la
|
||||
cualificación el bot **no dispara nada**: ni render, ni PDF, ni entrega. Es una promesa vacía.
|
||||
|
||||
**Qué falta (lado bot):** cuando la cualificación se completa (estado `presupuesto`/`fin_viable`), el
|
||||
bot debe (1) **pedir las fotos** del espacio por WhatsApp, (2) subirlas vía `POST /api/leads/:id/ingesta`
|
||||
(items `foto`, `momento:"antes"`), y (3) marcar `perfilCompleto:true` (y/o `finalizar`). Eso dispara en
|
||||
la app: `PERFIL_WEBHOOK` → worker genera renders → `ingesta finalizar` → PDF + email + entrega WhatsApp.
|
||||
**La app y el worker ya están listos para esto; solo falta que el bot llame al EP.** Contrato:
|
||||
[`mvp/b2c/api-docs/README.md`](../mvp/b2c/api-docs/README.md).
|
||||
|
||||
## 5. Infra (referencia rápida)
|
||||
|
||||
@@ -94,6 +108,8 @@ ya loguea: `Lead X persistido via API: {...} → ok/fallo`.
|
||||
|
||||
---
|
||||
|
||||
**Resumen:** el flujo está demostrado funcionando; los 2 problemas son de la capa de transporte/runtime
|
||||
del bot. Recomendación: cerrar el Problema B mirando los logs de `persistirTurno`, y para el Problema A
|
||||
(estabilidad) migrar a **Evolution API**. Mientras tanto, para una demo: escaneo fresco + no redeployar.
|
||||
**Resumen:** la conversación de Luisa funciona (recibe, resuelve `@lid`, cualifica, responde). Quedan 3
|
||||
cosas del bot: **A)** conexión inestable (`conflict`+`503`, bucle de reconexión) → Evolution API;
|
||||
**B)** persistencia parcial del perfil + estado errático; **C)** el bot **nunca dispara la generación**
|
||||
(no llama a `ingesta`/`perfilCompleto`), así que el presupuesto/render/entrega no llega — esto es lo que
|
||||
rompe el end-to-end y lo que más conviene cerrar. App y worker ya están listos esperando esa llamada.
|
||||
|
||||
Reference in New Issue
Block a user