El journey no es una pantalla. Es un sistema que conversa con el cliente en cada hito.
Alberto diseñó en un Excel con macros el "cerebro" de Goopcar: máquinas de estado que acompañan al cliente por WhatsApp, automáticamente, desde que compra su auto hasta que lo renueva. Este documento traduce ese diseño a un modelo claro, lo muestra funcionando, y lo conecta con el motor que ya existe en el código — para que pase a producción de forma eficiente, sostenible y escalable.
La tesis de Alberto, en una frase
Un journey es el ciclo de vida del cliente-vehículo en postventa, sostenido por un canal de comunicación siempre activo que toca cada hito accionable. Cada estado del cliente se empareja con un mensaje automático; cada respuesta del cliente mueve el estado. El sistema toma el control — no el asesor.
"Muchos de esos estados que estamos hablando después los podríamos poner asociados a mensajes automáticos. Esa es la idea." — Alberto Jara (recogido en las notas de Óscar)
Estado ↔ mensaje
Enviar un mensaje es una señal de estado. "Tu auto ya está en el taller" confirma el ingreso. La comunicación es la fuente de verdad del avance.
El sistema toma el control
El asesor ve uno o dos días; el journey dura años. Por eso la automatización orquesta — pero todo queda visible y auditable en el ledger.
Hacia hitos monetizables
"El journey se tiene que traducir en hitos monetizables." Cada bitácora distingue hitos que generan ingreso de hitos de apoyo.
El micro-journey y el macro-journey
Todo cliente vive dos escalas anidadas. El micro-journey es una visita al taller (días). El macro-journey es la tenencia del auto (años), que enlaza un micro-journey con el siguiente por desgaste o kilometraje.
La cita o mantención: T-2 → T+1. Es el journey operativo que el asesor gestiona a diario. Ocho hitos funcionales, algunos monetizables.
Los años que el cliente es dueño del vehículo. El sistema lo acompaña entre visitas y detecta el próximo momento de valor.
Dos señales que Alberto separa
- Próxima acción del journey — la mantención que "toca" por kilometraje (ej: los 20.000 km).
- Próxima oportunidad — una venta cruzada distinta: revisión técnica, seguro obligatorio, garantía extendida.
"¿Cuál es la próxima acción del journey? La de 20.000… Pero ¿cuál es la próxima oportunidad? Una revisión técnica. O la venta de seguro obligatorio." — Alberto Jara
El macro-journey cierra con la renovación / retoma (venta de unidad nueva) — y detecta la fuga hacia (o desde) otro concesionario con scoring. Cerca del 50% de los clientes no llega a su primera mantención: ese churn es el dolor que el sistema ataca.
Fuentes: Docs/Contexto/31_journeys.md, notas de Óscar sobre las reuniones, SSoT-Vision-Alberto-Jara.md.
Anatomía de un journey
El Journey Designer de Alberto define diez piezas. Lo notable: casi todas ya tienen su tabla en el esquema del proyecto. Haz clic en cada pieza para ver qué es y cómo se aterriza en código.
Un flujo conversacional con un objetivo de negocio. Es una máquina de estados completa y autocontenida: se crea, cambia o agrega sin tocar el motor ni otros journeys (Journey-as-Module).
Identidad (code, version). Sin tenantId: es catálogo de producto, no dato de un cliente.
Dónde está el cliente en el journey (Pendiente, Confirmado, EnServicio…). Un journey tiene un estado inicial; los estados finales lo cierran; un estado puede encadenar a otro journey.
La condición que enciende el journey. Dos clases: por evento (una variable/estado cambia) o por tiempo (una ventana temporal). Formato: SI [condición] Y [ventana] → ENTONCES [journey].
Se evalúa después del trigger y antes de instanciar: una condición que permite o bloquea. Es el "validador de redundancia" de Alberto: no mandar recordatorio si el cliente ya confirmó.
⚠️ Gap: la tabla existe pero el motor aún no la consulta. Ver sección 8.
Conecta un estado con el siguiente. Se dispara por uno de tres elementos: el Mensaje Inicial, un Botón que el cliente presiona, o un Timeout. Al aplicarse, actualiza variables y puede encadenar otro journey.
El texto que se envía, emitido por bot, asesor o IA. Usa variables {{Nombre_Cliente}} y botones de respuesta rápida. Son plantillas WhatsApp pre-aprobadas (HSM). Solo admin las crea — gobernanza.
Datos que el journey lee (en triggers y guardas) y escribe (como efecto de una transición). Ej: Vehiculo_Nuevo, Bienvenida_Enviada, Cliente_Activado. También son los merge-tags de los mensajes.
Qué pasa si el cliente no responde en un plazo. Define la espera y cuántas veces reintentar antes de forzar la transición. Es el "trigger temporal" que exime al asesor de acordarse.
⚠️ Gap: falta el worker que dispare timeouts del motor genérico en producción.
El estado final de un journey puede convertirse en el disparador del siguiente. Así se compone el macro-journey a partir de micro-journeys: activación → mantención → renovación.
⚠️ Gap: el modelo lo registra en el ledger (journey.chained) pero aún no crea la instancia encadenada.
El motor: un solo handleHito() para todos los journeys
El motor no sabe de "el journey del taller" ni de "el de activación". Solo sabe procesar hitos. Un hito llega, el motor busca qué transición calza, la aplica, y avanza. Esto es config-as-data: los journeys son datos, el motor es genérico. Agregar el journey número 91 es registrar datos, no escribir código.
Los cuatro hitos que el mundo le manda al motor
message_sentSe envió un mensaje. Suele ser el hito que abre el journey (y confirma un estado).
customer_replyEl cliente respondió: por botón, texto o lista. Es lo que ramifica el flujo.
timeout_elapsedVenció el plazo de espera. El cliente no respondió: se fuerza la transición de timeout.
dms_eventEl sistema del taller (DMS) reportó algo: apertura de OT, pago, cierre. Único origen válido para el ingreso físico.
El ciclo de OrchestrationEngine.handleHito(hito)
eventId? Marcador durable en el ledger — un webhook repetido no avanza dos veces.TenantJourney me dice qué versión del journey usa este tenant y con qué overrides (sus plantillas WABA, sus textos).fromStateCode + hitoType + hitoQualifier + guard sobre las variables.DOCTRINE_NO_DOUBLE_INPUT toda transición al estado físico "ingresado" que no venga de un dms_event. La doctrina de Goopcar vive dentro del motor.Fuente: Docs/adr/0003-config-as-data-journey-engine.md, apps/api/src/modules/journey-engine/orchestration/.
Journeys reales, funcionando
Estos no son ejemplos inventados: son los journeys que ya viven como datos en el repositorio. Explora la máquina de estados, y en el primero, juega la conversación por WhatsApp como si fueras el cliente.
Cuando alguien compra un auto 0 km, el sistema abre su canal de postventa por WhatsApp y le ofrece activarlo. Según responda, ramifica — y si activa, encadena al siguiente journey (JOU_010).
Juégalo: la conversación real por WhatsApp
Simulación fiel al descriptor jou001.package.ts. Cada clic emite un hito real del motor.
El journey operativo de la visita al taller — el que más le importa a Alberto. 18 estados, un camino feliz y
sus rutas de excepción (no-show, reagendamiento, recuperación). Hoy avanza por dms_event. Haz clic
en un estado para ver el mensaje automático que debería emitir en ese hito.
taller.package.ts tiene
messages: [] — solo modela las transiciones. Los textos que ves al hacer clic son la
propuesta derivada de las plantillas canónicas de Alberto. Poblar esos mensajes es exactamente
"asociar cada estado a un mensaje automático" — su tesis central.
Fuentes: jou001.package.ts, taller.package.ts, Docs/Contexto/33_templates_mensajes.md, .claude/rules/backend.md §6.
El universo: 5 productos, 37 módulos, 90 journeys
Los dos journeys que exploraste son la punta del iceberg. El modelo de negocio canónico de Alberto (Excel
2606 Detalle Goopcar Tech) define 90 journeys en 5 productos y 37 módulos, que cubren
toda la vida del cliente-vehículo — de la compra a la renovación. Hoy solo 5 están diseñados como
máquina de estados; los otros 85 son intención declarada, listos para poblarse como datos sobre el mismo motor.
Los cinco productos son las fases del ciclo de vida: Engage+ (el canal) sostiene a los otros cuatro, que van de la postventa inmediata (Service) a la renovación del auto (Ownership+), pasando por movilidad y fidelización.
El "spine" real: lo único diseñado hoy
Un solo camino está modelado como FSM ejecutable: el onboarding post-compra. Nace de la compra de un 0 km y encadena cinco journeys diseñados. Los destinos en gris son journeys ya referenciados pero aún sin diseñar — el trabajo pendiente es poblarlos.
JOU_001 canónico
(Excel 2606, v56) tiene 2 botones, encadena a JOU_002 / JOU_004 y su timeout es de 24 hrs.
El JOU_001 del código (greenfield, jou001.package.ts) tiene 3 botones,
encadena a JOU_010 y su timeout es de 30 min. El greenfield fue un vehículo para validar el motor, no el
journey definitivo. Al implementar en serio, JOU_001 debe alinearse con el modelo canónico (o versionarse de forma explícita).
2606 Detalle Goopcar Tech.xlsm.
Los tres planos: definición, adopción, instancia
Aquí está la clave de "sostenible y escalable". Un journey se diseña una vez (genérico), cada concesionario lo adopta con sus textos, y cada cliente tiene su instancia viva. Tres planos separados = un solo diseño sirve a todos los tenants sin duplicar código.
Definición
catálogo genérico · sin tenantId · sin RLSLa FSM como datos. Identidad (code, version). Es activo de producto: se versiona y se comparte entre todos los concesionarios.
Adopción
per-tenant · con RLSQué journey usa cada concesionario, en qué versión, y con qué overrides: sus plantillas WABA propias, sus textos, su marca.
Instancia
per-tenant · con RLSEl journey vivo de un cliente concreto: en qué estado está y el valor de sus variables. El historial es append-only en el ledger.
Invariante del ADR-0003: definición genérica + adopción per-tenant. La definición nunca lleva tenantId; el aislamiento vive en adopción e instancia.
Del Excel de Alberto al código actual
La conclusión que hace esto viable de programar hoy: el motor config-as-data ya
existe y su esquema calza casi 1:1 con el Journey Designer. No hay que reinventar — hay que poblar datos
y cerrar unos pocos gaps.
| Pieza del Journey Designer (Excel/VBA) | Equivalente en el código (Prisma) | Estado |
|---|---|---|
| Journey Config_Journey | JourneyDefinition (code, version) | Existe |
| Estado Config_Estados | JourneyState | Existe |
| Mensaje Inicial | JourneyMessage (emitOnStateCode) | Existe |
| Botón (respuesta) CFG_Botones | JourneyTransition (hitoType=customer_reply, qualifier=BTN_xxx) | Existe |
| Timeout Config_Timeout | JourneyTimeout + transición timeout_elapsed | Parcial — falta worker |
| Mensaje / Template Config_Mensajes | JourneyMessage → CommunicationTemplate | Existe (WhatsApp) |
| Resultado / Acción Config_Resultado_Accion | toStateCode + variableUpdates | Existe |
| Variable DN_Variables | JourneyVariable + variablesJson | Existe |
| Orquestación Config_Orquestacion | OrchestrationRule | Persiste, motor no la consulta |
| Encadenamiento JOU_OUT | JourneyState.chainsToJourneyCode | Registra, no instancia |
| Producto | JourneyDefinition.productCode (string) | Falta catálogo |
| Módulo | JourneyDefinition.moduleCode (string) | Falta catálogo |
| Recurso (adjunto) Config_Recursos | CommunicationTemplate.metadata | Sin entidad propia |
Lo que falta cerrar para producción
Siete tareas concretas separan el motor "validado en tests" del motor "corriendo journeys en vivo". Ordenadas por impacto.
timeout_elapsed en producción. Bloqueante para tener JOU_001 vivo.JOURNEY_ENGINE_TALLER dispatcher.tsOrchestrationRule en el motor orchestration/engine.tschainsToJourneyCode existe y se loguea journey.chained, pero no se crea la instancia del journey siguiente. Sin esto, el macro-journey no se compone.messages: [] hoy. Asociar cada estado a su plantilla (confirmación, check-in, no-show, diagnóstico, listo, NPS) materializa la tesis "estado ↔ mensaje automático".ProductCatalog y ModuleCatalog schema.prismarepeat de timeoutvariablesJson interno; disparar por datos de cliente/vehículo/OT requiere un resolutor de variables. Y el campo repeat > 0 aún no se ejecuta.Seis decisiones que Alberto debe cerrar
Estas ambigüedades salieron de las notas de Óscar sobre lo conversado con Alberto — él las marca como "clave" pero no las resuelve. Cada una tiene una recomendación nuestra; necesitamos su confirmación antes de escribir el código que depende de ellas.
assignedAdvisorId.customerId.ownerCustomerId, primaryDriverCustomerId, messageRecipientCustomerId. Los mensajes van a quien tenga el "check" de gestionado.Próximo paso sugerido
Validar este modelo con Alberto (usar la sección 10 como agenda de decisiones). Cerradas esas seis, el camino de implementación es corto porque el motor ya existe: poblar el journey del taller como datos, cerrar los gaps 1–4, y activar el flag con paridad verificada.
2606 Journey Designer - Goopcar Tech.xlsm · modelo de negocio canónico 2606 Detalle Goopcar Tech.xlsm.